]> arthur.barton.de Git - netdata.git/commitdiff
Merge remote-tracking branch 'upstream/master' into health
authorCosta Tsaousis <costa@tsaousis.gr>
Mon, 15 Aug 2016 16:33:58 +0000 (19:33 +0300)
committerCosta Tsaousis <costa@tsaousis.gr>
Mon, 15 Aug 2016 16:33:58 +0000 (19:33 +0300)
98 files changed:
CMakeLists.txt
conf.d/Makefile.am
conf.d/health.d/disks.conf [new file with mode: 0644]
configs.signatures
netdata-installer.sh
node.d/named.node.js
node.d/sma_webbox.node.js
node.d/snmp.node.js
plugins.d/cgroup-name.sh
plugins.d/charts.d.dryrun-helper.sh
plugins.d/charts.d.plugin
plugins.d/loopsleepms.sh.inc
plugins.d/node.d.plugin
plugins.d/tc-qos-helper.sh
profile/benchmark-dictionary.c
profile/benchmark-registry.c
profile/test-eval.c [new file with mode: 0644]
src/Makefile.am
src/appconfig.c
src/appconfig.h
src/apps_plugin.c
src/avl.c
src/avl.h
src/common.c
src/common.h
src/daemon.c
src/daemon.h
src/dictionary.c
src/dictionary.h
src/eval.c [new file with mode: 0644]
src/eval.h [new file with mode: 0644]
src/global_statistics.c
src/global_statistics.h
src/health.c [new file with mode: 0644]
src/health.h [new file with mode: 0644]
src/log.c
src/log.h
src/main.c
src/main.h
src/plugin_checks.c
src/plugin_idlejitter.c
src/plugin_nfacct.c
src/plugin_proc.c
src/plugin_tc.c
src/plugins_d.c
src/plugins_d.h
src/popen.c
src/popen.h
src/proc_diskstats.c
src/proc_interrupts.c
src/proc_loadavg.c
src/proc_meminfo.c
src/proc_net_dev.c
src/proc_net_ip_vs_stats.c
src/proc_net_netstat.c
src/proc_net_rpc_nfsd.c
src/proc_net_snmp.c
src/proc_net_snmp6.c
src/proc_net_stat_conntrack.c
src/proc_net_stat_synproxy.c
src/proc_self_mountinfo.c
src/proc_self_mountinfo.h
src/proc_softirqs.c
src/proc_stat.c
src/proc_sys_kernel_random_entropy_avail.c
src/proc_vmstat.c
src/procfile.c
src/procfile.h
src/registry.c
src/registry.h
src/rrd.c
src/rrd.h
src/rrd2json.c
src/rrd2json.h
src/storage_number.c
src/storage_number.h
src/sys_fs_cgroup.c
src/sys_kernel_mm_ksm.c
src/unit_test.c
src/url.c
src/web_buffer.c
src/web_buffer.h
src/web_buffer_svg.c
src/web_buffer_svg.h
src/web_client.c
src/web_client.h
src/web_server.c
src/web_server.h
system/netdata.logrotate.in
system/netdata.service.in
web/dashboard.html
web/dashboard.js
web/demo.html
web/demo2.html
web/demosites.html
web/index.html
web/registry.html
web/tv.html

index 30ea5fbf41056ae04636c6ec0acca0d87e1c88c8..d5e6d195e3b277b66913d99e5545306e7d286864 100755 (executable)
@@ -82,7 +82,7 @@ set(NETDATA_SOURCE_FILES
         src/web_client.h
         src/web_server.c
         src/web_server.h
-        config.h)
+        config.h src/health.h src/health.c src/eval.h src/eval.c)
 
 set(APPS_PLUGIN_SOURCE_FILES
         src/appconfig.c
index f628a042bee0049dbe36583cdfb9206b19fea0ce..56510c3f421edc3f0642435a3abc2a6d67d48bdc 100644 (file)
@@ -38,3 +38,8 @@ dist_pythonconfig_DATA = \
        python.d/squid.conf \
        python.d/tomcat.conf \
        $(NULL)
+
+healthconfigdir=$(configdir)/health.d
+dist_healthconfig_DATA = \
+       health.d/disks.conf \
+       $(NULL)
diff --git a/conf.d/health.d/disks.conf b/conf.d/health.d/disks.conf
new file mode 100644 (file)
index 0000000..4f66f92
--- /dev/null
@@ -0,0 +1,67 @@
+# -----------------------------------------------------------------------------
+# low disk space
+
+# checking the latest collected values
+# raise an alarm if the disk is low on
+# available disk space
+
+template: low_disk_space
+      on: disk.space
+   every: 1m
+    warn: $avail * 100 / ($avail + $used) > 80
+    crit: $avail * 100 / ($avail + $used) > 90
+
+# -----------------------------------------------------------------------------
+# disk fill rate
+
+# calculate the rate the disk fills
+# use as base, the available space change
+# during the last minute
+
+template: disk_fill_rate_1m
+      on: disk.space
+  lookup: max -1s at -1m unaligned of avail
+    calc: ($this - $avail) / (1 * 60)
+   every: 1m
+    warn: $this * 2 * 86400 > $avail
+    crit: $this * 1 * 86400 > $avail
+
+# calculate the rate the disk fills
+# use as base, the available space change
+# during the last hour
+template: disk_fill_rate_59m
+      on: disk.space
+  lookup: max -1s at -59m unaligned of avail
+   every: 1m
+    calc: ($this - $avail) / (59 * 60)
+    warn: $this * 2 * 86400 > $avail
+    crit: $this * 1 * 86400 > $avail
+
+# -----------------------------------------------------------------------------
+# disk congestion
+
+# raise an alarm if the disk is congested
+# by calculating the average disk utilization
+# for the last minute
+
+template: disk_congested
+      on: disk.util
+  lookup: average -1m every 1m unaligned
+   green: 70
+     red: 95
+    warn: $this > $green
+    crit: $this > $red
+
+# raise an alarm if the disk backlog
+# is above 1000ms (1s) per second
+# for 1 minute
+# (i.e. the disk cannot catch up)
+
+template: disk_not_catching_up
+      on: disk.backlog
+  lookup: average -1m every 1m unaligned
+   green: 500
+     red: 1000
+    warn: $this > $green
+    crit: $this > $red
+
index 26d1a277de1f746602b92cc981a416f4d36d9f47..2e4baa76325b59468fc3c4d608213c092e880b1b 100644 (file)
 declare -A configs_signatures=(
-       ['0056936ce99788ed9ae1c611c87aa6d8']='apps_groups.conf'
-       ['18ee1c6197a4381b1c1631ef6129824f']='apps_groups.conf'
-       ['2f4a85fedecce1bf425fa1039f6b021e']='apps_groups.conf'
-       ['3af522d65b50a5e447607ffb28c81ff5']='apps_groups.conf'
-       ['3b1bfa40a4ff6a200bb2fc00bc51a664']='apps_groups.conf'
-       ['4a448831776de8acf2e0bdc4cc994cb4']='apps_groups.conf'
-       ['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf'
-       ['636d032928ea0f4741eab264fb49c099']='apps_groups.conf'
-       ['647361e99b5f4e0d73470c569bb9461c']='apps_groups.conf'
-       ['6a47af861ad3dd112124c37fbf09672b']='apps_groups.conf'
-       ['79a37756869d9b4629285922572d6b9b']='apps_groups.conf'
-       ['99c1617448abbdc493976ab9bda5ce02']='apps_groups.conf'
-       ['9c0185ceff15415bc59b2ce2c1f04367']='apps_groups.conf'
-       ['a0ee8f351f213c0e8af9eb7a4a09cb95']='apps_groups.conf'
-       ['a7cceeafb1e6ef1ead503ab65f687902']='apps_groups.conf'
-       ['a837986be634fd7648bcdf939019424a']='apps_groups.conf'
-       ['a9cd91675467c5426f5b51c47602c889']='apps_groups.conf'
-       ['acaa6731a272f6d251afb357e99b518f']='apps_groups.conf'
-       ['bb51112d01ff20053196a57632df8962']='apps_groups.conf'
-       ['d9258e671d0d0b6498af1ce16ef030d2']='apps_groups.conf'
-       ['ebd0612ccc5807524ebb2b647e3e56c9']='apps_groups.conf'
-       ['f2f1b8656f5011e965ac45b818cf668d']='apps_groups.conf'
-       ['fdea185e0e52b459b48852aa37f20e0f']='apps_groups.conf'
-       ['4ccb06fff1ce06dc5bc80e0a9f568f6e']='charts.d.conf'
-       ['4e995acb0d6fd77403a2a9dca984b55b']='charts.d.conf'
-       ['535e5113b07b0fc6f3abd59546c276f6']='charts.d.conf'
-       ['7cf6402b51e5070f2be3ad6fe059ff89']='charts.d.conf'
-       ['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf'
-       ['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf'
-       ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf'
-       ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf'
-       ['5278ebbae19c60db600f0a119cb3664e']='python.d/apache.conf'
-       ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf'
-       ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf'
-       ['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf'
-       ['4b775fb31342f1478b3773d041a72911']='python.d.conf'
-       ['7a21ccc76be2968ce5d0b52ec1166788']='python.d.conf'
-       ['99a3de85d1e7826ed64a5f8576712e5d']='python.d.conf'
-       ['9e0553ebdc21b64295873fc104cfa79d']='python.d.conf'
-       ['a2944a309f8ce1a3195451856478d6ae']='python.d.conf'
-       ['af44cc53aa2bc5cc8935667119567522']='python.d.conf'
-       ['b27f10a38a95edbbec20f44a4728b7c4']='python.d.conf'
-       ['b32164929eda7449a9677044e11151bf']='python.d.conf'
-       ['b8969be5b3ceb4a99477937119bd4323']='python.d.conf'
-       ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf'
-       ['d55be5bb5e108da1e7645da007c53cd4']='python.d.conf'
-       ['f82924563e41d99cdae5431f0af69155']='python.d.conf'
-       ['7830066c46a7e5f9682b8d3f4566b4e5']='python.d/cpufreq.conf'
-       ['b5b5a8d6d991fb1cef8d80afa23ba114']='python.d/cpufreq.conf'
-       ['dc0d2b96378f290eec3fcf98b89ad824']='python.d/cpufreq.conf'
-       ['a8bb4e1d0525f59692778ad8f675a77a']='python.d/example.conf'
-       ['ae5ac0a3521e50aa6f6eda2a330b4075']='python.d/example.conf'
-       ['e5f32f54d6d6728f21f9ac26f37d6573']='python.d/example.conf'
-       ['15e32114994b92be7853b88091e7c6fb']='python.d/exim.conf'
-       ['54614490a14e1a4b7b3d9fecb6b4cfa5']='python.d/exim.conf'
-       ['73125ae64d5c6e9361944cd9bd14844e']='python.d/exim.conf'
-       ['a94af1c808aafdf00537d85ff2197ec8']='python.d/exim.conf'
-       ['2a0794fd43eadf30a51805bc9ba2c64d']='python.d/hddtemp.conf'
-       ['47180421d580baeaedf8c0ef3d647fb5']='python.d/hddtemp.conf'
-       ['731a1fcfe9b2da1b9d685056a59541b8']='python.d/hddtemp.conf'
-       ['d74dc63fbe631dab9a2ff1b0f5d71719']='python.d/hddtemp.conf'
-       ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf'
-       ['70105b1744a8e13f49083d7f1981aea2']='python.d/ipfs.conf'
-       ['97f337eb96213f3ede05e522e3743a6c']='python.d/memcached.conf'
-       ['1ea8e8ef1fa8a3a0fcdfba236f4cb195']='python.d/mysql.conf'
-       ['5379cdc26d7725e2b0d688d785816cef']='python.d/mysql.conf'
-       ['7deb236ec68a512b9bdd18e6a51d76f7']='python.d/mysql.conf'
-       ['88f77865f75c9fb61c97d700bd4561ee']='python.d/mysql.conf'
-       ['b0f0a0ac415e4b1a82187b80d211e83b']='python.d/mysql.conf'
-       ['df381f3a7ca9fb2b4b43ae7cb7a4c492']='python.d/mysql.conf'
-       ['061c45b0e34170d357e47883166ecf40']='python.d/nginx.conf'
-       ['21924a6ab8008d16ffac340f226ebad9']='python.d/nginx.conf'
-       ['c61948101e0e6846679682794ee48c5b']='python.d/nginx.conf'
-       ['3ca696189911fb38a0319ddd71e9a395']='python.d/phpfpm.conf'
-       ['8c1d41e2c88aeca78bc319ed74c8748c']='python.d/phpfpm.conf'
-       ['b8b87574fd496a66ede884c5336493bd']='python.d/phpfpm.conf'
-       ['c88fb430f35b7d8f08775d84debffbd2']='python.d/phpfpm.conf'
-       ['d7e0bd12d4a60a761dcab3531a841711']='python.d/phpfpm.conf'
-       ['142a5b693d34b0308bb0b8aec71fad79']='python.d/postfix.conf'
-       ['ca249db7a0637d55abb938d969f9b486']='python.d/postfix.conf'
-       ['39571e9fad9b759200c5d5b2ee13feb4']='python.d/redis.conf'
-       ['777f4da70f461ef675bde07fb3644312']='python.d/redis.conf'
-       ['b915126262d08aa9da81de539a58a3fb']='python.d/redis.conf'
-       ['837480f77ba1a85677a36747fbc2cd2e']='python.d/sensors.conf'
-       ['cfecf298bdafaa7e0a3a263548e82132']='python.d/sensors.conf'
-       ['48195c5c8c0476a49b714b4c76bdb570']='python.d/squid.conf'
-       ['64070d856ab1b47a18ec871e49bbc13b']='python.d/squid.conf'
-       ['78bb08809dffcb62e9bc493840f9c039']='python.d/squid.conf'
-       ['78e0065738394f5bf15023f41d66ed4b']='python.d/squid.conf'
-       ['7d8bd884ec26cb35d16c4fc05f969799']='python.d/squid.conf'
-       ['91cf3b3d42cac969b8b3fd4f531ecfb3']='python.d/squid.conf'
-       ['ade389c1b6efe0cff47c33e662731f0a']='python.d/squid.conf'
-       ['b846ca1f99fa6a65303b58186f47d7a4']='python.d/squid.conf'
-       ['e3e5bc57335c489f01b8559f5c70e112']='python.d/squid.conf'
-       ['0388b873d0d7e47c19005b7241db77d8']='python.d/tomcat.conf'
-       ['f7a99e94231beda85c6254912d8d31c1']='python.d/tomcat.conf'
+  ['0056936ce99788ed9ae1c611c87aa6d8']='apps_groups.conf'
+  ['18ee1c6197a4381b1c1631ef6129824f']='apps_groups.conf'
+  ['2f4a85fedecce1bf425fa1039f6b021e']='apps_groups.conf'
+  ['3af522d65b50a5e447607ffb28c81ff5']='apps_groups.conf'
+  ['3b1bfa40a4ff6a200bb2fc00bc51a664']='apps_groups.conf'
+  ['4a448831776de8acf2e0bdc4cc994cb4']='apps_groups.conf'
+  ['5bf51bb24fb41db9b1e448bd060d3f8c']='apps_groups.conf'
+  ['636d032928ea0f4741eab264fb49c099']='apps_groups.conf'
+  ['647361e99b5f4e0d73470c569bb9461c']='apps_groups.conf'
+  ['6a47af861ad3dd112124c37fbf09672b']='apps_groups.conf'
+  ['79a37756869d9b4629285922572d6b9b']='apps_groups.conf'
+  ['99c1617448abbdc493976ab9bda5ce02']='apps_groups.conf'
+  ['9c0185ceff15415bc59b2ce2c1f04367']='apps_groups.conf'
+  ['a0ee8f351f213c0e8af9eb7a4a09cb95']='apps_groups.conf'
+  ['a7cceeafb1e6ef1ead503ab65f687902']='apps_groups.conf'
+  ['a837986be634fd7648bcdf939019424a']='apps_groups.conf'
+  ['a9cd91675467c5426f5b51c47602c889']='apps_groups.conf'
+  ['acaa6731a272f6d251afb357e99b518f']='apps_groups.conf'
+  ['bb51112d01ff20053196a57632df8962']='apps_groups.conf'
+  ['d9258e671d0d0b6498af1ce16ef030d2']='apps_groups.conf'
+  ['ebd0612ccc5807524ebb2b647e3e56c9']='apps_groups.conf'
+  ['f2f1b8656f5011e965ac45b818cf668d']='apps_groups.conf'
+  ['fdea185e0e52b459b48852aa37f20e0f']='apps_groups.conf'
+  ['4ccb06fff1ce06dc5bc80e0a9f568f6e']='charts.d.conf'
+  ['4e995acb0d6fd77403a2a9dca984b55b']='charts.d.conf'
+  ['535e5113b07b0fc6f3abd59546c276f6']='charts.d.conf'
+  ['7cf6402b51e5070f2be3ad6fe059ff89']='charts.d.conf'
+  ['a02d14124b19c635c1426cee2e98bac5']='charts.d.conf'
+  ['ca026d7c779f0a7cb7787713c5be5c47']='charts.d.conf'
+  ['02fa10fa85ab88e9723998de48d1aca0']='health.d/disks.conf'
+  ['8989b5e2f4ef9cd278ef58be0fae4074']='health.d/disks.conf'
+  ['8dc0bd0a70b5117454bd5f5b98f91c2c']='health.d/disks.conf'
+  ['6b917300747e7e8314844237e2462261']='python.d/apache_cache.conf'
+  ['e0e96cc47ed61d6492416be5236cd4d3']='python.d/apache_cache.conf'
+  ['5278ebbae19c60db600f0a119cb3664e']='python.d/apache.conf'
+  ['5829812db29598db5857c9f433e96fef']='python.d/apache.conf'
+  ['6bf0de6e3b251b765b10a71d8c5c319d']='python.d/apache.conf'
+  ['38d1bf04fe9901481dd6febcc0404a86']='python.d.conf'
+  ['4b775fb31342f1478b3773d041a72911']='python.d.conf'
+  ['7a21ccc76be2968ce5d0b52ec1166788']='python.d.conf'
+  ['99a3de85d1e7826ed64a5f8576712e5d']='python.d.conf'
+  ['9e0553ebdc21b64295873fc104cfa79d']='python.d.conf'
+  ['a2944a309f8ce1a3195451856478d6ae']='python.d.conf'
+  ['af44cc53aa2bc5cc8935667119567522']='python.d.conf'
+  ['b27f10a38a95edbbec20f44a4728b7c4']='python.d.conf'
+  ['b32164929eda7449a9677044e11151bf']='python.d.conf'
+  ['b8969be5b3ceb4a99477937119bd4323']='python.d.conf'
+  ['bba2f3886587f137ea08a6e63dd3d376']='python.d.conf'
+  ['d55be5bb5e108da1e7645da007c53cd4']='python.d.conf'
+  ['f82924563e41d99cdae5431f0af69155']='python.d.conf'
+  ['7830066c46a7e5f9682b8d3f4566b4e5']='python.d/cpufreq.conf'
+  ['b5b5a8d6d991fb1cef8d80afa23ba114']='python.d/cpufreq.conf'
+  ['dc0d2b96378f290eec3fcf98b89ad824']='python.d/cpufreq.conf'
+  ['e40947d22f7ed5359f12fc89e3512963']='python.d/dovecot.conf'
+  ['a8bb4e1d0525f59692778ad8f675a77a']='python.d/example.conf'
+  ['ae5ac0a3521e50aa6f6eda2a330b4075']='python.d/example.conf'
+  ['e5f32f54d6d6728f21f9ac26f37d6573']='python.d/example.conf'
+  ['15e32114994b92be7853b88091e7c6fb']='python.d/exim.conf'
+  ['54614490a14e1a4b7b3d9fecb6b4cfa5']='python.d/exim.conf'
+  ['73125ae64d5c6e9361944cd9bd14844e']='python.d/exim.conf'
+  ['a94af1c808aafdf00537d85ff2197ec8']='python.d/exim.conf'
+  ['2a0794fd43eadf30a51805bc9ba2c64d']='python.d/hddtemp.conf'
+  ['3fc45cc18e884c22482524dff6d27833']='python.d/hddtemp.conf'
+  ['47180421d580baeaedf8c0ef3d647fb5']='python.d/hddtemp.conf'
+  ['731a1fcfe9b2da1b9d685056a59541b8']='python.d/hddtemp.conf'
+  ['d74dc63fbe631dab9a2ff1b0f5d71719']='python.d/hddtemp.conf'
+  ['0e59bc11d0a869ea0247c04c08c8d72e']='python.d/ipfs.conf'
+  ['70105b1744a8e13f49083d7f1981aea2']='python.d/ipfs.conf'
+  ['97f337eb96213f3ede05e522e3743a6c']='python.d/memcached.conf'
+  ['1ea8e8ef1fa8a3a0fcdfba236f4cb195']='python.d/mysql.conf'
+  ['5379cdc26d7725e2b0d688d785816cef']='python.d/mysql.conf'
+  ['7deb236ec68a512b9bdd18e6a51d76f7']='python.d/mysql.conf'
+  ['88f77865f75c9fb61c97d700bd4561ee']='python.d/mysql.conf'
+  ['b0f0a0ac415e4b1a82187b80d211e83b']='python.d/mysql.conf'
+  ['df381f3a7ca9fb2b4b43ae7cb7a4c492']='python.d/mysql.conf'
+  ['061c45b0e34170d357e47883166ecf40']='python.d/nginx.conf'
+  ['21924a6ab8008d16ffac340f226ebad9']='python.d/nginx.conf'
+  ['c61948101e0e6846679682794ee48c5b']='python.d/nginx.conf'
+  ['3ca696189911fb38a0319ddd71e9a395']='python.d/phpfpm.conf'
+  ['8c1d41e2c88aeca78bc319ed74c8748c']='python.d/phpfpm.conf'
+  ['b8b87574fd496a66ede884c5336493bd']='python.d/phpfpm.conf'
+  ['c88fb430f35b7d8f08775d84debffbd2']='python.d/phpfpm.conf'
+  ['d7e0bd12d4a60a761dcab3531a841711']='python.d/phpfpm.conf'
+  ['142a5b693d34b0308bb0b8aec71fad79']='python.d/postfix.conf'
+  ['ca249db7a0637d55abb938d969f9b486']='python.d/postfix.conf'
+  ['39571e9fad9b759200c5d5b2ee13feb4']='python.d/redis.conf'
+  ['777f4da70f461ef675bde07fb3644312']='python.d/redis.conf'
+  ['b915126262d08aa9da81de539a58a3fb']='python.d/redis.conf'
+  ['837480f77ba1a85677a36747fbc2cd2e']='python.d/sensors.conf'
+  ['cfecf298bdafaa7e0a3a263548e82132']='python.d/sensors.conf'
+  ['48195c5c8c0476a49b714b4c76bdb570']='python.d/squid.conf'
+  ['64070d856ab1b47a18ec871e49bbc13b']='python.d/squid.conf'
+  ['78bb08809dffcb62e9bc493840f9c039']='python.d/squid.conf'
+  ['78e0065738394f5bf15023f41d66ed4b']='python.d/squid.conf'
+  ['7d8bd884ec26cb35d16c4fc05f969799']='python.d/squid.conf'
+  ['91cf3b3d42cac969b8b3fd4f531ecfb3']='python.d/squid.conf'
+  ['ade389c1b6efe0cff47c33e662731f0a']='python.d/squid.conf'
+  ['b846ca1f99fa6a65303b58186f47d7a4']='python.d/squid.conf'
+  ['e3e5bc57335c489f01b8559f5c70e112']='python.d/squid.conf'
+  ['0388b873d0d7e47c19005b7241db77d8']='python.d/tomcat.conf'
+  ['f7a99e94231beda85c6254912d8d31c1']='python.d/tomcat.conf'
 )
index d465cc08b4d21658aa64c863943150e6dd3ee88b..4ffddbcd12a419a8943904b6601012874b542b61 100755 (executable)
@@ -26,18 +26,18 @@ printf "\n" >>netdata-installer.log
 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
+    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
 }
 
 ME="$0"
@@ -47,353 +47,353 @@ NETDATA_PREFIX=
 LIBS_ARE_HERE=0
 
 usage() {
-       cat <<-USAGE
+    cat <<USAGE
 
-       ${ME} <installer options>
+${ME} <installer options>
 
-       Valid <installer options> are:
+Valid <installer options> are:
 
-          --install /PATH/TO/INSTALL
+   --install /PATH/TO/INSTALL
 
-                       If your give: --install /opt
-                       netdata will be installed in /opt/netdata
+        If your give: --install /opt
+        netdata will be installed in /opt/netdata
 
-          --dont-start-it
+   --dont-start-it
 
-                       Do not (re)start netdata.
-                       Just install it.
+        Do not (re)start netdata.
+        Just install it.
 
-          --dont-wait
+   --dont-wait
 
-                       Do not wait for the user to press ENTER.
-                       Start immediately building it.
+        Do not wait for the user to press ENTER.
+        Start immediately building it.
 
-          --zlib-is-really-here
-          --libs-are-really-here
+   --zlib-is-really-here
+   --libs-are-really-here
 
-                       If you get errors about missing zlib,
-                       or libuuid but you know it is available,
-                       you have a broken pkg-config.
-                       Use this option to allow it continue
-                       without checking pkg-config.
+        If you get errors about missing zlib,
+        or libuuid but you know it is available,
+        you have a broken pkg-config.
+        Use this option to allow it continue
+        without checking pkg-config.
 
-       Netdata will by default be compiled with gcc optimization -O3
-       If you need to pass different CFLAGS, use something like this:
+Netdata will by default be compiled with gcc optimization -O3
+If you need to pass different CFLAGS, use something like this:
 
-         CFLAGS="<gcc options>" ${ME} <installer options>
+  CFLAGS="<gcc options>" ${ME} <installer options>
 
-       For the installer to complete successfully, you will need
-       these packages installed:
+For the installer to complete successfully, you will need
+these packages installed:
 
-          gcc make autoconf automake pkg-config zlib1g-dev (or zlib-devel)
-          uuid-dev (or libuuid-devel)
+   gcc make autoconf automake pkg-config zlib1g-dev (or zlib-devel)
+   uuid-dev (or libuuid-devel)
 
-       For the plugins, you will at least need:
+For the plugins, you will at least need:
 
-          curl nodejs
+   curl nodejs
 
 USAGE
 }
 
 md5sum="$(which md5sum 2>/dev/null || command -v md5sum 2>/dev/null)"
 get_git_config_signatures() {
-       local x s file md5
-
-       [ ! -d "conf.d" ] && echo >&2 "Wrong directory." && return 1
-       [ -z "${md5sum}" -o ! -x "${md5sum}" ] && echo >&2 "No md5sum command." && return 1
-
-       echo >configs.signatures.tmp
-
-       for x in $(find conf.d -name \*.conf)
-       do
-               x="${x/conf.d\//}"
-               echo "${x}"
-               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)"
-                       echo >>configs.signatures.tmp "${x}:${s}"
-                       echo "    ${s}"
-               done
-               git checkout HEAD "conf.d/${x}" || break
-       done
-
-       cat configs.signatures.tmp |\
-               grep -v "^$" |\
-               sort -u |\
-               {
-                       echo "declare -A configs_signatures=("
-                       IFS=":"
-                       while read file md5
-                       do
-                               echo "  ['${md5}']='${file}'"
-                       done
-                       echo ")"
-               } >configs.signatures
-
-       rm configs.signatures.tmp
-
-       return 0
+    local x s file md5
+
+    [ ! -d "conf.d" ] && echo >&2 "Wrong directory." && return 1
+    [ -z "${md5sum}" -o ! -x "${md5sum}" ] && echo >&2 "No md5sum command." && return 1
+
+    echo >configs.signatures.tmp
+
+    for x in $(find conf.d -name \*.conf)
+    do
+            x="${x/conf.d\//}"
+            echo "${x}"
+            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)"
+                    echo >>configs.signatures.tmp "${x}:${s}"
+                    echo "    ${s}"
+            done
+            git checkout HEAD "conf.d/${x}" || break
+    done
+
+    cat configs.signatures.tmp |\
+        grep -v "^$" |\
+        sort -u |\
+        {
+            echo "declare -A configs_signatures=("
+            IFS=":"
+            while read file md5
+            do
+                echo "  ['${md5}']='${file}'"
+            done
+            echo ")"
+        } >configs.signatures
+
+    rm configs.signatures.tmp
+
+    return 0
 }
 
 
 while [ ! -z "${1}" ]
 do
-       if [ "$1" = "--install" ]
-               then
-               NETDATA_PREFIX="${2}/netdata"
-               shift 2
-       elif [ "$1" = "--zlib-is-really-here" -o "$1" = "--libs-are-really-here" ]
-               then
-               LIBS_ARE_HERE=1
-               shift 1
-       elif [ "$1" = "--dont-start-it" ]
-               then
-               DONOTSTART=1
-               shift 1
-       elif [ "$1" = "--dont-wait" ]
-               then
-               DONOTWAIT=1
-               shift 1
-       elif [ "$1" = "--help" -o "$1" = "-h" ]
-               then
-               usage
-               exit 1
-       elif [ "$1" = "get_git_config_signatures" ]
-               then
-               get_git_config_signatures && exit 0
-               exit 1
-       else
-               echo >&2
-               echo >&2 "ERROR:"
-               echo >&2 "I cannot understand option '$1'."
-               usage
-               exit 1
-       fi
+    if [ "$1" = "--install" ]
+        then
+        NETDATA_PREFIX="${2}/netdata"
+        shift 2
+    elif [ "$1" = "--zlib-is-really-here" -o "$1" = "--libs-are-really-here" ]
+        then
+        LIBS_ARE_HERE=1
+        shift 1
+    elif [ "$1" = "--dont-start-it" ]
+        then
+        DONOTSTART=1
+        shift 1
+    elif [ "$1" = "--dont-wait" ]
+        then
+        DONOTWAIT=1
+        shift 1
+    elif [ "$1" = "--help" -o "$1" = "-h" ]
+        then
+        usage
+        exit 1
+    elif [ "$1" = "get_git_config_signatures" ]
+        then
+        get_git_config_signatures && exit 0
+        exit 1
+    else
+        echo >&2
+        echo >&2 "ERROR:"
+        echo >&2 "I cannot understand option '$1'."
+        usage
+        exit 1
+    fi
 done
 
-cat <<-BANNER
+cat <<BANNER
 
-       Welcome to netdata!
-       Nice to see you are giving it a try!
+Welcome to netdata!
+Nice to see you are giving it a try!
 
-       You are about to build and install netdata to your system.
+You are about to build and install netdata to your system.
 
-       It will be installed at these locations:
+It will be installed at these locations:
 
-         - the daemon    at ${NETDATA_PREFIX}/usr/sbin/netdata
-         - config files  at ${NETDATA_PREFIX}/etc/netdata
-         - web files     at ${NETDATA_PREFIX}/usr/share/netdata
-         - plugins       at ${NETDATA_PREFIX}/usr/libexec/netdata
-         - cache files   at ${NETDATA_PREFIX}/var/cache/netdata
-         - db files      at ${NETDATA_PREFIX}/var/lib/netdata
-         - log files     at ${NETDATA_PREFIX}/var/log/netdata
-         - pid file      at ${NETDATA_PREFIX}/var/run
+  - the daemon    at ${NETDATA_PREFIX}/usr/sbin/netdata
+  - config files  at ${NETDATA_PREFIX}/etc/netdata
+  - web files     at ${NETDATA_PREFIX}/usr/share/netdata
+  - plugins       at ${NETDATA_PREFIX}/usr/libexec/netdata
+  - cache files   at ${NETDATA_PREFIX}/var/cache/netdata
+  - db files      at ${NETDATA_PREFIX}/var/lib/netdata
+  - log files     at ${NETDATA_PREFIX}/var/log/netdata
+  - pid file      at ${NETDATA_PREFIX}/var/run
 
-       This installer allows you to change the installation path.
-       Press Control-C and run the same command with --help for help.
+This installer allows you to change the installation path.
+Press Control-C and run the same command with --help for help.
 
 BANNER
 
 if [ "${UID}" -ne 0 ]
-       then
-       if [ -z "${NETDATA_PREFIX}" ]
-               then
-               cat <<-NONROOTNOPREFIX
+    then
+    if [ -z "${NETDATA_PREFIX}" ]
+        then
+        cat <<NONROOTNOPREFIX
 
-               Sorry! This will fail!
+Sorry! This will fail!
 
-               You are attempting to install netdata as non-root, but you plan to install it
-               in system paths.
+You are attempting to install netdata as non-root, but you plan to install it
+in system paths.
 
-               Please set an installation prefix, like this:
+Please set an installation prefix, like this:
 
-                       $0 ${@} --install /tmp
+    $0 ${@} --install /tmp
 
-               or, run the installer as root:
+or, run the installer as root:
 
-                       sudo $0 ${@}
+    sudo $0 ${@}
 
-               We suggest to install it as root, or certain data collectors will not be able
-               to work. Netdata drops root privileges when running. So, if you plan to keep
-               it, install it as root to get the full functionality.
+We suggest to install it as root, or certain data collectors will not be able
+to work. Netdata drops root privileges when running. So, if you plan to keep
+it, install it as root to get the full functionality.
 
 NONROOTNOPREFIX
-               exit 1
+        exit 1
 
-       else
-               cat <<-NONROOT
+    else
+        cat <<NONROOT
 
-               IMPORTANT:
-               You are about to install netdata as a non-root user.
-               Netdata will work, but a few data collection modules that
-               require root access will fail.
+IMPORTANT:
+You are about to install netdata as a non-root user.
+Netdata will work, but a few data collection modules that
+require root access will fail.
 
-               If you installing permanently on your system, run the
-               installer like this:
+If you installing permanently on your system, run the
+installer like this:
 
-                       sudo $0 ${@}
+    sudo $0 ${@}
 
 NONROOT
-       fi
+    fi
 fi
 
 have_autotools=
 if [ "$(type autoreconf 2> /dev/null)" ]
 then
-       autoconf_maj_min() {
-               local maj min IFS=.-
-
-               maj=$1
-               min=$2
-
-               set -- $(autoreconf -V | sed -ne '1s/.* \([^ ]*\)$/\1/p')
-               eval $maj=\$1 $min=\$2
-       }
-       autoconf_maj_min AMAJ AMIN
-
-       if [ "$AMAJ" -gt 2 ]
-       then
-               have_autotools=Y
-       elif [ "$AMAJ" -eq 2 -a "$AMIN" -ge 60 ]
-       then
-               have_autotools=Y
-       else
-               echo "Found autotools $AMAJ.$AMIN"
-       fi
+    autoconf_maj_min() {
+        local maj min IFS=.-
+
+        maj=$1
+        min=$2
+
+        set -- $(autoreconf -V | sed -ne '1s/.* \([^ ]*\)$/\1/p')
+        eval $maj=\$1 $min=\$2
+    }
+    autoconf_maj_min AMAJ AMIN
+
+    if [ "$AMAJ" -gt 2 ]
+    then
+        have_autotools=Y
+    elif [ "$AMAJ" -eq 2 -a "$AMIN" -ge 60 ]
+    then
+        have_autotools=Y
+    else
+        echo "Found autotools $AMAJ.$AMIN"
+    fi
 else
-       echo "No autotools found"
+    echo "No autotools found"
 fi
 
 if [ ! "$have_autotools" ]
 then
-       if [ -f configure ]
-       then
-               echo "Will skip autoreconf step"
-       else
-               cat <<-"EOF"
+    if [ -f configure ]
+    then
+        echo "Will skip autoreconf step"
+    else
+        cat <<"EOF"
 
-               -------------------------------------------------------------------------------
-               autotools 2.60 or later is required
+-------------------------------------------------------------------------------
+autotools 2.60 or later is required
 
-               Sorry, you do not seem to have autotools 2.60 or later, which is
-               required to build from the git sources of netdata.
+Sorry, you do not seem to have autotools 2.60 or later, which is
+required to build from the git sources of netdata.
 
-               You can either install a suitable version of autotools and automake
-               or download a netdata package which does not have these dependencies.
+You can either install a suitable version of autotools and automake
+or download a netdata package which does not have these dependencies.
 
-               Source packages where autotools have already been run are available
-               here:
-                          https://firehol.org/download/netdata/
+Source packages where autotools have already been run are available
+here:
+       https://firehol.org/download/netdata/
 
-               The unsigned/master folder tracks the head of the git tree and released
-               packages are also available.
+The unsigned/master folder tracks the head of the git tree and released
+packages are also available.
 EOF
-               exit 1
-       fi
+        exit 1
+    fi
 fi
 
 if [ ${DONOTWAIT} -eq 0 ]
-       then
-       if [ ! -z "${NETDATA_PREFIX}" ]
-               then
-               read -p "Press ENTER to build and install netdata to '${NETDATA_PREFIX}' > "
-       else
-               read -p "Press ENTER to build and install netdata to your system > "
-       fi
+    then
+    if [ ! -z "${NETDATA_PREFIX}" ]
+        then
+        read -p "Press ENTER to build and install netdata to '${NETDATA_PREFIX}' > "
+    else
+        read -p "Press ENTER to build and install netdata to your system > "
+    fi
 fi
 
 build_error() {
-       cat <<-EOF
+    cat <<EOF
 
-       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-       Sorry! NetData failed to build...
+Sorry! NetData failed to build...
 
-       You many need to check these:
+You many need to check these:
 
-       1. The package uuid-dev (or libuuid-devel) has to be installed.
+1. The package uuid-dev (or libuuid-devel) has to be installed.
 
-          If your system cannot find libuuid, although it is installed
-          run me with the option:  --libs-are-really-here
+   If your system cannot find libuuid, although it is installed
+   run me with the option:  --libs-are-really-here
 
-       2. The package zlib1g-dev (or zlib-devel) has to be installed.
+2. The package zlib1g-dev (or zlib-devel) has to be installed.
 
-          If your system cannot find zlib, although it is installed
-          run me with the option:  --libs-are-really-here
+   If your system cannot find zlib, although it is installed
+   run me with the option:  --libs-are-really-here
 
-       3. You need basic build tools installed, like:
+3. You need basic build tools installed, like:
 
-          gcc make autoconf automake pkg-config
+   gcc make autoconf automake pkg-config
 
-          Autoconf version 2.60 or higher is required.
+   Autoconf version 2.60 or higher is required.
 
-       If you still cannot get it to build, ask for help at github:
+If you still cannot get it to build, ask for help at github:
 
-          https://github.com/firehol/netdata/issues
+   https://github.com/firehol/netdata/issues
 
 
 EOF
-       trap - EXIT
-       exit 1
+    trap - EXIT
+    exit 1
 }
 
 run() {
-       printf >>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}
+    printf >>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
-       echo >&2 "ok, assuming libs are really installed."
-       export ZLIB_CFLAGS=" "
-       export ZLIB_LIBS="-lz"
-       export UUID_CFLAGS=" "
-       export UUID_LIBS="-luuid"
+    then
+    shift
+    echo >&2 "ok, assuming libs are really installed."
+    export ZLIB_CFLAGS=" "
+    export ZLIB_LIBS="-lz"
+    export UUID_CFLAGS=" "
+    export UUID_LIBS="-luuid"
 fi
 
 trap build_error EXIT
 
 if [ "$have_autotools" ]
 then
-       run ./autogen.sh || exit 1
+    run ./autogen.sh || exit 1
 fi
 
 run ./configure \
-       --prefix="${NETDATA_PREFIX}/usr" \
-       --sysconfdir="${NETDATA_PREFIX}/etc" \
-       --localstatedir="${NETDATA_PREFIX}/var" \
-       --with-zlib --with-math --with-user=netdata \
-       CFLAGS="${CFLAGS}" || exit 1
+    --prefix="${NETDATA_PREFIX}/usr" \
+    --sysconfdir="${NETDATA_PREFIX}/etc" \
+    --localstatedir="${NETDATA_PREFIX}/var" \
+    --with-zlib --with-math --with-user=netdata \
+    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
+    then
+    echo >&2 "Cleaning a possibly old compilation ..."
+    run make clean
 fi
 
 echo >&2 "Compiling netdata ..."
@@ -401,101 +401,101 @@ run make || 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
+    declare -A configs_signatures=()
+    if [ -f "configs.signatures" ]
+        then
+        source "configs.signatures" || echo >&2 "ERROR: Failed to load configs.signatures !"
+    fi
 fi
 
 # migrate existing configuration files
 # for node.d and charts.d
 if [ -d "${NETDATA_PREFIX}/etc/netdata" ]
-       then
-       # the configuration directory exists
-
-       if [ ! -d "${NETDATA_PREFIX}/etc/netdata/charts.d" ]
-               then
-               run mkdir "${NETDATA_PREFIX}/etc/netdata/charts.d"
-       fi
-
-       # move the charts.d config files
-       for x in apache ap cpu_apps cpufreq example exim hddtemp load_average mem_apps mysql nginx nut opensips phpfpm postfix sensors squid tomcat
-       do
-               for y in "" ".old" ".orig"
-               do
-                       if [ -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" -a ! -f "${NETDATA_PREFIX}/etc/netdata/charts.d/${x}.conf${y}" ]
-                               then
-                               run mv -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" "${NETDATA_PREFIX}/etc/netdata/charts.d/${x}.conf${y}"
-                       fi
-               done
-       done
-
-       if [ ! -d "${NETDATA_PREFIX}/etc/netdata/node.d" ]
-               then
-               run mkdir "${NETDATA_PREFIX}/etc/netdata/node.d"
-       fi
-
-       # move the node.d config files
-       for x in named sma_webbox snmp
-       do
-               for y in "" ".old" ".orig"
-               do
-                       if [ -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" -a ! -f "${NETDATA_PREFIX}/etc/netdata/node.d/${x}.conf${y}" ]
-                               then
-                               run mv -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" "${NETDATA_PREFIX}/etc/netdata/node.d/${x}.conf${y}"
-                       fi
-               done
-       done
+    then
+    # the configuration directory exists
+
+    if [ ! -d "${NETDATA_PREFIX}/etc/netdata/charts.d" ]
+        then
+        run mkdir "${NETDATA_PREFIX}/etc/netdata/charts.d"
+    fi
+
+    # move the charts.d config files
+    for x in apache ap cpu_apps cpufreq example exim hddtemp load_average mem_apps mysql nginx nut opensips phpfpm postfix sensors squid tomcat
+    do
+        for y in "" ".old" ".orig"
+        do
+            if [ -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" -a ! -f "${NETDATA_PREFIX}/etc/netdata/charts.d/${x}.conf${y}" ]
+                then
+                run mv -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" "${NETDATA_PREFIX}/etc/netdata/charts.d/${x}.conf${y}"
+            fi
+        done
+    done
+
+    if [ ! -d "${NETDATA_PREFIX}/etc/netdata/node.d" ]
+        then
+        run mkdir "${NETDATA_PREFIX}/etc/netdata/node.d"
+    fi
+
+    # move the node.d config files
+    for x in named sma_webbox snmp
+    do
+        for y in "" ".old" ".orig"
+        do
+            if [ -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" -a ! -f "${NETDATA_PREFIX}/etc/netdata/node.d/${x}.conf${y}" ]
+                then
+                run mv -f "${NETDATA_PREFIX}/etc/netdata/${x}.conf${y}" "${NETDATA_PREFIX}/etc/netdata/node.d/${x}.conf${y}"
+            fi
+        done
+    done
 fi
 
 # backup user configurations
 installer_backup_suffix="${PID}.${RANDOM}"
 for x in $(find "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f)
 do
-       if [ -f "${x}" ]
-               then
-               # make a backup of the configuration file
-               cp -p "${x}" "${x}.old"
-
-               if [ -z "${md5sum}" -o ! -x "${md5sum}" ]
-                       then
-                       # we don't have md5sum - keep it
-                       cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}"
-               else
-                       # find it relative filename
-                       f="${x/*\/etc\/netdata\//}"
-
-                       # find its checksum
-                       md5="$(cat "${x}" | ${md5sum} | cut -d ' ' -f 1)"
-
-                       # copy the original
-                       if [ -f "conf.d/${f}" ]
-                               then
-                               cp "conf.d/${f}" "${x}.orig"
-                       fi
-
-                       if [ "${BASH_VERSINFO[0]}" -ge "4" ]
-                       then
-                               if [ "${configs_signatures[${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
-                       else
-                               echo >&2 "File '${x}' cannot be check for custom edits."
-                               cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}"
-                       fi
-               fi
-
-       elif [ -f "${x}.installer_backup.${installer_backup_suffix}" ]
-               then
-               rm -f "${x}.installer_backup.${installer_backup_suffix}"
-       fi
+    if [ -f "${x}" ]
+        then
+        # make a backup of the configuration file
+        cp -p "${x}" "${x}.old"
+
+        if [ -z "${md5sum}" -o ! -x "${md5sum}" ]
+            then
+            # we don't have md5sum - keep it
+            cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}"
+        else
+            # find it relative filename
+            f="${x/*\/etc\/netdata\//}"
+
+            # find its checksum
+            md5="$(cat "${x}" | ${md5sum} | cut -d ' ' -f 1)"
+
+            # copy the original
+            if [ -f "conf.d/${f}" ]
+                then
+                cp "conf.d/${f}" "${x}.orig"
+            fi
+
+            if [ "${BASH_VERSINFO[0]}" -ge "4" ]
+            then
+                if [ "${configs_signatures[${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
+            else
+                echo >&2 "File '${x}' cannot be check for custom edits."
+                cp -p "${x}" "${x}.installer_backup.${installer_backup_suffix}"
+            fi
+        fi
+
+    elif [ -f "${x}.installer_backup.${installer_backup_suffix}" ]
+        then
+        rm -f "${x}.installer_backup.${installer_backup_suffix}"
+    fi
 done
 
 echo >&2 "Installing netdata ..."
@@ -504,53 +504,53 @@ run make install || exit 1
 # restore user configurations
 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}"
-       fi
+    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}"
+    fi
 done
 
 NETDATA_ADDED_TO_DOCKER=0
 if [ ${UID} -eq 0 ]
-       then
-       getent group netdata > /dev/null
-       if [ $? -ne 0 ]
-               then
-               echo >&2 "Adding netdata user group ..."
-               run groupadd -r netdata
-       fi
-
-       getent passwd netdata > /dev/null
-       if [ $? -ne 0 ]
-               then
-               echo >&2 "Adding netdata user account ..."
-               run useradd -r -g netdata -c netdata -s $(which nologin || echo '/bin/false') -d / netdata
-       fi
-
-       getent group docker > /dev/null
-       if [ $? -eq 0 ]
-               then
-               # find the users in the docker group
-               docker=$(getent group docker | cut -d ':' -f 4)
-               if [[ ",${docker}," =~ ,netdata, ]]
-                       then
-                       # netdata is already there
-                       :
-               else
-                       # netdata is not in docker group
-                       echo >&2 "Adding netdata user to the docker group (needed to get container names) ..."
-                       run usermod -a -G docker netdata
-               fi
-               # let the uninstall script know
-               NETDATA_ADDED_TO_DOCKER=1
-       fi
-
-       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
+    then
+    getent group netdata > /dev/null
+    if [ $? -ne 0 ]
+        then
+        echo >&2 "Adding netdata user group ..."
+        run groupadd -r netdata
+    fi
+
+    getent passwd netdata > /dev/null
+    if [ $? -ne 0 ]
+        then
+        echo >&2 "Adding netdata user account ..."
+        run useradd -r -g netdata -c netdata -s $(which nologin || echo '/bin/false') -d / netdata
+    fi
+
+    getent group docker > /dev/null
+    if [ $? -eq 0 ]
+        then
+        # find the users in the docker group
+        docker=$(getent group docker | cut -d ':' -f 4)
+        if [[ ",${docker}," =~ ,netdata, ]]
+            then
+            # netdata is already there
+            :
+        else
+            # netdata is not in docker group
+            echo >&2 "Adding netdata user to the docker group (needed to get container names) ..."
+            run usermod -a -G docker netdata
+        fi
+        # let the uninstall script know
+        NETDATA_ADDED_TO_DOCKER=1
+    fi
+
+    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
 fi
 
 
@@ -562,15 +562,15 @@ fi
 
 # function to extract values from the config file
 config_option() {
-       local key="${1}" value="${2}" line=
+    local key="${1}" value="${2}" line=
 
-       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" )"
-       fi
+    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" )"
+    fi
 
-       echo "${value}"
+    echo "${value}"
 }
 
 # user
@@ -612,304 +612,304 @@ NETDATA_RUN_DIR="${NETDATA_PREFIX}/var/run"
 
 # this is needed if NETDATA_PREFIX is not empty
 if [ ! -d "${NETDATA_RUN_DIR}" ]
-       then
-       mkdir -p "${NETDATA_RUN_DIR}" || exit 1
+    then
+    mkdir -p "${NETDATA_RUN_DIR}" || exit 1
 fi
 
 echo >&2
 echo >&2 "Fixing directories (user: ${NETDATA_USER})..."
 for x in "${NETDATA_WEB_DIR}" "${NETDATA_CONF_DIR}" "${NETDATA_CACHE_DIR}" "${NETDATA_LOG_DIR}" "${NETDATA_LIB_DIR}" "${NETDATA_CONF_DIR}/python.d" "${NETDATA_CONF_DIR}/charts.d" "${NETDATA_CONF_DIR}/node.d"
 do
-       if [ ! -d "${x}" ]
-               then
-               echo >&2 "Creating directory '${x}'"
-               run mkdir -p "${x}" || exit 1
-       fi
-
-       if [ ${UID} -eq 0 ]
-               then
-               if [ "${x}" = "${NETDATA_WEB_DIR}" ]
-                       then
-                       run chown -R "${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}..."
-               else
-                       run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_USER}..."
-               fi
-       fi
-
-       run chmod 0755 "${x}" || echo >&2 "WARNING: Cannot change the permissions of the directory ${x} to 0755..."
+    if [ ! -d "${x}" ]
+        then
+        echo >&2 "Creating directory '${x}'"
+        run mkdir -p "${x}" || exit 1
+    fi
+
+    if [ ${UID} -eq 0 ]
+        then
+        if [ "${x}" = "${NETDATA_WEB_DIR}" ]
+            then
+            run chown -R "${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_WEB_USER}:${NETDATA_WEB_GROUP}..."
+        else
+            run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_USER}..."
+        fi
+    fi
+
+    run chmod 0755 "${x}" || echo >&2 "WARNING: Cannot change the permissions of the directory ${x} to 0755..."
 done
 
 if [ ${UID} -eq 0 ]
-       then
-       run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
-       run chmod 0755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
-       run setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
-       if [ $? -ne 0 ]
-               then
-               # fix apps.plugin to be setuid to root
-               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
+    then
+    run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    run chmod 0755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    run setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    if [ $? -ne 0 ]
+        then
+        # fix apps.plugin to be setuid to root
+        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
 fi
 
 # -----------------------------------------------------------------------------
 # check if we can re-start netdata
 
 if [ ${DONOTSTART} -eq 1 ]
-       then
-       if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]
-               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"
-
-               if [ "${UID}" -eq 0 ]
-                       then
-                       chown "${NETDATA_USER}" "${NETDATA_PREFIX}/etc/netdata/netdata.conf"
-               fi
-               chmod 0664 "${NETDATA_PREFIX}/etc/netdata/netdata.conf"
-       fi
-       echo >&2 "OK. It is now installed and ready."
-       exit 0
+    then
+    if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]
+        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"
+
+        if [ "${UID}" -eq 0 ]
+            then
+            chown "${NETDATA_USER}" "${NETDATA_PREFIX}/etc/netdata/netdata.conf"
+        fi
+        chmod 0664 "${NETDATA_PREFIX}/etc/netdata/netdata.conf"
+    fi
+    echo >&2 "OK. It is now installed and ready."
+    exit 0
 fi
 
 # -----------------------------------------------------------------------------
 # stop a running netdata
 
 isnetdata() {
-       [ -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
+    [ -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
 }
 
 stop_netdata_on_pid() {
-       local pid="$1" ret=0 count=0
+    local pid="$1" ret=0 count=0
 
-       isnetdata $pid || return 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
+    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 ))
+        count=$(( count + 1 ))
 
-               run kill $pid 2>/dev/null
-               ret=$?
+        run kill $pid 2>/dev/null
+        ret=$?
 
-               test $ret -eq 0 && printf >&2 "." && sleep 2
-       done
+        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
+    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
+    echo >&2 "netdata on pid $pid stopped."
+    return 0
 }
 
 stop_all_netdata() {
-       local p
+    local p
 
-       echo >&2 "Stopping a (possibly) running netdata..."
+    echo >&2 "Stopping a (possibly) running netdata..."
 
-       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
-               stop_netdata_on_pid $p
-       done
+    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
+        stop_netdata_on_pid $p
+    done
 }
 
 # -----------------------------------------------------------------------------
 # check netdata for systemd
 
 issystemd() {
-       # if the directory /etc/systemd/system does not exit, it is not systemd
-       [ ! -d /etc/systemd/system ] && return 1
+    # if the directory /etc/systemd/system does not exit, it is not systemd
+    [ ! -d /etc/systemd/system ] && return 1
 
-       # if pid 1 is systemd, it is systemd
-       [ "$(basename $(readlink /proc/1/exe) 2>/dev/null)" = "systemd" ] && return 0
+    # if pid 1 is systemd, it is systemd
+    [ "$(basename $(readlink /proc/1/exe) 2>/dev/null)" = "systemd" ] && return 0
 
-       # if systemd is running, it is systemd
-       pidof systemd >/dev/null 2>&1 && return 0
+    # if systemd is running, it is systemd
+    pidof systemd >/dev/null 2>&1 && return 0
 
-       # else, it is not systemd
-       return 1
+    # else, it is not systemd
+    return 1
 }
 
 started=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
-               else
-                       service netdata stop
-               fi
-
-               stop_all_netdata
-               service netdata restart && started=1
-       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
-               if [ ${started} -eq 0 ]
-               then
-                       service netdata start && started=1
-               fi
-       fi
+    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
+        else
+            service netdata stop
+        fi
+
+        stop_all_netdata
+        service netdata restart && started=1
+    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
+        if [ ${started} -eq 0 ]
+        then
+            service netdata start && started=1
+        fi
+    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
+    # 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
 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
-
-       # remove a possibly obsolete download
-       [ -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] && rm "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new"
-
-       # disable a proxy to get data from the local netdata
-       export http_proxy=
-       export https_proxy=
-
-       # try wget
-       wget 2>/dev/null -O "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf"
-       ret=$?
-
-       # 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 [ $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"
-
-               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
+    then
+    echo >&2
+    echo >&2 "-------------------------------------------------------------------------------"
+    echo >&2
+    echo >&2 "Downloading default configuration from netdata..."
+    sleep 5
+
+    # remove a possibly obsolete download
+    [ -f "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" ] && rm "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new"
+
+    # disable a proxy to get data from the local netdata
+    export http_proxy=
+    export https_proxy=
+
+    # try wget
+    wget 2>/dev/null -O "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "http://localhost:${NETDATA_PORT}/netdata.conf"
+    ret=$?
+
+    # 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 [ $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"
+
+        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
 fi
 
 # -----------------------------------------------------------------------------
 # Check for KSM
 
 ksm_is_available_but_disabled() {
-       cat <<-KSM1
+    cat <<KSM1
 
-       -------------------------------------------------------------------------------
-       Memory de-duplication instructions
+-------------------------------------------------------------------------------
+Memory de-duplication instructions
 
-       You have kernel memory de-duper (called Kernel Same-page Merging,
-       or KSM) available, but it is not currently enabled.
+You have kernel memory de-duper (called Kernel Same-page Merging,
+or KSM) available, but it is not currently enabled.
 
-       To enable it run:
+To enable it run:
 
-       echo 1 >/sys/kernel/mm/ksm/run
-       echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs
+echo 1 >/sys/kernel/mm/ksm/run
+echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs
 
-       If you enable it, you will save 40-60% of netdata memory.
+If you enable it, you will save 40-60% of netdata memory.
 
 KSM1
 }
 
 ksm_is_not_available() {
-       cat <<-KSM2
+    cat <<KSM2
 
-       -------------------------------------------------------------------------------
-       Memory de-duplication not present in your kernel
+-------------------------------------------------------------------------------
+Memory de-duplication not present in your kernel
 
-       It seems you do not have kernel memory de-duper (called Kernel Same-page
-       Merging, or KSM) available.
+It seems you do not have kernel memory de-duper (called Kernel Same-page
+Merging, or KSM) available.
 
-       To enable it, you need a kernel built with CONFIG_KSM=y
+To enable it, you need a kernel built with CONFIG_KSM=y
 
-       If you can have it, you will save 40-60% of netdata memory.
+If you can have it, you will save 40-60% of netdata memory.
 
 KSM2
 }
 
 if [ -f "/sys/kernel/mm/ksm/run" ]
-       then
-       if [ $(cat "/sys/kernel/mm/ksm/run") != "1" ]
-               then
-               ksm_is_available_but_disabled
-       fi
+    then
+    if [ $(cat "/sys/kernel/mm/ksm/run") != "1" ]
+        then
+        ksm_is_available_but_disabled
+    fi
 else
-       ksm_is_not_available
+    ksm_is_not_available
 fi
 
 # -----------------------------------------------------------------------------
 # Check for version.txt
 
 if [ ! -s web/version.txt ]
-       then
-       cat <<-VERMSG
+    then
+    cat <<VERMSG
 
-       -------------------------------------------------------------------------------
-       Version update check warning
+-------------------------------------------------------------------------------
+Version update check warning
 
-       The way you downloaded netdata, we cannot find its version. This means the
-       Update check on the dashboard, will not work.
+The way you downloaded netdata, we cannot find its version. This means the
+Update check on the dashboard, will not work.
 
-       If you want to have version update check, please re-install it
-       following the procedure in:
+If you want to have version update check, please re-install it
+following the procedure in:
 
-       https://github.com/firehol/netdata/wiki/Installation
+https://github.com/firehol/netdata/wiki/Installation
 
 VERMSG
 fi
@@ -918,30 +918,30 @@ fi
 # apps.plugin warning
 
 if [ "${UID}" -ne 0 ]
-       then
-       cat <<-SETUID_WARNING
+    then
+    cat <<SETUID_WARNING
 
-       -------------------------------------------------------------------------------
-       apps.plugin needs privileges
+-------------------------------------------------------------------------------
+apps.plugin needs privileges
 
-       Since you have installed netdata as a normal user, to have apps.plugin collect
-       all the needed data, you have to give it the access rights it needs, by running
-       either of the following sets of commands:
+Since you have installed netdata as a normal user, to have apps.plugin collect
+all the needed data, you have to give it the access rights it needs, by running
+either of the following sets of commands:
 
-       To run apps.plugin with escalated capabilities:
+To run apps.plugin with escalated capabilities:
 
-               sudo chown root:${NETDATA_USER} "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
-               sudo chmod 0750 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
-               sudo setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    sudo chown root:${NETDATA_USER} "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    sudo chmod 0750 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    sudo setcap cap_dac_read_search,cap_sys_ptrace+ep "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
 
-       or, to run apps.plugin as root:
+or, to run apps.plugin as root:
 
-               sudo chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
-               sudo chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    sudo chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+    sudo chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
 
-       apps.plugin is performing a hard-coded function of data collection for all
-       running processes. It cannot be instructed from the netdata daemon to perform
-       any task, so it is pretty safe to do this.
+apps.plugin is performing a hard-coded function of data collection for all
+running processes. It cannot be instructed from the netdata daemon to perform
+any task, so it is pretty safe to do this.
 
 SETUID_WARNING
 fi
@@ -949,122 +949,122 @@ fi
 # -----------------------------------------------------------------------------
 # Keep un-install info
 
-cat >netdata-uninstaller.sh <<-UNINSTALL
-       #!/bin/bash
-
-       # this script will uninstall netdata
-
-       if [ "\$1" != "--force" ]
-               then
-               echo >&2 "This script will REMOVE netdata from your system."
-               echo >&2 "Run it again with --force to do it."
-               exit 1
-       fi
-
-       echo >&2 "Stopping a possibly running netdata..."
-       for p in \$(pidof netdata); do kill \$p; done
-       sleep 2
-
-       deletedir() {
-               if [ ! -z "\$1" -a -d "\$1" ]
-                       then
-                       echo
-                       echo "Deleting directory '\$1' ..."
-                       rm -I -R "\$1"
-               fi
-       }
-
-       if [ ! -z "${NETDATA_PREFIX}" -a -d "${NETDATA_PREFIX}" ]
-               then
-               # installation prefix was given
-
-               deletedir "${NETDATA_PREFIX}"
-
-       else
-               # installation prefix was NOT given
-
-               if [ -f "${NETDATA_PREFIX}/usr/sbin/netdata" ]
-                       then
-                       echo "Deleting ${NETDATA_PREFIX}/usr/sbin/netdata ..."
-                       rm -i "${NETDATA_PREFIX}/usr/sbin/netdata"
-               fi
-
-               deletedir "${NETDATA_PREFIX}/etc/netdata"
-               deletedir "${NETDATA_PREFIX}/usr/share/netdata"
-               deletedir "${NETDATA_PREFIX}/usr/libexec/netdata"
-               deletedir "${NETDATA_PREFIX}/var/lib/netdata"
-               deletedir "${NETDATA_PREFIX}/var/cache/netdata"
-               deletedir "${NETDATA_PREFIX}/var/log/netdata"
-       fi
-
-       if [ -f /etc/logrotate.d/netdata ]
-               then
-               echo "Deleting /etc/logrotate.d/netdata ..."
-               rm -i /etc/logrotate.d/netdata
-       fi
-
-       if [ -f /etc/systemd/system/netdata.service ]
-               then
-               echo "Deleting /etc/systemd/system/netdata.service ..."
-               rm -i /etc/systemd/system/netdata.service
-       fi
-
-       getent passwd netdata > /dev/null
-       if [ $? -eq 0 ]
-               then
-               echo
-               echo "You may also want to remove the user netdata"
-               echo "by running:"
-               echo "   userdel netdata"
-       fi
-
-       getent group netdata > /dev/null
-       if [ $? -eq 0 ]
-               then
-               echo
-               echo "You may also want to remove the group netdata"
-               echo "by running:"
-               echo "   groupdel netdata"
-       fi
-
-       getent group docker > /dev/null
-       if [ $? -eq 0 -a "${NETDATA_ADDED_TO_DOCKER}" = "1" ]
-               then
-               echo
-               echo "You may also want to remove the netdata user from the docker group"
-               echo "by running:"
-               echo "   gpasswd -d netdata docker"
-       fi
+cat >netdata-uninstaller.sh <<UNINSTALL
+#!/usr/bin/env bash
+
+# this script will uninstall netdata
+
+if [ "\$1" != "--force" ]
+    then
+    echo >&2 "This script will REMOVE netdata from your system."
+    echo >&2 "Run it again with --force to do it."
+    exit 1
+fi
+
+echo >&2 "Stopping a possibly running netdata..."
+for p in \$(pidof netdata); do kill \$p; done
+sleep 2
+
+deletedir() {
+    if [ ! -z "\$1" -a -d "\$1" ]
+        then
+        echo
+        echo "Deleting directory '\$1' ..."
+        rm -I -R "\$1"
+    fi
+}
+
+if [ ! -z "${NETDATA_PREFIX}" -a -d "${NETDATA_PREFIX}" ]
+    then
+    # installation prefix was given
+
+    deletedir "${NETDATA_PREFIX}"
+
+else
+    # installation prefix was NOT given
+
+    if [ -f "${NETDATA_PREFIX}/usr/sbin/netdata" ]
+        then
+        echo "Deleting ${NETDATA_PREFIX}/usr/sbin/netdata ..."
+        rm -i "${NETDATA_PREFIX}/usr/sbin/netdata"
+    fi
+
+    deletedir "${NETDATA_PREFIX}/etc/netdata"
+    deletedir "${NETDATA_PREFIX}/usr/share/netdata"
+    deletedir "${NETDATA_PREFIX}/usr/libexec/netdata"
+    deletedir "${NETDATA_PREFIX}/var/lib/netdata"
+    deletedir "${NETDATA_PREFIX}/var/cache/netdata"
+    deletedir "${NETDATA_PREFIX}/var/log/netdata"
+fi
+
+if [ -f /etc/logrotate.d/netdata ]
+    then
+    echo "Deleting /etc/logrotate.d/netdata ..."
+    rm -i /etc/logrotate.d/netdata
+fi
+
+if [ -f /etc/systemd/system/netdata.service ]
+    then
+    echo "Deleting /etc/systemd/system/netdata.service ..."
+    rm -i /etc/systemd/system/netdata.service
+fi
+
+getent passwd netdata > /dev/null
+if [ $? -eq 0 ]
+    then
+    echo
+    echo "You may also want to remove the user netdata"
+    echo "by running:"
+    echo "   userdel netdata"
+fi
+
+getent group netdata > /dev/null
+if [ $? -eq 0 ]
+    then
+    echo
+    echo "You may also want to remove the group netdata"
+    echo "by running:"
+    echo "   groupdel netdata"
+fi
+
+getent group docker > /dev/null
+if [ $? -eq 0 -a "${NETDATA_ADDED_TO_DOCKER}" = "1" ]
+    then
+    echo
+    echo "You may also want to remove the netdata user from the docker group"
+    echo "by running:"
+    echo "   gpasswd -d netdata docker"
+fi
 
 UNINSTALL
 chmod 750 netdata-uninstaller.sh
 
 # -----------------------------------------------------------------------------
 
-cat <<-END
+cat <<END
 
 
-       -------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
 
-       OK. NetData is installed and it is running.
+OK. NetData is installed and it is running.
 
-       -------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
 
-       By default netdata listens on all IPs on port ${NETDATA_PORT},
-       so you can access it with:
+By default netdata listens on all IPs on port ${NETDATA_PORT},
+so you can access it with:
 
-       http://this.machine.ip:${NETDATA_PORT}/
+http://this.machine.ip:${NETDATA_PORT}/
 
-       To stop netdata, just kill it, with:
+To stop netdata, just kill it, with:
 
-         killall netdata
+  killall netdata
 
-       To start it, just run it:
+To start it, just run it:
 
-         ${NETDATA_PREFIX}/usr/sbin/netdata
+  ${NETDATA_PREFIX}/usr/sbin/netdata
 
 
-       Enjoy!
+Enjoy!
 
 END
 echo >&2 "Uninstall script generated: ./netdata-uninstaller.sh"
index c2b15eae72a1aec2ca84351db280b234d9ace8df..26c6ec2e09540a7ec6627957673c76060f54f705 100644 (file)
 
 /*
 {
-       "enable_autodetect": true,
-       "update_every": 5,
-       "servers": [
-               {
-                       "name": "bind1",
-                       "url": "http://127.0.0.1:8888/json/v1/server",
-                       "update_every": 1
-               },
-               {
-                       "name": "bind2",
-                       "url": "http://10.0.0.1:8888/xml/v3/server",
-                       "update_every": 2
-               }
-       ]
+    "enable_autodetect": true,
+    "update_every": 5,
+    "servers": [
+        {
+            "name": "bind1",
+            "url": "http://127.0.0.1:8888/json/v1/server",
+            "update_every": 1
+        },
+        {
+            "name": "bind2",
+            "url": "http://10.0.0.1:8888/xml/v3/server",
+            "update_every": 2
+        }
+    ]
 }
 */
 
@@ -44,543 +44,543 @@ var netdata = require('netdata');
 if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');
 
 var named = {
-       name: __filename,
-       enable_autodetect: true,
-       update_every: 1,
-       base_priority: 60000,
-       charts: {},
-
-       chartFromMembersCreate: function(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
-               var chart = {
-                       id: id,                                                                                 // the unique id of the chart
-                       name: '',                                                                               // the unique name of the chart
-                       title: service.name + ' ' + title_suffix,               // the title of the chart
-                       units: units,                                                                   // the units of the chart dimensions
-                       family: family,                                                                 // the family of the chart
-                       context: context,                                                               // the context of the chart
-                       type: type,                                                                             // the type of the chart
-                       priority: priority,                                                             // the priority relative to others in the same family
-                       update_every: service.update_every,                     // the expected update frequency of the chart
-                       dimensions: {}
-               }
-
-               var found = 0;
-               for(var x in obj) {
-                       if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {
-                               found++;
-                               chart.dimensions[x] = {
-                                       id: x,                                  // the unique id of the dimension
-                                       name: x,                                // the name of the dimension
-                                       algorithm: algorithm,   // the id of the netdata algorithm
-                                       multiplier: multiplier, // the multiplier
-                                       divisor: divisor,               // the divisor
-                                       hidden: false                   // is hidden (boolean)
-                               }
-                       }
-               }
-
-               if(found === false)
-                       return null;
-
-               chart = service.chart(id, chart);
-               this.charts[id] = chart;
-               return chart;
-       },
-
-       chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
-               var id = 'named_' + service.name + '.' + id_suffix;
-               var chart = this.charts[id];
-
-               if(typeof chart === 'undefined') {
-                       chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
-                       if(chart === null) return false;
-               }
-               else {
-                       // check if we need to re-generate the chart
-                       for(var x in obj) {
-                               if(typeof(chart.dimensions[x]) === 'undefined') {
-                                       chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
-                                       if(chart === null) return false;
-                                       break;
-                               }
-                       }
-               }
-
-               var found = 0;
-               service.begin(chart);
-               for(var x in obj) {
-                       if(typeof(chart.dimensions[x]) !== 'undefined') {
-                               found++;
-                               service.set(x, obj[x]);
-                       }
-               }
-               service.end();
-
-               if(found > 0) return true;
-               return false;
-       },
-
-       // an index to map values to different charts
-       lookups: {
-               nsstats: {},
-               resolver_stats: {},
-               numfetch: {}
-       },
-
-       // transform the XML response of bind
-       // to the JSON response of bind
-       xml2js: function(service, data_xml) {
-               var d = XML.parse(data_xml);
-               if(d === null) return null;
-
-               var data = {};
-               var len = d.server.counters.length;
-               while(len--) {
-                       var a = d.server.counters[len];
-                       if(typeof a.counter === 'undefined') continue;
-                       if(a.type === 'opcode') a.type = 'opcodes';
-                       else if(a.type === 'qtype') a.type = 'qtypes';
-                       else if(a.type === 'nsstat') a.type = 'nsstats';
-                       var aa = data[a.type] = {};
-                       var alen = 0
-                       var alen2 = a.counter.length;
-                       while(alen < alen2) {
-                               aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);
-                               alen++;
-                       }
-               }
-
-               data.views = {};
-               var vlen = d.views.view.length;
-               while(vlen--) {
-                       var vname = d.views.view[vlen].name;
-                       data.views[vname] = { resolver: {} };
-                       var len = d.views.view[vlen].counters.length;
-                       while(len--) {
-                               var a = d.views.view[vlen].counters[len];
-                               if(typeof a.counter === 'undefined') continue;
-                               if(a.type === 'resstats') a.type = 'stats';
-                               else if(a.type === 'resqtype') a.type = 'qtypes';
-                               else if(a.type === 'adbstat') a.type = 'adb';
-                               var aa = data.views[vname].resolver[a.type] = {};
-                               var alen = 0;
-                               var alen2 = a.counter.length;
-                               while(alen < alen2) {
-                                       aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);
-                                       alen++;
-                               }
-                       }
-               }
-
-               return data;
-       },
-
-       processResponse: function(service, data) {
-               if(data !== null) {
-                       var r;
-
-                       // parse XML or JSON
-                       // pepending on the URL given
-                       if(service.request.path.match(/^\/xml/) !== null)
-                               r = named.xml2js(service, data);
-                       else
-                               r = JSON.parse(data);
-
-                       if(typeof r === 'undefined' || r === null) {
-                               netdata.serviceError(service, "Cannot parse these data: " + data);
-                               return;
-                       }
-
-                       if(service.added !== true)
-                               service.commit();
-
-                       if(typeof r.nsstats !== 'undefined') {
-                               // we split the nsstats object to several others
-                               var global_requests = {}, global_requests_enable = false;
-                               var global_failures = {}, global_failures_enable = false;
-                               var global_failures_detail = {}, global_failures_detail_enable = false;
-                               var global_updates = {}, global_updates_enable = false;
-                               var protocol_queries = {}, protocol_queries_enable = false;
-                               var global_queries = {}, global_queries_enable = false;
-                               var global_queries_success = {}, global_queries_success_enable = false;
-                               var default_enable = false;
-                               var RecursClients = 0;
-
-                               // RecursClients is an absolute value
-                               if(typeof r.nsstats['RecursClients'] !== 'undefined') {
-                                       RecursClients = r.nsstats['RecursClients'];
-                                       delete r.nsstats['RecursClients'];
-                               }
-
-                               for( var x in r.nsstats ) {
-                                       // we maintain an index of the values found
-                                       // mapping them to objects splitted
-
-                                       var look = named.lookups.nsstats[x];
-                                       if(typeof look === 'undefined') {
-                                               // a new value, not found in the index
-                                               // index it:
-                                               if(x === 'Requestv4') {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: 'IPv4',
-                                                               type: 'global_requests'
-                                                       };
-                                               }
-                                               else if(x === 'Requestv6') {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: 'IPv6',
-                                                               type: 'global_requests'
-                                                       };
-                                               }
-                                               else if(x === 'QryFailure') {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: 'failures',
-                                                               type: 'global_failures'
-                                                       };
-                                               }
-                                               else if(x === 'QryUDP') {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: 'UDP',
-                                                               type: 'protocol_queries'
-                                                       };
-                                               }
-                                               else if(x === 'QryTCP') {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: 'TCP',
-                                                               type: 'protocol_queries'
-                                                       };
-                                               }
-                                               else if(x === 'QrySuccess') {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: 'queries',
-                                                               type: 'global_queries_success'
-                                                       };
-                                               }
-                                               else if(x.match(/QryRej$/) !== null) {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: x,
-                                                               type: 'global_failures_detail'
-                                                       };
-                                               }
-                                               else if(x.match(/^Qry/) !== null) {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: x,
-                                                               type: 'global_queries'
-                                                       };
-                                               }
-                                               else if(x.match(/^Update/) !== null) {
-                                                       named.lookups.nsstats[x] = {
-                                                               name: x,
-                                                               type: 'global_updates'
-                                                       };
-                                               }
-                                               else {
-                                                       // values not mapped, will remain
-                                                       // in the default map
-                                                       named.lookups.nsstats[x] = {
-                                                               name: x,
-                                                               type: 'default'
-                                                       };
-                                               }
-
-                                               look = named.lookups.nsstats[x];
-                                               // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);
-                                       }
-
-                                       switch(look.type) {
-                                               case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;
-                                               case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;
-                                               case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;
-                                               case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;
-                                               case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;
-                                               case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;
-                                               case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;
-                                               default: default_enable = true; break;
-                                       }
-                               }
-
-                               if(global_requests_enable == true)
-                                       service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'requests', 'named.requests', netdata.chartTypes.stacked, named.base_priority + 1, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(global_queries_success_enable == true)
-                                       service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'queries', 'named.queries.succcess', netdata.chartTypes.line, named.base_priority + 2, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(protocol_queries_enable == true)
-                                       service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'queries', 'named.protocol.queries', netdata.chartTypes.stacked, named.base_priority + 3, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(global_queries_enable == true)
-                                       service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'queries', 'named.global.queries', netdata.chartTypes.stacked, named.base_priority + 4, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(global_updates_enable == true)
-                                       service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'updates', 'named.global.updates', netdata.chartTypes.stacked, named.base_priority + 5, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(global_failures_enable == true)
-                                       service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'failures', 'named.global.failures', netdata.chartTypes.line, named.base_priority + 6, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(global_failures_detail_enable == true)
-                                       service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'failures', 'named.global.failures.detail', netdata.chartTypes.stacked, named.base_priority + 7, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               if(default_enable === true)
-                                       service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'other', 'named.nsstats', netdata.chartTypes.line, named.base_priority + 8, netdata.chartAlgorithms.incremental, 1, 1);
-
-                               // RecursClients chart
-                               {
-                                       var id = 'named_' + service.name + '.recursive_clients';
-                                       var chart = named.charts[id];
-
-                                       if(typeof chart === 'undefined') {
-                                               chart = {
-                                                       id: id,                                                                                 // the unique id of the chart
-                                                       name: '',                                                                               // the unique name of the chart
-                                                       title: service.name + ' Bind, Current Recursive Clients',               // the title of the chart
-                                                       units: 'clients',                                                               // the units of the chart dimensions
-                                                       family: 'clients',                                                              // the family of the chart
-                                                       context: 'named.recursive.clients',                             // the context of the chart
-                                                       type: netdata.chartTypes.line,                                  // the type of the chart
-                                                       priority: named.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: {
-                                                               'clients': {
-                                                                       id: 'clients',                                                          // the unique id of the dimension
-                                                                       name: '',                                                                       // the name of the dimension
-                                                                       algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
-                                                                       multiplier: 1,                                                          // the multiplier
-                                                                       divisor: 1,                                                                     // the divisor
-                                                                       hidden: false                                                           // is hidden (boolean)
-                                                               }
-                                                       }
-                                               };
-
-                                               chart = service.chart(id, chart);
-                                               named.charts[id] = chart;
-                                       }
-
-                                       service.begin(chart);
-                                       service.set('clients', RecursClients);
-                                       service.end();
-                               }
-                       }
-
-                       if(typeof r.opcodes !== 'undefined')
-                               service.module.chartFromMembers(service, r.opcodes, 'in_opcodes', 'Bind, Global Incoming Requests by OpCode', 'requests/s', 'requests', 'named.in.opcodes', netdata.chartTypes.stacked, named.base_priority + 9, netdata.chartAlgorithms.incremental, 1, 1);
-
-                       if(typeof r.qtypes !== 'undefined')
-                               service.module.chartFromMembers(service, r.qtypes, 'in_qtypes', 'Bind, Global Incoming Requests by Query Type', 'requests/s', 'requests', 'named.in.qtypes', netdata.chartTypes.stacked, named.base_priority + 10, netdata.chartAlgorithms.incremental, 1, 1);
-
-                       if(typeof r.sockstats !== 'undefined')
-                               service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'sockets', 'named.in.sockstats', netdata.chartTypes.line, named.base_priority + 11, netdata.chartAlgorithms.incremental, 1, 1);
-
-                       if(typeof r.views !== 'undefined') {
-                               for( var x in r.views ) {
-                                       var resolver = r.views[x].resolver;
-
-                                       if(typeof resolver !== 'undefined') {
-                                               if(typeof resolver.stats !== 'undefined') {
-                                                       var NumFetch = 0;
-                                                       var key = service.name + '.' + x;
-                                                       var default_enable = false;
-                                                       var rtt = {}, rtt_enable = false;
-
-                                                       // NumFetch is an absolute value
-                                                       if(typeof resolver.stats['NumFetch'] !== 'undefined') {
-                                                               named.lookups.numfetch[key] = true;
-                                                               NumFetch = resolver.stats['NumFetch'];
-                                                               delete resolver.stats['NumFetch'];
-                                                       }
-                                                       if(typeof resolver.stats['BucketSize'] !== 'undefined') {
-                                                               delete resolver.stats['BucketSize'];
-                                                       }
-
-                                                       // split the QryRTT* from the main chart
-                                                       for( var y in resolver.stats ) {
-                                                               // we maintain an index of the values found
-                                                               // mapping them to objects splitted
-
-                                                               var look = named.lookups.resolver_stats[y];
-                                                               if(typeof look === 'undefined') {
-                                                                       if(y.match(/^QryRTT/) !== null) {
-                                                                               named.lookups.resolver_stats[y] = {
-                                                                                       name: y,
-                                                                                       type: 'rtt'
-                                                                               };
-                                                                       }
-                                                                       else {
-                                                                               named.lookups.resolver_stats[y] = {
-                                                                                       name: y,
-                                                                                       type: 'default'
-                                                                               };
-                                                                       }
-
-                                                                       look = named.lookups.resolver_stats[y];
-                                                                       // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);
-                                                               }
-
-                                                               switch(look.type) {
-                                                                       case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;
-                                                                       default: default_enable = true; break;
-                                                               }
-                                                       }
-
-                                                       if(rtt_enable)
-                                                               service.module.chartFromMembers(service, rtt, 'view_resolver_rtt_' + x, 'Bind, ' + x + ' View, Resolver Round Trip Timings', 'queries/s', 'view_' + x, 'named.resolver.rtt', netdata.chartTypes.stacked, named.base_priority + 12, netdata.chartAlgorithms.incremental, 1, 1);
-
-                                                       if(default_enable)
-                                                               service.module.chartFromMembers(service, resolver.stats, 'view_resolver_stats_' + x, 'Bind, ' + x + ' View, Resolver Statistics', 'operations/s', 'view_' + x, 'named.resolver.stats', netdata.chartTypes.line, named.base_priority + 13, netdata.chartAlgorithms.incremental, 1, 1);
-
-                                                       // NumFetch chart
-                                                       if(typeof named.lookups.numfetch[key] !== 'undefined') {
-                                                               var id = 'named_' + service.name + '.view_resolver_numfetch_' + x;
-                                                               var chart = named.charts[id];
-
-                                                               if(typeof chart === 'undefined') {
-                                                                       chart = {
-                                                                               id: id,                                                                                 // the unique id of the chart
-                                                                               name: '',                                                                               // the unique name of the chart
-                                                                               title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries',         // the title of the chart
-                                                                               units: 'queries',                                                               // the units of the chart dimensions
-                                                                               family: 'view_' + x,                                                    // the family of the chart
-                                                                               context: 'named.resolver.active.queries',               // the context of the chart
-                                                                               type: netdata.chartTypes.line,                                  // the type of the chart
-                                                                               priority: named.base_priority + 1001,                   // the priority relative to others in the same family
-                                                                               update_every: service.update_every,                             // the expected update frequency of the chart
-                                                                               dimensions: {
-                                                                                       'queries': {
-                                                                                               id: 'queries',                                                          // the unique id of the dimension
-                                                                                               name: '',                                                                       // the name of the dimension
-                                                                                               algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
-                                                                                               multiplier: 1,                                                          // the multiplier
-                                                                                               divisor: 1,                                                                     // the divisor
-                                                                                               hidden: false                                                           // is hidden (boolean)
-                                                                                       }
-                                                                               }
-                                                                       };
-
-                                                                       chart = service.chart(id, chart);
-                                                                       named.charts[id] = chart;
-                                                               }
-
-                                                               service.begin(chart);
-                                                               service.set('queries', NumFetch);
-                                                               service.end();
-                                                       }
-                                               }
-                                       }
-
-                                       if(typeof resolver.qtypes !== 'undefined')
-                                               service.module.chartFromMembers(service, resolver.qtypes, 'view_resolver_qtypes_' + x, 'Bind, ' + x + ' View, Requests by Query Type', 'requests/s', 'view_' + x, 'named.resolver.qtypes', netdata.chartTypes.stacked, named.base_priority + 14, netdata.chartAlgorithms.incremental, 1, 1);
-
-                                       //if(typeof resolver.cache !== 'undefined')
-                                       //      service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'view_' + x, 'named.resolver.cache', netdata.chartTypes.stacked, named.base_priority + 15, netdata.chartAlgorithms.absolute, 1, 1);
-
-                                       if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) {
-                                               var id = 'named_' + service.name + '.view_resolver_cachehits_' + x;
-                                               var chart = named.charts[id];
-
-                                               if(typeof chart === 'undefined') {
-                                                       chart = {
-                                                               id: id,                                                                                 // the unique id of the chart
-                                                               name: '',                                                                               // the unique name of the chart
-                                                               title: service.name + ' Bind, ' + x + ' View, Resolver Cache Hits',             // the title of the chart
-                                                               units: 'operations/s',                                                  // the units of the chart dimensions
-                                                               family: 'view_' + x,                                                    // the family of the chart
-                                                               context: 'named.resolver.cache.hits',                   // the context of the chart
-                                                               type: netdata.chartTypes.area,                                  // the type of the chart
-                                                               priority: named.base_priority + 1100,                   // the priority relative to others in the same family
-                                                               update_every: service.update_every,                             // the expected update frequency of the chart
-                                                               dimensions: {
-                                                                       'CacheHits': {
-                                                                               id: 'CacheHits',                                                        // the unique id of the dimension
-                                                                               name: 'hits',                                                           // the name of the dimension
-                                                                               algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
-                                                                               multiplier: 1,                                                          // the multiplier
-                                                                               divisor: 1,                                                                     // the divisor
-                                                                               hidden: false                                                           // is hidden (boolean)
-                                                                       },
-                                                                       'CacheMisses': {
-                                                                               id: 'CacheMisses',                                                      // the unique id of the dimension
-                                                                               name: 'misses',                                                         // the name of the dimension
-                                                                               algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
-                                                                               multiplier: -1,                                                         // the multiplier
-                                                                               divisor: 1,                                                                     // the divisor
-                                                                               hidden: false                                                           // is hidden (boolean)
-                                                                       }
-                                                               }
-                                                       };
-
-                                                       chart = service.chart(id, chart);
-                                                       named.charts[id] = chart;
-                                               }
-
-                                               service.begin(chart);
-                                               service.set('CacheHits', resolver.cachestats['CacheHits']);
-                                               service.set('CacheMisses', resolver.cachestats['CacheMisses']);
-                                               service.end();
-                                       }
-
-                                       // this is wrong, it contains many types of info:
-                                       // 1. CacheHits, CacheMisses - incremental (added above)
-                                       // 2. QueryHits, QueryMisses - incremental
-                                       // 3. DeleteLRU, DeleteTTL - incremental
-                                       // 4. CacheNodes, CacheBuckets - absolute
-                                       // 5. TreeMemTotal, TreeMemInUse - absolute
-                                       // 6. HeapMemMax, HeapMemTotal, HeapMemInUse - absolute
-                                       //if(typeof resolver.cachestats !== 'undefined')
-                                       //      service.module.chartFromMembers(service, resolver.cachestats, 'view_resolver_cachestats_' + x, 'Bind, ' + x + ' View, Cache Statistics', 'requests/s', 'view_' + x, 'named.resolver.cache.stats', netdata.chartTypes.line, named.base_priority + 1001, netdata.chartAlgorithms.incremental, 1, 1);
-
-                                       //if(typeof resolver.adb !== 'undefined')
-                                       //      service.module.chartFromMembers(service, resolver.adb, 'view_resolver_adb_' + x, 'Bind, ' + x + ' View, ADB Statistics', 'entries', 'view_' + x, 'named.resolver.adb', netdata.chartTypes.line, named.base_priority + 1002, netdata.chartAlgorithms.absolute, 1, 1);
-                               }
-                       }
-               }
-       },
-
-       // module.serviceExecute()
-       // this function is called only from this module
-       // its purpose is to prepare the request and call
-       // netdata.serviceExecute()
-       serviceExecute: function(name, a_url, update_every) {
-               if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);
-               var service = netdata.service({
-                       name: name,
-                       request: netdata.requestFromURL(a_url),
-                       update_every: update_every,
-                       module: this
-               });
-
-               service.execute(this.processResponse);
-       },
-
-       configure: function(config) {
-               var added = 0;
-
-               if(this.enable_autodetect === true) {
-                       this.serviceExecute('local', 'http://localhost:8888/json/v1/server', this.update_every);
-                       added++;
-               }
-               
-               if(typeof(config.servers) !== 'undefined') {
-                       var len = config.servers.length;
-                       while(len--) {
-                               if(typeof config.servers[len].update_every === 'undefined')
-                                       config.servers[len].update_every = this.update_every;
-
-                               this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);
-                               added++;
-                       }
-               }
-
-               return added;
-       },
-
-       // module.update()
-       // this is called repeatidly to collect data, by calling
-       // netdata.serviceExecute()
-       update: function(service, callback) {
-               service.execute(function(serv, data) {
-                       service.module.processResponse(serv, data);
-                       callback();
-               });
-       },
+    name: __filename,
+    enable_autodetect: true,
+    update_every: 1,
+    base_priority: 60000,
+    charts: {},
+
+    chartFromMembersCreate: function(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
+        var chart = {
+            id: id,                                         // the unique id of the chart
+            name: '',                                       // the unique name of the chart
+            title: service.name + ' ' + title_suffix,       // the title of the chart
+            units: units,                                   // the units of the chart dimensions
+            family: family,                                 // the family of the chart
+            context: context,                               // the context of the chart
+            type: type,                                     // the type of the chart
+            priority: priority,                             // the priority relative to others in the same family
+            update_every: service.update_every,             // the expected update frequency of the chart
+            dimensions: {}
+        }
+
+        var found = 0;
+        for(var x in obj) {
+            if(typeof(obj[x]) !== 'undefined' && obj[x] !== 0) {
+                found++;
+                chart.dimensions[x] = {
+                    id: x,                  // the unique id of the dimension
+                    name: x,                // the name of the dimension
+                    algorithm: algorithm,   // the id of the netdata algorithm
+                    multiplier: multiplier, // the multiplier
+                    divisor: divisor,       // the divisor
+                    hidden: false           // is hidden (boolean)
+                }
+            }
+        }
+
+        if(found === false)
+            return null;
+
+        chart = service.chart(id, chart);
+        this.charts[id] = chart;
+        return chart;
+    },
+
+    chartFromMembers: function(service, obj, id_suffix, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor) {
+        var id = 'named_' + service.name + '.' + id_suffix;
+        var chart = this.charts[id];
+
+        if(typeof chart === 'undefined') {
+            chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
+            if(chart === null) return false;
+        }
+        else {
+            // check if we need to re-generate the chart
+            for(var x in obj) {
+                if(typeof(chart.dimensions[x]) === 'undefined') {
+                    chart = this.chartFromMembersCreate(service, obj, id, title_suffix, units, family, context, type, priority, algorithm, multiplier, divisor);
+                    if(chart === null) return false;
+                    break;
+                }
+            }
+        }
+
+        var found = 0;
+        service.begin(chart);
+        for(var x in obj) {
+            if(typeof(chart.dimensions[x]) !== 'undefined') {
+                found++;
+                service.set(x, obj[x]);
+            }
+        }
+        service.end();
+
+        if(found > 0) return true;
+        return false;
+    },
+
+    // an index to map values to different charts
+    lookups: {
+        nsstats: {},
+        resolver_stats: {},
+        numfetch: {}
+    },
+
+    // transform the XML response of bind
+    // to the JSON response of bind
+    xml2js: function(service, data_xml) {
+        var d = XML.parse(data_xml);
+        if(d === null) return null;
+
+        var data = {};
+        var len = d.server.counters.length;
+        while(len--) {
+            var a = d.server.counters[len];
+            if(typeof a.counter === 'undefined') continue;
+            if(a.type === 'opcode') a.type = 'opcodes';
+            else if(a.type === 'qtype') a.type = 'qtypes';
+            else if(a.type === 'nsstat') a.type = 'nsstats';
+            var aa = data[a.type] = {};
+            var alen = 0
+            var alen2 = a.counter.length;
+            while(alen < alen2) {
+                aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);
+                alen++;
+            }
+        }
+
+        data.views = {};
+        var vlen = d.views.view.length;
+        while(vlen--) {
+            var vname = d.views.view[vlen].name;
+            data.views[vname] = { resolver: {} };
+            var len = d.views.view[vlen].counters.length;
+            while(len--) {
+                var a = d.views.view[vlen].counters[len];
+                if(typeof a.counter === 'undefined') continue;
+                if(a.type === 'resstats') a.type = 'stats';
+                else if(a.type === 'resqtype') a.type = 'qtypes';
+                else if(a.type === 'adbstat') a.type = 'adb';
+                var aa = data.views[vname].resolver[a.type] = {};
+                var alen = 0;
+                var alen2 = a.counter.length;
+                while(alen < alen2) {
+                    aa[a.counter[alen].name] = parseInt(a.counter[alen]._Data);
+                    alen++;
+                }
+            }
+        }
+
+        return data;
+    },
+
+    processResponse: function(service, data) {
+        if(data !== null) {
+            var r;
+
+            // parse XML or JSON
+            // pepending on the URL given
+            if(service.request.path.match(/^\/xml/) !== null)
+                r = named.xml2js(service, data);
+            else
+                r = JSON.parse(data);
+
+            if(typeof r === 'undefined' || r === null) {
+                netdata.serviceError(service, "Cannot parse these data: " + data);
+                return;
+            }
+
+            if(service.added !== true)
+                service.commit();
+
+            if(typeof r.nsstats !== 'undefined') {
+                // we split the nsstats object to several others
+                var global_requests = {}, global_requests_enable = false;
+                var global_failures = {}, global_failures_enable = false;
+                var global_failures_detail = {}, global_failures_detail_enable = false;
+                var global_updates = {}, global_updates_enable = false;
+                var protocol_queries = {}, protocol_queries_enable = false;
+                var global_queries = {}, global_queries_enable = false;
+                var global_queries_success = {}, global_queries_success_enable = false;
+                var default_enable = false;
+                var RecursClients = 0;
+
+                // RecursClients is an absolute value
+                if(typeof r.nsstats['RecursClients'] !== 'undefined') {
+                    RecursClients = r.nsstats['RecursClients'];
+                    delete r.nsstats['RecursClients'];
+                }
+
+                for( var x in r.nsstats ) {
+                    // we maintain an index of the values found
+                    // mapping them to objects splitted
+
+                    var look = named.lookups.nsstats[x];
+                    if(typeof look === 'undefined') {
+                        // a new value, not found in the index
+                        // index it:
+                        if(x === 'Requestv4') {
+                            named.lookups.nsstats[x] = {
+                                name: 'IPv4',
+                                type: 'global_requests'
+                            };
+                        }
+                        else if(x === 'Requestv6') {
+                            named.lookups.nsstats[x] = {
+                                name: 'IPv6',
+                                type: 'global_requests'
+                            };
+                        }
+                        else if(x === 'QryFailure') {
+                            named.lookups.nsstats[x] = {
+                                name: 'failures',
+                                type: 'global_failures'
+                            };
+                        }
+                        else if(x === 'QryUDP') {
+                            named.lookups.nsstats[x] = {
+                                name: 'UDP',
+                                type: 'protocol_queries'
+                            };
+                        }
+                        else if(x === 'QryTCP') {
+                            named.lookups.nsstats[x] = {
+                                name: 'TCP',
+                                type: 'protocol_queries'
+                            };
+                        }
+                        else if(x === 'QrySuccess') {
+                            named.lookups.nsstats[x] = {
+                                name: 'queries',
+                                type: 'global_queries_success'
+                            };
+                        }
+                        else if(x.match(/QryRej$/) !== null) {
+                            named.lookups.nsstats[x] = {
+                                name: x,
+                                type: 'global_failures_detail'
+                            };
+                        }
+                        else if(x.match(/^Qry/) !== null) {
+                            named.lookups.nsstats[x] = {
+                                name: x,
+                                type: 'global_queries'
+                            };
+                        }
+                        else if(x.match(/^Update/) !== null) {
+                            named.lookups.nsstats[x] = {
+                                name: x,
+                                type: 'global_updates'
+                            };
+                        }
+                        else {
+                            // values not mapped, will remain
+                            // in the default map
+                            named.lookups.nsstats[x] = {
+                                name: x,
+                                type: 'default'
+                            };
+                        }
+
+                        look = named.lookups.nsstats[x];
+                        // netdata.error('lookup nsstats value: ' + x + ' >>> ' + named.lookups.nsstats[x].type);
+                    }
+
+                    switch(look.type) {
+                        case 'global_requests': global_requests[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_requests_enable = true; break;
+                        case 'global_queries': global_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_enable = true; break;
+                        case 'global_queries_success': global_queries_success[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_queries_success_enable = true; break;
+                        case 'global_updates': global_updates[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_updates_enable = true; break;
+                        case 'protocol_queries': protocol_queries[look.name] = r.nsstats[x]; delete r.nsstats[x]; protocol_queries_enable = true; break;
+                        case 'global_failures': global_failures[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_enable = true; break;
+                        case 'global_failures_detail': global_failures_detail[look.name] = r.nsstats[x]; delete r.nsstats[x]; global_failures_detail_enable = true; break;
+                        default: default_enable = true; break;
+                    }
+                }
+
+                if(global_requests_enable == true)
+                    service.module.chartFromMembers(service, global_requests, 'received_requests', 'Bind, Global Received Requests by IP version', 'requests/s', 'requests', 'named.requests', netdata.chartTypes.stacked, named.base_priority + 1, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(global_queries_success_enable == true)
+                    service.module.chartFromMembers(service, global_queries_success, 'global_queries_success', 'Bind, Global Successful Queries', 'queries/s', 'queries', 'named.queries.succcess', netdata.chartTypes.line, named.base_priority + 2, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(protocol_queries_enable == true)
+                    service.module.chartFromMembers(service, protocol_queries, 'protocols_queries', 'Bind, Global Queries by IP Protocol', 'queries/s', 'queries', 'named.protocol.queries', netdata.chartTypes.stacked, named.base_priority + 3, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(global_queries_enable == true)
+                    service.module.chartFromMembers(service, global_queries, 'global_queries', 'Bind, Global Queries Analysis', 'queries/s', 'queries', 'named.global.queries', netdata.chartTypes.stacked, named.base_priority + 4, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(global_updates_enable == true)
+                    service.module.chartFromMembers(service, global_updates, 'received_updates', 'Bind, Global Received Updates', 'updates/s', 'updates', 'named.global.updates', netdata.chartTypes.stacked, named.base_priority + 5, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(global_failures_enable == true)
+                    service.module.chartFromMembers(service, global_failures, 'query_failures', 'Bind, Global Query Failures', 'failures/s', 'failures', 'named.global.failures', netdata.chartTypes.line, named.base_priority + 6, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(global_failures_detail_enable == true)
+                    service.module.chartFromMembers(service, global_failures_detail, 'query_failures_detail', 'Bind, Global Query Failures Analysis', 'failures/s', 'failures', 'named.global.failures.detail', netdata.chartTypes.stacked, named.base_priority + 7, netdata.chartAlgorithms.incremental, 1, 1);
+
+                if(default_enable === true)
+                    service.module.chartFromMembers(service, r.nsstats, 'nsstats', 'Bind, Other Global Server Statistics', 'operations/s', 'other', 'named.nsstats', netdata.chartTypes.line, named.base_priority + 8, netdata.chartAlgorithms.incremental, 1, 1);
+
+                // RecursClients chart
+                {
+                    var id = 'named_' + service.name + '.recursive_clients';
+                    var chart = named.charts[id];
+
+                    if(typeof chart === 'undefined') {
+                        chart = {
+                            id: id,                                         // the unique id of the chart
+                            name: '',                                       // the unique name of the chart
+                            title: service.name + ' Bind, Current Recursive Clients',       // the title of the chart
+                            units: 'clients',                               // the units of the chart dimensions
+                            family: 'clients',                              // the family of the chart
+                            context: 'named.recursive.clients',             // the context of the chart
+                            type: netdata.chartTypes.line,                  // the type of the chart
+                            priority: named.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: {
+                                'clients': {
+                                    id: 'clients',                              // the unique id of the dimension
+                                    name: '',                                   // the name of the dimension
+                                    algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
+                                    multiplier: 1,                              // the multiplier
+                                    divisor: 1,                                 // the divisor
+                                    hidden: false                               // is hidden (boolean)
+                                }
+                            }
+                        };
+
+                        chart = service.chart(id, chart);
+                        named.charts[id] = chart;
+                    }
+
+                    service.begin(chart);
+                    service.set('clients', RecursClients);
+                    service.end();
+                }
+            }
+
+            if(typeof r.opcodes !== 'undefined')
+                service.module.chartFromMembers(service, r.opcodes, 'in_opcodes', 'Bind, Global Incoming Requests by OpCode', 'requests/s', 'requests', 'named.in.opcodes', netdata.chartTypes.stacked, named.base_priority + 9, netdata.chartAlgorithms.incremental, 1, 1);
+
+            if(typeof r.qtypes !== 'undefined')
+                service.module.chartFromMembers(service, r.qtypes, 'in_qtypes', 'Bind, Global Incoming Requests by Query Type', 'requests/s', 'requests', 'named.in.qtypes', netdata.chartTypes.stacked, named.base_priority + 10, netdata.chartAlgorithms.incremental, 1, 1);
+
+            if(typeof r.sockstats !== 'undefined')
+                service.module.chartFromMembers(service, r.sockstats, 'in_sockstats', 'Bind, Global Socket Statistics', 'operations/s', 'sockets', 'named.in.sockstats', netdata.chartTypes.line, named.base_priority + 11, netdata.chartAlgorithms.incremental, 1, 1);
+
+            if(typeof r.views !== 'undefined') {
+                for( var x in r.views ) {
+                    var resolver = r.views[x].resolver;
+
+                    if(typeof resolver !== 'undefined') {
+                        if(typeof resolver.stats !== 'undefined') {
+                            var NumFetch = 0;
+                            var key = service.name + '.' + x;
+                            var default_enable = false;
+                            var rtt = {}, rtt_enable = false;
+
+                            // NumFetch is an absolute value
+                            if(typeof resolver.stats['NumFetch'] !== 'undefined') {
+                                named.lookups.numfetch[key] = true;
+                                NumFetch = resolver.stats['NumFetch'];
+                                delete resolver.stats['NumFetch'];
+                            }
+                            if(typeof resolver.stats['BucketSize'] !== 'undefined') {
+                                delete resolver.stats['BucketSize'];
+                            }
+
+                            // split the QryRTT* from the main chart
+                            for( var y in resolver.stats ) {
+                                // we maintain an index of the values found
+                                // mapping them to objects splitted
+
+                                var look = named.lookups.resolver_stats[y];
+                                if(typeof look === 'undefined') {
+                                    if(y.match(/^QryRTT/) !== null) {
+                                        named.lookups.resolver_stats[y] = {
+                                            name: y,
+                                            type: 'rtt'
+                                        };
+                                    }
+                                    else {
+                                        named.lookups.resolver_stats[y] = {
+                                            name: y,
+                                            type: 'default'
+                                        };
+                                    }
+
+                                    look = named.lookups.resolver_stats[y];
+                                    // netdata.error('lookup resolver stats value: ' + y + ' >>> ' + look.type);
+                                }
+
+                                switch(look.type) {
+                                    case 'rtt': rtt[look.name] = resolver.stats[y]; delete resolver.stats[y]; rtt_enable = true; break;
+                                    default: default_enable = true; break;
+                                }
+                            }
+
+                            if(rtt_enable)
+                                service.module.chartFromMembers(service, rtt, 'view_resolver_rtt_' + x, 'Bind, ' + x + ' View, Resolver Round Trip Timings', 'queries/s', 'view_' + x, 'named.resolver.rtt', netdata.chartTypes.stacked, named.base_priority + 12, netdata.chartAlgorithms.incremental, 1, 1);
+
+                            if(default_enable)
+                                service.module.chartFromMembers(service, resolver.stats, 'view_resolver_stats_' + x, 'Bind, ' + x + ' View, Resolver Statistics', 'operations/s', 'view_' + x, 'named.resolver.stats', netdata.chartTypes.line, named.base_priority + 13, netdata.chartAlgorithms.incremental, 1, 1);
+
+                            // NumFetch chart
+                            if(typeof named.lookups.numfetch[key] !== 'undefined') {
+                                var id = 'named_' + service.name + '.view_resolver_numfetch_' + x;
+                                var chart = named.charts[id];
+
+                                if(typeof chart === 'undefined') {
+                                    chart = {
+                                        id: id,                                         // the unique id of the chart
+                                        name: '',                                       // the unique name of the chart
+                                        title: service.name + ' Bind, ' + x + ' View, Resolver Active Queries',     // the title of the chart
+                                        units: 'queries',                               // the units of the chart dimensions
+                                        family: 'view_' + x,                            // the family of the chart
+                                        context: 'named.resolver.active.queries',       // the context of the chart
+                                        type: netdata.chartTypes.line,                  // the type of the chart
+                                        priority: named.base_priority + 1001,           // the priority relative to others in the same family
+                                        update_every: service.update_every,             // the expected update frequency of the chart
+                                        dimensions: {
+                                            'queries': {
+                                                id: 'queries',                              // the unique id of the dimension
+                                                name: '',                                   // the name of the dimension
+                                                algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
+                                                multiplier: 1,                              // the multiplier
+                                                divisor: 1,                                 // the divisor
+                                                hidden: false                               // is hidden (boolean)
+                                            }
+                                        }
+                                    };
+
+                                    chart = service.chart(id, chart);
+                                    named.charts[id] = chart;
+                                }
+
+                                service.begin(chart);
+                                service.set('queries', NumFetch);
+                                service.end();
+                            }
+                        }
+                    }
+
+                    if(typeof resolver.qtypes !== 'undefined')
+                        service.module.chartFromMembers(service, resolver.qtypes, 'view_resolver_qtypes_' + x, 'Bind, ' + x + ' View, Requests by Query Type', 'requests/s', 'view_' + x, 'named.resolver.qtypes', netdata.chartTypes.stacked, named.base_priority + 14, netdata.chartAlgorithms.incremental, 1, 1);
+
+                    //if(typeof resolver.cache !== 'undefined')
+                    //  service.module.chartFromMembers(service, resolver.cache, 'view_resolver_cache_' + x, 'Bind, ' + x + ' View, Cache Entries', 'entries', 'view_' + x, 'named.resolver.cache', netdata.chartTypes.stacked, named.base_priority + 15, netdata.chartAlgorithms.absolute, 1, 1);
+
+                    if(typeof resolver.cachestats['CacheHits'] !== 'undefined' && resolver.cachestats['CacheHits'] > 0) {
+                        var id = 'named_' + service.name + '.view_resolver_cachehits_' + x;
+                        var chart = named.charts[id];
+
+                        if(typeof chart === 'undefined') {
+                            chart = {
+                                id: id,                                         // the unique id of the chart
+                                name: '',                                       // the unique name of the chart
+                                title: service.name + ' Bind, ' + x + ' View, Resolver Cache Hits',     // the title of the chart
+                                units: 'operations/s',                          // the units of the chart dimensions
+                                family: 'view_' + x,                            // the family of the chart
+                                context: 'named.resolver.cache.hits',           // the context of the chart
+                                type: netdata.chartTypes.area,                  // the type of the chart
+                                priority: named.base_priority + 1100,           // the priority relative to others in the same family
+                                update_every: service.update_every,             // the expected update frequency of the chart
+                                dimensions: {
+                                    'CacheHits': {
+                                        id: 'CacheHits',                            // the unique id of the dimension
+                                        name: 'hits',                               // the name of the dimension
+                                        algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
+                                        multiplier: 1,                              // the multiplier
+                                        divisor: 1,                                 // the divisor
+                                        hidden: false                               // is hidden (boolean)
+                                    },
+                                    'CacheMisses': {
+                                        id: 'CacheMisses',                          // the unique id of the dimension
+                                        name: 'misses',                             // the name of the dimension
+                                        algorithm: netdata.chartAlgorithms.incremental,// the id of the netdata algorithm
+                                        multiplier: -1,                             // the multiplier
+                                        divisor: 1,                                 // the divisor
+                                        hidden: false                               // is hidden (boolean)
+                                    }
+                                }
+                            };
+
+                            chart = service.chart(id, chart);
+                            named.charts[id] = chart;
+                        }
+
+                        service.begin(chart);
+                        service.set('CacheHits', resolver.cachestats['CacheHits']);
+                        service.set('CacheMisses', resolver.cachestats['CacheMisses']);
+                        service.end();
+                    }
+
+                    // this is wrong, it contains many types of info:
+                    // 1. CacheHits, CacheMisses - incremental (added above)
+                    // 2. QueryHits, QueryMisses - incremental
+                    // 3. DeleteLRU, DeleteTTL - incremental
+                    // 4. CacheNodes, CacheBuckets - absolute
+                    // 5. TreeMemTotal, TreeMemInUse - absolute
+                    // 6. HeapMemMax, HeapMemTotal, HeapMemInUse - absolute
+                    //if(typeof resolver.cachestats !== 'undefined')
+                    //  service.module.chartFromMembers(service, resolver.cachestats, 'view_resolver_cachestats_' + x, 'Bind, ' + x + ' View, Cache Statistics', 'requests/s', 'view_' + x, 'named.resolver.cache.stats', netdata.chartTypes.line, named.base_priority + 1001, netdata.chartAlgorithms.incremental, 1, 1);
+
+                    //if(typeof resolver.adb !== 'undefined')
+                    //  service.module.chartFromMembers(service, resolver.adb, 'view_resolver_adb_' + x, 'Bind, ' + x + ' View, ADB Statistics', 'entries', 'view_' + x, 'named.resolver.adb', netdata.chartTypes.line, named.base_priority + 1002, netdata.chartAlgorithms.absolute, 1, 1);
+                }
+            }
+        }
+    },
+
+    // module.serviceExecute()
+    // this function is called only from this module
+    // its purpose is to prepare the request and call
+    // netdata.serviceExecute()
+    serviceExecute: function(name, a_url, update_every) {
+        if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': url: ' + a_url + ', update_every: ' + update_every);
+        var service = netdata.service({
+            name: name,
+            request: netdata.requestFromURL(a_url),
+            update_every: update_every,
+            module: this
+        });
+
+        service.execute(this.processResponse);
+    },
+
+    configure: function(config) {
+        var added = 0;
+
+        if(this.enable_autodetect === true) {
+            this.serviceExecute('local', 'http://localhost:8888/json/v1/server', this.update_every);
+            added++;
+        }
+        
+        if(typeof(config.servers) !== 'undefined') {
+            var len = config.servers.length;
+            while(len--) {
+                if(typeof config.servers[len].update_every === 'undefined')
+                    config.servers[len].update_every = this.update_every;
+
+                this.serviceExecute(config.servers[len].name, config.servers[len].url, config.servers[len].update_every);
+                added++;
+            }
+        }
+
+        return added;
+    },
+
+    // module.update()
+    // this is called repeatidly to collect data, by calling
+    // netdata.serviceExecute()
+    update: function(service, callback) {
+        service.execute(function(serv, data) {
+            service.module.processResponse(serv, data);
+            callback();
+        });
+    },
 };
 
 module.exports = named;
index 5ed1c55a7c8e1e37dc29362bc5a30d2d37c1310e..a6ce12052a61e34d640458ca09fd3b4c8a32a712 100644 (file)
@@ -6,20 +6,20 @@
 // example configuration in /etc/netdata/sma_webbox.conf
 /*
 {
-       "enable_autodetect": false,
-       "update_every": 5,
-       "servers": [
-               {
-                       "name": "plant1",
-                       "hostname": "10.0.1.1",
-                       "update_every": 10
-               },
-               {
-                       "name": "plant2",
-                       "hostname": "10.0.2.1",
-                       "update_every": 15
-               }
-       ]
+    "enable_autodetect": false,
+    "update_every": 5,
+    "servers": [
+        {
+            "name": "plant1",
+            "hostname": "10.0.1.1",
+            "update_every": 10
+        },
+        {
+            "name": "plant2",
+            "hostname": "10.0.2.1",
+            "update_every": 15
+        }
+    ]
 }
 */
 
@@ -30,208 +30,208 @@ var netdata = require('netdata');
 if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');
 
 var webbox = {
-       name: __filename,
-       enable_autodetect: true,
-       update_every: 1,
-       base_priority: 60000,
-       charts: {},
-
-       processResponse: function(service, data) {
-               if(data !== null) {
-                       var r = JSON.parse(data);
-
-                       var d = {
-                               'GriPwr': {
-                                       unit: null,
-                                       value: null
-                               },
-                               'GriEgyTdy': {
-                                       unit: null,
-                                       value: null
-                               },
-                               'GriEgyTot': {
-                                       unit: null,
-                                       value: null
-                               }
-                       };
-
-                       // parse the webbox response
-                       // and put it in our d object
-                       var found = 0;
-                       var len = r.result.overview.length;
-                       while(len--) {
-                               var e = r.result.overview[len];
-                               if(typeof(d[e.meta]) !== 'undefined') {
-                                       found++;
-                                       d[e.meta].value = e.value;
-                                       d[e.meta].unit = e.unit;
-                               }
-                       }
-
-                       // add the service
-                       if(found > 0 && service.added !== true)
-                               service.commit();
-
-                       // Grid Current Power Chart
-                       if(d['GriPwr'].value !== null) {
-                               var id = 'sma_webbox_' + service.name + '.current';
-                               var chart = webbox.charts[id];
-
-                               if(typeof chart === 'undefined') {
-                                       chart = {
-                                               id: id,                                                                                 // the unique id of the chart
-                                               name: '',                                                                               // the unique name of the chart
-                                               title: service.name + ' Current Grid Power',    // the title of the chart
-                                               units: d['GriPwr'].unit,                                                // the units of the chart dimensions
-                                               family: 'now',                                                                  // the family of the chart
-                                               context: 'sma_webbox.grid.power',                               // the context of the chart
-                                               type: netdata.chartTypes.area,                                  // the type of the chart
-                                               priority: webbox.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: {
-                                                       'GriPwr': {
-                                                               id: 'GriPwr',                                                           // the unique id of the dimension
-                                                               name: 'power',                                                          // the name of the dimension
-                                                               algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
-                                                               multiplier: 1,                                                          // the multiplier
-                                                               divisor: 1,                                                                     // the divisor
-                                                               hidden: false                                                           // is hidden (boolean)
-                                                       }
-                                               }
-                                       };
-
-                                       chart = service.chart(id, chart);
-                                       webbox.charts[id] = chart;
-                               }
-
-                               service.begin(chart);
-                               service.set('GriPwr', Math.round(d['GriPwr'].value));
-                               service.end();
-                       }
-
-                       if(d['GriEgyTdy'].value !== null) {
-                               var id = 'sma_webbox_' + service.name + '.today';
-                               var chart = webbox.charts[id];
-
-                               if(typeof chart === 'undefined') {
-                                       chart = {
-                                               id: id,                                                                                 // the unique id of the chart
-                                               name: '',                                                                               // the unique name of the chart
-                                               title: service.name + ' Today Grid Power',              // the title of the chart
-                                               units: d['GriEgyTdy'].unit,                                             // the units of the chart dimensions
-                                               family: 'today',                                                                // the family of the chart
-                                               context: 'sma_webbox.grid.power.today',                 // the context of the chart
-                                               type: netdata.chartTypes.area,                                  // the type of the chart
-                                               priority: webbox.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: {
-                                                       'GriEgyTdy': {
-                                                               id: 'GriEgyTdy',                                                                // the unique id of the dimension
-                                                               name: 'power',                                                          // the name of the dimension
-                                                               algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
-                                                               multiplier: 1,                                                          // the multiplier
-                                                               divisor: 1000,                                                          // the divisor
-                                                               hidden: false                                                           // is hidden (boolean)
-                                                       }
-                                               }
-                                       };
-
-                                       chart = service.chart(id, chart);
-                                       webbox.charts[id] = chart;
-                               }
-
-                               service.begin(chart);
-                               service.set('GriEgyTdy', Math.round(d['GriEgyTdy'].value * 1000));
-                               service.end();
-                       }
-
-                       if(d['GriEgyTot'].value !== null) {
-                               var id = 'sma_webbox_' + service.name + '.total';
-                               var chart = webbox.charts[id];
-
-                               if(typeof chart === 'undefined') {
-                                       chart = {
-                                               id: id,                                                                                 // the unique id of the chart
-                                               name: '',                                                                               // the unique name of the chart
-                                               title: service.name + ' Total Grid Power',              // the title of the chart
-                                               units: d['GriEgyTot'].unit,                                             // the units of the chart dimensions
-                                               family: 'total',                                                                // the family of the chart
-                                               context: 'sma_webbox.grid.power.total',                 // the context of the chart
-                                               type: netdata.chartTypes.area,                                  // the type of the chart
-                                               priority: webbox.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: {
-                                                       'GriEgyTot': {
-                                                               id: 'GriEgyTot',                                                                // the unique id of the dimension
-                                                               name: 'power',                                                          // the name of the dimension
-                                                               algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
-                                                               multiplier: 1,                                                          // the multiplier
-                                                               divisor: 1000,                                                          // the divisor
-                                                               hidden: false                                                           // is hidden (boolean)
-                                                       }
-                                               }
-                                       };
-
-                                       chart = service.chart(id, chart);
-                                       webbox.charts[id] = chart;
-                               }
-
-                               service.begin(chart);
-                               service.set('GriEgyTot', Math.round(d['GriEgyTot'].value * 1000));
-                               service.end();
-                       }
-               }
-       },
-
-       // module.serviceExecute()
-       // this function is called only from this module
-       // its purpose is to prepare the request and call
-       // netdata.serviceExecute()
-       serviceExecute: function(name, hostname, update_every) {
-               if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': hostname: ' + hostname + ', update_every: ' + update_every);
-
-               var service = netdata.service({
-                       name: name,
-                       request: netdata.requestFromURL('http://' + hostname + '/rpc'),
-                       update_every: update_every,
-                       module: this
-               });
-               service.postData = 'RPC={"proc":"GetPlantOverview","format":"JSON","version":"1.0","id":"1"}';
-               service.request.method = 'POST';
-               service.request.headers['Content-Length'] = service.postData.length;
-
-               service.execute(this.processResponse);
-       },
-
-       configure: function(config) {
-               var added = 0;
-
-               if(typeof(config.servers) !== 'undefined') {
-                       var len = config.servers.length;
-                       while(len--) {
-                               if(typeof config.servers[len].update_every === 'undefined')
-                                       config.servers[len].update_every = this.update_every;
-
-                               if(config.servers[len].update_every < 5)
-                                       config.servers[len].update_every = 5;
-
-                               this.serviceExecute(config.servers[len].name, config.servers[len].hostname, config.servers[len].update_every);
-                               added++;
-                       }
-               }
-
-               return added;
-       },
-
-       // module.update()
-       // this is called repeatidly to collect data, by calling
-       // netdata.serviceExecute()
-       update: function(service, callback) {
-               service.execute(function(serv, data) {
-                       service.module.processResponse(serv, data);
-                       callback();
-               });
-       },
+    name: __filename,
+    enable_autodetect: true,
+    update_every: 1,
+    base_priority: 60000,
+    charts: {},
+
+    processResponse: function(service, data) {
+        if(data !== null) {
+            var r = JSON.parse(data);
+
+            var d = {
+                'GriPwr': {
+                    unit: null,
+                    value: null
+                },
+                'GriEgyTdy': {
+                    unit: null,
+                    value: null
+                },
+                'GriEgyTot': {
+                    unit: null,
+                    value: null
+                }
+            };
+
+            // parse the webbox response
+            // and put it in our d object
+            var found = 0;
+            var len = r.result.overview.length;
+            while(len--) {
+                var e = r.result.overview[len];
+                if(typeof(d[e.meta]) !== 'undefined') {
+                    found++;
+                    d[e.meta].value = e.value;
+                    d[e.meta].unit = e.unit;
+                }
+            }
+
+            // add the service
+            if(found > 0 && service.added !== true)
+                service.commit();
+
+            // Grid Current Power Chart
+            if(d['GriPwr'].value !== null) {
+                var id = 'sma_webbox_' + service.name + '.current';
+                var chart = webbox.charts[id];
+
+                if(typeof chart === 'undefined') {
+                    chart = {
+                        id: id,                                         // the unique id of the chart
+                        name: '',                                       // the unique name of the chart
+                        title: service.name + ' Current Grid Power',    // the title of the chart
+                        units: d['GriPwr'].unit,                        // the units of the chart dimensions
+                        family: 'now',                                  // the family of the chart
+                        context: 'sma_webbox.grid.power',               // the context of the chart
+                        type: netdata.chartTypes.area,                  // the type of the chart
+                        priority: webbox.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: {
+                            'GriPwr': {
+                                id: 'GriPwr',                               // the unique id of the dimension
+                                name: 'power',                              // the name of the dimension
+                                algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
+                                multiplier: 1,                              // the multiplier
+                                divisor: 1,                                 // the divisor
+                                hidden: false                               // is hidden (boolean)
+                            }
+                        }
+                    };
+
+                    chart = service.chart(id, chart);
+                    webbox.charts[id] = chart;
+                }
+
+                service.begin(chart);
+                service.set('GriPwr', Math.round(d['GriPwr'].value));
+                service.end();
+            }
+
+            if(d['GriEgyTdy'].value !== null) {
+                var id = 'sma_webbox_' + service.name + '.today';
+                var chart = webbox.charts[id];
+
+                if(typeof chart === 'undefined') {
+                    chart = {
+                        id: id,                                         // the unique id of the chart
+                        name: '',                                       // the unique name of the chart
+                        title: service.name + ' Today Grid Power',      // the title of the chart
+                        units: d['GriEgyTdy'].unit,                     // the units of the chart dimensions
+                        family: 'today',                                // the family of the chart
+                        context: 'sma_webbox.grid.power.today',         // the context of the chart
+                        type: netdata.chartTypes.area,                  // the type of the chart
+                        priority: webbox.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: {
+                            'GriEgyTdy': {
+                                id: 'GriEgyTdy',                                // the unique id of the dimension
+                                name: 'power',                              // the name of the dimension
+                                algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
+                                multiplier: 1,                              // the multiplier
+                                divisor: 1000,                              // the divisor
+                                hidden: false                               // is hidden (boolean)
+                            }
+                        }
+                    };
+
+                    chart = service.chart(id, chart);
+                    webbox.charts[id] = chart;
+                }
+
+                service.begin(chart);
+                service.set('GriEgyTdy', Math.round(d['GriEgyTdy'].value * 1000));
+                service.end();
+            }
+
+            if(d['GriEgyTot'].value !== null) {
+                var id = 'sma_webbox_' + service.name + '.total';
+                var chart = webbox.charts[id];
+
+                if(typeof chart === 'undefined') {
+                    chart = {
+                        id: id,                                         // the unique id of the chart
+                        name: '',                                       // the unique name of the chart
+                        title: service.name + ' Total Grid Power',      // the title of the chart
+                        units: d['GriEgyTot'].unit,                     // the units of the chart dimensions
+                        family: 'total',                                // the family of the chart
+                        context: 'sma_webbox.grid.power.total',         // the context of the chart
+                        type: netdata.chartTypes.area,                  // the type of the chart
+                        priority: webbox.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: {
+                            'GriEgyTot': {
+                                id: 'GriEgyTot',                                // the unique id of the dimension
+                                name: 'power',                              // the name of the dimension
+                                algorithm: netdata.chartAlgorithms.absolute,// the id of the netdata algorithm
+                                multiplier: 1,                              // the multiplier
+                                divisor: 1000,                              // the divisor
+                                hidden: false                               // is hidden (boolean)
+                            }
+                        }
+                    };
+
+                    chart = service.chart(id, chart);
+                    webbox.charts[id] = chart;
+                }
+
+                service.begin(chart);
+                service.set('GriEgyTot', Math.round(d['GriEgyTot'].value * 1000));
+                service.end();
+            }
+        }
+    },
+
+    // module.serviceExecute()
+    // this function is called only from this module
+    // its purpose is to prepare the request and call
+    // netdata.serviceExecute()
+    serviceExecute: function(name, hostname, update_every) {
+        if(netdata.options.DEBUG === true) netdata.debug(this.name + ': ' + name + ': hostname: ' + hostname + ', update_every: ' + update_every);
+
+        var service = netdata.service({
+            name: name,
+            request: netdata.requestFromURL('http://' + hostname + '/rpc'),
+            update_every: update_every,
+            module: this
+        });
+        service.postData = 'RPC={"proc":"GetPlantOverview","format":"JSON","version":"1.0","id":"1"}';
+        service.request.method = 'POST';
+        service.request.headers['Content-Length'] = service.postData.length;
+
+        service.execute(this.processResponse);
+    },
+
+    configure: function(config) {
+        var added = 0;
+
+        if(typeof(config.servers) !== 'undefined') {
+            var len = config.servers.length;
+            while(len--) {
+                if(typeof config.servers[len].update_every === 'undefined')
+                    config.servers[len].update_every = this.update_every;
+
+                if(config.servers[len].update_every < 5)
+                    config.servers[len].update_every = 5;
+
+                this.serviceExecute(config.servers[len].name, config.servers[len].hostname, config.servers[len].update_every);
+                added++;
+            }
+        }
+
+        return added;
+    },
+
+    // module.update()
+    // this is called repeatidly to collect data, by calling
+    // netdata.serviceExecute()
+    update: function(service, callback) {
+        service.execute(function(serv, data) {
+            service.module.processResponse(serv, data);
+            callback();
+        });
+    },
 };
 
 module.exports = webbox;
index 21615f6237e9e9564a3f309dbccf4443ef9db28d..ddc8985270be68ebd6c4e3a8d17e415f8273fdef 100644 (file)
@@ -5,60 +5,60 @@
 // example configuration in /etc/netdata/snmp.conf
 /*
 {
-       "enable_autodetect": false,
-       "update_every": 5,
-       "max_request_size": 50,
-       "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,
-                                       "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,
-                                       "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
-                                               }
-                                       }
-                               }
-                       }
-               }
-       ]
+    "enable_autodetect": false,
+    "update_every": 5,
+    "max_request_size": 50,
+    "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,
+                    "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,
+                    "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
+                        }
+                    }
+                }
+            }
+        }
+    ]
 }
 */
 
 // so that 24 charts will be created.
 /*
 {
-       "enable_autodetect": false,
-       "update_every": 10,
-       "max_request_size": 50,
-       "servers": [
-               {
-                       "hostname": "10.11.12.8",
-                       "community": "public",
-                       "update_every": 10,
-                       "max_request_size": 50,
-                       "options": { "timeout": 20000 },
-                       "charts": {
-                               "snmp_switch.bandwidth_port": {
-                                       "title": "Switch Bandwidth for port ",
-                                       "units": "kilobits/s",
-                                       "type": "area",
-                                       "priority": 1,
-                                       "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
-                                               }
-                                       }
-                               }
-                       }
-               }
-       ]
+    "enable_autodetect": false,
+    "update_every": 10,
+    "max_request_size": 50,
+    "servers": [
+        {
+            "hostname": "10.11.12.8",
+            "community": "public",
+            "update_every": 10,
+            "max_request_size": 50,
+            "options": { "timeout": 20000 },
+            "charts": {
+                "snmp_switch.bandwidth_port": {
+                    "title": "Switch Bandwidth for port ",
+                    "units": "kilobits/s",
+                    "type": "area",
+                    "priority": 1,
+                    "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
+                        }
+                    }
+                }
+            }
+        }
+    ]
 }
 */
 
@@ -112,325 +112,325 @@ var netdata = require('netdata');
 if(netdata.options.DEBUG === true) netdata.debug('loaded ' + __filename + ' plugin');
 
 netdata.processors.snmp = {
-       name: 'snmp',
-
-       fixoid: function(oid) {
-               if(typeof oid !== 'string')
-                       return oid;
-
-               if(oid.charAt(0) === '.')
-                       return oid.substring(1, oid.length);
-
-               return oid;
-       },
-
-       prepare: function(service) {
-               if(typeof service.snmp_oids === 'undefined' || service.snmp_oids === null || service.snmp_oids.length === 0) {
-                       // this is the first time we see this service
-
-                       if(netdata.options.DEBUG === true)
-                               netdata.debug(service.module.name + ': ' + service.name + ': preparing ' + this.name + ' OIDs');
-
-                       // build an index of all OIDs
-                       service.snmp_oids_index = {};
-                       for(var c in service.request.charts) {
-                               // for each chart
-
-                               if(netdata.options.DEBUG === true)
-                                       netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c);
-
-                               if(typeof service.request.charts[c].titleoid !== 'undefined') {
-                                               service.snmp_oids_index[this.fixoid(service.request.charts[c].titleoid)] = {
-                                                       type: 'title',
-                                                       link: service.request.charts[c]
-                                               };
-                                       }
-
-                               for(var d in service.request.charts[c].dimensions) {
-                                       // for each dimension in the chart
-
-                                       var oid = this.fixoid(service.request.charts[c].dimensions[d].oid);
-                                       var oidname = this.fixoid(service.request.charts[c].dimensions[d].oidname);
-                                       
-                                       if(netdata.options.DEBUG === true)
-                                               netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c + ', dimension: ' + d + ', OID: ' + oid + ", OID name: " + oidname);
-
-                                       // link it to the point we need to set the value to
-                                       service.snmp_oids_index[oid] = {
-                                               type: 'value',
-                                               link: service.request.charts[c].dimensions[d]
-                                       };
-
-                                       if(typeof oidname !== 'undefined')
-                                               service.snmp_oids_index[oidname] = {
-                                                       type: 'name',
-                                                       link: service.request.charts[c].dimensions[d]
-                                               };
-
-                                       // and set the value to null
-                                       service.request.charts[c].dimensions[d].value = null;
-                               }
-                       }
-
-                       if(netdata.options.DEBUG === true)
-                               netdata.debug(service.module.name + ': ' + service.name + ': indexed ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids_index));
-
-                       // now create the array of OIDs needed by net-snmp
-                       service.snmp_oids = new Array();
-                       for(var o in service.snmp_oids_index)
-                               service.snmp_oids.push(o);
-
-                       if(netdata.options.DEBUG === true)
-                               netdata.debug(service.module.name + ': ' + service.name + ': final list of ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids));
-
-                       service.snmp_oids_cleaned = 0;
-               }
-               else if(service.snmp_oids_cleaned === 0) {
-                       service.snmp_oids_cleaned = 1;
-
-                       // the second time, keep only values
-                       service.snmp_oids = new Array();
-                       for(var o in service.snmp_oids_index)
-                               if(service.snmp_oids_index[o].type === 'value')
-                                       service.snmp_oids.push(o);
-               }
-       },
-
-       getdata: function(service, index, ok, failed, callback) {
-               var that = this;
-
-               if(index >= service.snmp_oids.length) {
-                       callback((ok > 0)?{ ok: ok, failed: failed }:null);
-                       return;
-               }
-
-               var slice;
-               if(service.snmp_oids.length <= service.request.max_request_size) {
-                       slice = service.snmp_oids;
-                       index = service.snmp_oids.length;
-               }
-               else if(service.snmp_oids.length - index <= service.request.max_request_size) {
-                       slice = service.snmp_oids.slice(index, service.snmp_oids.length);
-                       index = service.snmp_oids.length;
-               }
-               else {
-                       slice = service.snmp_oids.slice(index, index + service.request.max_request_size);
-                       index += service.request.max_request_size;
-               }
-
-               if(netdata.options.DEBUG === true)
-                       netdata.debug(service.module.name + ': ' + service.name + ': making ' + slice.length + ' entries request, max is: ' + service.request.max_request_size);
-
-               service.snmp_session.get(slice, function(error, varbinds) {
-                       if(error) {
-                               service.error('Received error = ' + netdata.stringify(error) + ' varbinds = ' + netdata.stringify(varbinds));
-
-                               // make all values null
-                               var len = slice.length;
-                               while(len--)
-                                       service.snmp_oids_index[slice[len]].value = null;
-                       }
-                       else {
-                               if(netdata.options.DEBUG === true)
-                                       netdata.debug(service.module.name + ': ' + service.name + ': got valid ' + service.module.name + ' response: ' + netdata.stringify(varbinds));
-
-                               for(var i = 0; i < varbinds.length; i++) {
-                                       var value = null;
-
-                                       if(net_snmp.isVarbindError(varbinds[i])) {
-                                               if(netdata.options.DEBUG === true)
-                                                       netdata.debug(service.module.name + ': ' + service.name + ': failed ' + service.module.name + ' get for OIDs ' + varbinds[i].oid);
-
-                                               service.error('OID ' + varbinds[i].oid + ' gave error: ' + snmp.varbindError(varbinds[i]));
-                                               value = null;
-                                               failed++;
-                                       }
-                                       else {
-                                               if(netdata.options.DEBUG === true)
-                                                       netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value);
-
-                                               value = varbinds[i].value;
-                                               ok++;
-                                       }
-
-                                       if(value !== null) {
-                                               switch(service.snmp_oids_index[varbinds[i].oid].type) {
-                                                       case 'title': service.snmp_oids_index[varbinds[i].oid].link.title += ' ' + value; break;
-                                                       case 'name' : service.snmp_oids_index[varbinds[i].oid].link.name = value; break;
-                                                       case 'value': service.snmp_oids_index[varbinds[i].oid].link.value = value; break;
-                                               }
-                                       }
-                               }
-
-                               if(netdata.options.DEBUG === true)
-                                       netdata.debug(service.module.name + ': ' + service.name + ': finished ' + service.module.name + ' with ' + ok + ' successful and ' + failed + ' failed values');
-                       }
-                       that.getdata(service, index, ok, failed, callback);
-               });
-       },
-
-       process: function(service, callback) {
-               this.prepare(service);
-
-               if(service.snmp_oids.length === 0) {
-                       // no OIDs found for this service
-
-                       if(netdata.options.DEBUG === true)
-                               service.error('no OIDs to process.');
-
-                       callback(null);
-                       return;
-               }
-
-               if(typeof service.snmp_session === 'undefined' || service.snmp_session === null) {
-                       // no SNMP session has been created for this service
-                       // the SNMP session is just the initialization of NET-SNMP
-
-                       if(netdata.options.DEBUG === true)
-                               netdata.debug(service.module.name + ': ' + service.name + ': opening ' + this.name + ' session on ' + service.request.hostname + ' community ' + service.request.community + ' options ' + netdata.stringify(service.request.options));
-
-                       // create the SNMP session
-                       service.snmp_session = net_snmp.createSession (service.request.hostname, service.request.community, service.request.options);
-
-                       if(netdata.options.DEBUG === true)
-                               netdata.debug(service.module.name + ': ' + service.name + ': got ' + this.name + ' session: ' + netdata.stringify(service.snmp_session));
-
-                       // if we later need traps, this is how to do it:
-                       //service.snmp_session.trap(net_snmp.TrapType.LinkDown, function(error) {
-                       //      if(error) console.error('trap error: ' + netdata.stringify(error));
-                       //});
-               }
-
-               // do it, get the SNMP values for the sessions we need
-               this.getdata(service, 0, 0, 0, callback);
-       }
+    name: 'snmp',
+
+    fixoid: function(oid) {
+        if(typeof oid !== 'string')
+            return oid;
+
+        if(oid.charAt(0) === '.')
+            return oid.substring(1, oid.length);
+
+        return oid;
+    },
+
+    prepare: function(service) {
+        if(typeof service.snmp_oids === 'undefined' || service.snmp_oids === null || service.snmp_oids.length === 0) {
+            // this is the first time we see this service
+
+            if(netdata.options.DEBUG === true)
+                netdata.debug(service.module.name + ': ' + service.name + ': preparing ' + this.name + ' OIDs');
+
+            // build an index of all OIDs
+            service.snmp_oids_index = {};
+            for(var c in service.request.charts) {
+                // for each chart
+
+                if(netdata.options.DEBUG === true)
+                    netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c);
+
+                if(typeof service.request.charts[c].titleoid !== 'undefined') {
+                        service.snmp_oids_index[this.fixoid(service.request.charts[c].titleoid)] = {
+                            type: 'title',
+                            link: service.request.charts[c]
+                        };
+                    }
+
+                for(var d in service.request.charts[c].dimensions) {
+                    // for each dimension in the chart
+
+                    var oid = this.fixoid(service.request.charts[c].dimensions[d].oid);
+                    var oidname = this.fixoid(service.request.charts[c].dimensions[d].oidname);
+                    
+                    if(netdata.options.DEBUG === true)
+                        netdata.debug(service.module.name + ': ' + service.name + ': indexing ' + this.name + ' chart: ' + c + ', dimension: ' + d + ', OID: ' + oid + ", OID name: " + oidname);
+
+                    // link it to the point we need to set the value to
+                    service.snmp_oids_index[oid] = {
+                        type: 'value',
+                        link: service.request.charts[c].dimensions[d]
+                    };
+
+                    if(typeof oidname !== 'undefined')
+                        service.snmp_oids_index[oidname] = {
+                            type: 'name',
+                            link: service.request.charts[c].dimensions[d]
+                        };
+
+                    // and set the value to null
+                    service.request.charts[c].dimensions[d].value = null;
+                }
+            }
+
+            if(netdata.options.DEBUG === true)
+                netdata.debug(service.module.name + ': ' + service.name + ': indexed ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids_index));
+
+            // now create the array of OIDs needed by net-snmp
+            service.snmp_oids = new Array();
+            for(var o in service.snmp_oids_index)
+                service.snmp_oids.push(o);
+
+            if(netdata.options.DEBUG === true)
+                netdata.debug(service.module.name + ': ' + service.name + ': final list of ' + this.name + ' OIDs: ' + netdata.stringify(service.snmp_oids));
+
+            service.snmp_oids_cleaned = 0;
+        }
+        else if(service.snmp_oids_cleaned === 0) {
+            service.snmp_oids_cleaned = 1;
+
+            // the second time, keep only values
+            service.snmp_oids = new Array();
+            for(var o in service.snmp_oids_index)
+                if(service.snmp_oids_index[o].type === 'value')
+                    service.snmp_oids.push(o);
+        }
+    },
+
+    getdata: function(service, index, ok, failed, callback) {
+        var that = this;
+
+        if(index >= service.snmp_oids.length) {
+            callback((ok > 0)?{ ok: ok, failed: failed }:null);
+            return;
+        }
+
+        var slice;
+        if(service.snmp_oids.length <= service.request.max_request_size) {
+            slice = service.snmp_oids;
+            index = service.snmp_oids.length;
+        }
+        else if(service.snmp_oids.length - index <= service.request.max_request_size) {
+            slice = service.snmp_oids.slice(index, service.snmp_oids.length);
+            index = service.snmp_oids.length;
+        }
+        else {
+            slice = service.snmp_oids.slice(index, index + service.request.max_request_size);
+            index += service.request.max_request_size;
+        }
+
+        if(netdata.options.DEBUG === true)
+            netdata.debug(service.module.name + ': ' + service.name + ': making ' + slice.length + ' entries request, max is: ' + service.request.max_request_size);
+
+        service.snmp_session.get(slice, function(error, varbinds) {
+            if(error) {
+                service.error('Received error = ' + netdata.stringify(error) + ' varbinds = ' + netdata.stringify(varbinds));
+
+                // make all values null
+                var len = slice.length;
+                while(len--)
+                    service.snmp_oids_index[slice[len]].value = null;
+            }
+            else {
+                if(netdata.options.DEBUG === true)
+                    netdata.debug(service.module.name + ': ' + service.name + ': got valid ' + service.module.name + ' response: ' + netdata.stringify(varbinds));
+
+                for(var i = 0; i < varbinds.length; i++) {
+                    var value = null;
+
+                    if(net_snmp.isVarbindError(varbinds[i])) {
+                        if(netdata.options.DEBUG === true)
+                            netdata.debug(service.module.name + ': ' + service.name + ': failed ' + service.module.name + ' get for OIDs ' + varbinds[i].oid);
+
+                        service.error('OID ' + varbinds[i].oid + ' gave error: ' + snmp.varbindError(varbinds[i]));
+                        value = null;
+                        failed++;
+                    }
+                    else {
+                        if(netdata.options.DEBUG === true)
+                            netdata.debug(service.module.name + ': ' + service.name + ': found ' + service.module.name + ' value of OIDs ' + varbinds[i].oid + " = " + varbinds[i].value);
+
+                        value = varbinds[i].value;
+                        ok++;
+                    }
+
+                    if(value !== null) {
+                        switch(service.snmp_oids_index[varbinds[i].oid].type) {
+                            case 'title': service.snmp_oids_index[varbinds[i].oid].link.title += ' ' + value; break;
+                            case 'name' : service.snmp_oids_index[varbinds[i].oid].link.name = value; break;
+                            case 'value': service.snmp_oids_index[varbinds[i].oid].link.value = value; break;
+                        }
+                    }
+                }
+
+                if(netdata.options.DEBUG === true)
+                    netdata.debug(service.module.name + ': ' + service.name + ': finished ' + service.module.name + ' with ' + ok + ' successful and ' + failed + ' failed values');
+            }
+            that.getdata(service, index, ok, failed, callback);
+        });
+    },
+
+    process: function(service, callback) {
+        this.prepare(service);
+
+        if(service.snmp_oids.length === 0) {
+            // no OIDs found for this service
+
+            if(netdata.options.DEBUG === true)
+                service.error('no OIDs to process.');
+
+            callback(null);
+            return;
+        }
+
+        if(typeof service.snmp_session === 'undefined' || service.snmp_session === null) {
+            // no SNMP session has been created for this service
+            // the SNMP session is just the initialization of NET-SNMP
+
+            if(netdata.options.DEBUG === true)
+                netdata.debug(service.module.name + ': ' + service.name + ': opening ' + this.name + ' session on ' + service.request.hostname + ' community ' + service.request.community + ' options ' + netdata.stringify(service.request.options));
+
+            // create the SNMP session
+            service.snmp_session = net_snmp.createSession (service.request.hostname, service.request.community, service.request.options);
+
+            if(netdata.options.DEBUG === true)
+                netdata.debug(service.module.name + ': ' + service.name + ': got ' + this.name + ' session: ' + netdata.stringify(service.snmp_session));
+
+            // if we later need traps, this is how to do it:
+            //service.snmp_session.trap(net_snmp.TrapType.LinkDown, function(error) {
+            //  if(error) console.error('trap error: ' + netdata.stringify(error));
+            //});
+        }
+
+        // do it, get the SNMP values for the sessions we need
+        this.getdata(service, 0, 0, 0, callback);
+    }
 };
 
 var snmp = {
-       name: __filename,
-       enable_autodetect: true,
-       update_every: 1,
-       base_priority: 50000,
-
-       charts: {},
-
-       processResponse: function(service, data) {
-               if(data !== null) {
-                       if(service.added !== true)
-                               service.commit();
-
-                       for(var c in service.request.charts) {
-                               var chart = snmp.charts[c];
-
-                               if(typeof chart === 'undefined') {
-                                       chart = service.chart(c, service.request.charts[c]);
-                                       snmp.charts[c] = chart;
-                               }
-
-                               service.begin(chart);
-                               
-                               for( var d in service.request.charts[c].dimensions )
-                                       if(service.request.charts[c].dimensions[d].value !== null)
-                                               service.set(d, service.request.charts[c].dimensions[d].value);
-
-                               service.end();
-                       }
-               }
-       },
-
-       // module.serviceExecute()
-       // this function is called only from this module
-       // its purpose is to prepare the request and call
-       // netdata.serviceExecute()
-       serviceExecute: function(conf) {
-               if(netdata.options.DEBUG === true)
-                       netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', update_every: ' + conf.update_every);
-
-               var service = netdata.service({
-                       name: conf.hostname,
-                       request: conf,
-                       update_every: conf.update_every,
-                       module: this,
-                       processor: netdata.processors.snmp
-               });
-
-               // multiply the charts, if required
-               for(var c in service.request.charts) {
-                       if(netdata.options.DEBUG === true)
-                               netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', examining chart: ' + c);
-
-                       if(typeof service.request.charts[c].update_every === 'undefined')
-                               service.request.charts[c].update_every = service.update_every;
-
-                       if(typeof service.request.charts[c].multiply_range !== 'undefined') {
-                               var from = service.request.charts[c].multiply_range[0];
-                               var to = service.request.charts[c].multiply_range[1];
-                               var prio = service.request.charts[c].priority || 1;
-
-                               if(prio < snmp.base_priority) prio += snmp.base_priority;
-
-                               while(from <= to) {
-                                       var id = c + from.toString();
-                                       var chart = extend(true, {}, service.request.charts[c]);
-                                       chart.title += from.toString();
-                                       
-                                       if(typeof chart.titleoid !== 'undefined')
-                                               chart.titleoid += from.toString();
-
-                                       chart.priority = prio++;
-                                       for(var d in chart.dimensions) {
-                                               chart.dimensions[d].oid += from.toString();
-
-                                               if(typeof chart.dimensions[d].oidname !== 'undefined')
-                                                       chart.dimensions[d].oidname += from.toString();
-                                       }
-                                       service.request.charts[id] = chart;
-                                       from++;
-                               }
-
-                               delete service.request.charts[c];
-                       }
-                       else {
-                               if(service.request.charts[c].priority < snmp.base_priority)
-                                       service.request.charts[c].priority += snmp.base_priority;
-                       }
-               }
-
-               service.execute(this.processResponse);
-       },
-
-       configure: function(config) {
-               var added = 0;
-
-               if(typeof config.max_request_size === 'undefined')
-                       config.max_request_size = 50;
-
-               if(typeof(config.servers) !== 'undefined') {
-                       var len = config.servers.length;
-                       while(len--) {
-                               if(typeof config.servers[len].update_every === 'undefined')
-                                       config.servers[len].update_every = this.update_every;
-
-                               if(typeof config.servers[len].max_request_size === 'undefined')
-                                       config.servers[len].max_request_size = config.max_request_size;
-
-                               this.serviceExecute(config.servers[len]);
-                               added++;
-                       }
-               }
-
-               return added;
-       },
-
-       // module.update()
-       // this is called repeatidly to collect data, by calling
-       // service.execute()
-       update: function(service, callback) {
-               service.execute(function(serv, data) {
-                       service.module.processResponse(serv, data);
-                       callback();
-               });
-       },
+    name: __filename,
+    enable_autodetect: true,
+    update_every: 1,
+    base_priority: 50000,
+
+    charts: {},
+
+    processResponse: function(service, data) {
+        if(data !== null) {
+            if(service.added !== true)
+                service.commit();
+
+            for(var c in service.request.charts) {
+                var chart = snmp.charts[c];
+
+                if(typeof chart === 'undefined') {
+                    chart = service.chart(c, service.request.charts[c]);
+                    snmp.charts[c] = chart;
+                }
+
+                service.begin(chart);
+                
+                for( var d in service.request.charts[c].dimensions )
+                    if(service.request.charts[c].dimensions[d].value !== null)
+                        service.set(d, service.request.charts[c].dimensions[d].value);
+
+                service.end();
+            }
+        }
+    },
+
+    // module.serviceExecute()
+    // this function is called only from this module
+    // its purpose is to prepare the request and call
+    // netdata.serviceExecute()
+    serviceExecute: function(conf) {
+        if(netdata.options.DEBUG === true)
+            netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', update_every: ' + conf.update_every);
+
+        var service = netdata.service({
+            name: conf.hostname,
+            request: conf,
+            update_every: conf.update_every,
+            module: this,
+            processor: netdata.processors.snmp
+        });
+
+        // multiply the charts, if required
+        for(var c in service.request.charts) {
+            if(netdata.options.DEBUG === true)
+                netdata.debug(this.name + ': snmp hostname: ' + conf.hostname + ', examining chart: ' + c);
+
+            if(typeof service.request.charts[c].update_every === 'undefined')
+                service.request.charts[c].update_every = service.update_every;
+
+            if(typeof service.request.charts[c].multiply_range !== 'undefined') {
+                var from = service.request.charts[c].multiply_range[0];
+                var to = service.request.charts[c].multiply_range[1];
+                var prio = service.request.charts[c].priority || 1;
+
+                if(prio < snmp.base_priority) prio += snmp.base_priority;
+
+                while(from <= to) {
+                    var id = c + from.toString();
+                    var chart = extend(true, {}, service.request.charts[c]);
+                    chart.title += from.toString();
+                    
+                    if(typeof chart.titleoid !== 'undefined')
+                        chart.titleoid += from.toString();
+
+                    chart.priority = prio++;
+                    for(var d in chart.dimensions) {
+                        chart.dimensions[d].oid += from.toString();
+
+                        if(typeof chart.dimensions[d].oidname !== 'undefined')
+                            chart.dimensions[d].oidname += from.toString();
+                    }
+                    service.request.charts[id] = chart;
+                    from++;
+                }
+
+                delete service.request.charts[c];
+            }
+            else {
+                if(service.request.charts[c].priority < snmp.base_priority)
+                    service.request.charts[c].priority += snmp.base_priority;
+            }
+        }
+
+        service.execute(this.processResponse);
+    },
+
+    configure: function(config) {
+        var added = 0;
+
+        if(typeof config.max_request_size === 'undefined')
+            config.max_request_size = 50;
+
+        if(typeof(config.servers) !== 'undefined') {
+            var len = config.servers.length;
+            while(len--) {
+                if(typeof config.servers[len].update_every === 'undefined')
+                    config.servers[len].update_every = this.update_every;
+
+                if(typeof config.servers[len].max_request_size === 'undefined')
+                    config.servers[len].max_request_size = config.max_request_size;
+
+                this.serviceExecute(config.servers[len]);
+                added++;
+            }
+        }
+
+        return added;
+    },
+
+    // module.update()
+    // this is called repeatidly to collect data, by calling
+    // service.execute()
+    update: function(service, callback) {
+        service.execute(function(serv, data) {
+            service.module.processResponse(serv, data);
+            callback();
+        });
+    },
 };
 
 module.exports = snmp;
index 3cd3cfab5e51a38c26c148dc20cc8aa360d7c6fd..90611905ceda43828ef218cf8134181811f054a8 100755 (executable)
@@ -9,66 +9,66 @@ CGROUP="${1}"
 NAME=
 
 if [ -z "${CGROUP}" ]
-       then
-       echo >&2 "${0}: called without a cgroup name. Nothing to do."
-       exit 1
+    then
+    echo >&2 "${0}: called without a cgroup name. Nothing to do."
+    exit 1
 fi
 
 if [ -f "${CONFIG}" ]
-       then
-       NAME="$(cat "${CONFIG}" | grep "^${CGROUP} " | sed "s/[[:space:]]\+/ /g" | cut -d ' ' -f 2)"
-       if [ -z "${NAME}" ]
-               then
-               echo >&2 "${0}: cannot find cgroup '${CGROUP}' in '${CONFIG}'."
-       fi
+    then
+    NAME="$(cat "${CONFIG}" | grep "^${CGROUP} " | sed "s/[[:space:]]\+/ /g" | cut -d ' ' -f 2)"
+    if [ -z "${NAME}" ]
+        then
+        echo >&2 "${0}: cannot find cgroup '${CGROUP}' in '${CONFIG}'."
+    fi
 #else
-#      echo >&2 "${0}: configuration file '${CONFIG}' is not available."
+#   echo >&2 "${0}: configuration file '${CONFIG}' is not available."
 fi
 
 function get_name_classic {
-       DOCKERID=$1
-       echo >&2 "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\""
-       NAME="$( docker ps --filter=id="${DOCKERID}" --format="{{.Names}}" )"
+    DOCKERID=$1
+    echo >&2 "Running command: docker ps --filter=id=\"${DOCKERID}\" --format=\"{{.Names}}\""
+    NAME="$( docker ps --filter=id="${DOCKERID}" --format="{{.Names}}" )"
 }
 
 function get_name_api {
-       DOCKERID=$1
-       if [ ! -S "/var/run/docker.sock" ]
-               then
-               echo >&2 "Can't find /var/run/docker.sock"
-               return
-       fi
-       echo >&2 "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 '^{.*')
-       NAME=$(echo $JSON | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
+    DOCKERID=$1
+    if [ ! -S "/var/run/docker.sock" ]
+        then
+        echo >&2 "Can't find /var/run/docker.sock"
+        return
+    fi
+    echo >&2 "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 '^{.*')
+    NAME=$(echo $JSON | jq -r .Name,.Config.Hostname | grep -v null | head -n1 | sed 's|^/||')
 }
 
 if [ -z "${NAME}" ]
-       then
-       if [[ "${CGROUP}" =~ ^.*docker[-/\.][a-fA-F0-9]+[-\.]?.*$ ]]
-               then
-               DOCKERID="$( echo "${CGROUP}" | sed "s|^.*docker[-/]\([a-fA-F0-9]\+\)[-\.]\?.*$|\1|" )"
+    then
+    if [[ "${CGROUP}" =~ ^.*docker[-/\.][a-fA-F0-9]+[-\.]?.*$ ]]
+        then
+        DOCKERID="$( echo "${CGROUP}" | sed "s|^.*docker[-/]\([a-fA-F0-9]\+\)[-\.]\?.*$|\1|" )"
 
-               if [ ! -z "${DOCKERID}" -a \( ${#DOCKERID} -eq 64 -o ${#DOCKERID} -eq 12 \) ]
-                       then
-                       if hash docker 2>/dev/null
-                               then
-                               get_name_classic $DOCKERID
-                       else
-                               get_name_api $DOCKERID
-                       fi
-                       if [ -z "${NAME}" ]
-                               then
-                               echo >&2 "Cannot find the name of docker container '${DOCKERID}'"
-                               NAME="${DOCKERID:0:12}"
-                       else
-                               echo >&2 "Docker container '${DOCKERID}' is named '${NAME}'"
-                       fi
-               fi
-       fi
+        if [ ! -z "${DOCKERID}" -a \( ${#DOCKERID} -eq 64 -o ${#DOCKERID} -eq 12 \) ]
+            then
+            if hash docker 2>/dev/null
+                then
+                get_name_classic $DOCKERID
+            else
+                get_name_api $DOCKERID
+            fi
+            if [ -z "${NAME}" ]
+                then
+                echo >&2 "Cannot find the name of docker container '${DOCKERID}'"
+                NAME="${DOCKERID:0:12}"
+            else
+                echo >&2 "Docker container '${DOCKERID}' is named '${NAME}'"
+            fi
+        fi
+    fi
 
-       [ -z "${NAME}" ] && NAME="${CGROUP}"
-       [ ${#NAME} -gt 50 ] && NAME="${NAME:0:50}"
+    [ -z "${NAME}" ] && NAME="${CGROUP}"
+    [ ${#NAME} -gt 50 ] && NAME="${NAME:0:50}"
 fi
 
 echo >&2 "${0}: cgroup '${CGROUP}' is called '${NAME}'"
index 9f024f6321ce4157750f12da632e3f4ae7a422b3..8142f9882b6aa79573d45eb744ba04496f9a0085 100755 (executable)
@@ -14,7 +14,7 @@ tmp1="`mktemp`"
 tmp2="`mktemp`"
 
 myset() {
-       set | grep -v "^_="     | grep -v "^PIPESTATUS=" | grep -v "^BASH_LINENO="
+    set | grep -v "^_=" | grep -v "^PIPESTATUS=" | grep -v "^BASH_LINENO="
 }
 
 # save 2 'set'
@@ -25,9 +25,9 @@ myset >"$tmp2"
 diff "$tmp1" "$tmp2" >/dev/null 2>&1
 if [ $? -ne 0 ]
 then
-       # they differ, we cannot do the check
-       echo >&2 "$me: cannot check with diff."
-       can_diff=0
+    # they differ, we cannot do the check
+    echo >&2 "$me: cannot check with diff."
+    can_diff=0
 fi
 
 # do it again, now including the script
@@ -36,21 +36,21 @@ myset >"$tmp1"
 # include the plugin and its config
 if [ -f "$conf" ]
 then
-       . "$conf"
-       if [ $? -ne 0 ]
-       then
-               echo >&2 "$me: cannot load config file $conf"
-               rm "$tmp1" "$tmp2"
-               exit 1
-       fi
+    . "$conf"
+    if [ $? -ne 0 ]
+    then
+        echo >&2 "$me: cannot load config file $conf"
+        rm "$tmp1" "$tmp2"
+        exit 1
+    fi
 fi
 
 . "$chart"
 if [ $? -ne 0 ]
 then
-       echo >&2 "$me: cannot load chart file $chart"
-       rm "$tmp1" "$tmp2"
-       exit 1
+    echo >&2 "$me: cannot load chart file $chart"
+    rm "$tmp1" "$tmp2"
+    exit 1
 fi
 
 # remove all variables starting with the plugin name
@@ -58,15 +58,15 @@ myset | grep -v "^$name" >"$tmp2"
 
 if [ $can_diff -eq 1 ]
 then
-       # check if they are different
-       # make sure they don't differ
-       diff "$tmp1" "$tmp2" >&2
-       if [ $? -ne 0 ]
-       then
-               # they differ
-               rm "$tmp1" "$tmp2"
-               exit 1
-       fi
+    # check if they are different
+    # make sure they don't differ
+    diff "$tmp1" "$tmp2" >&2
+    if [ $? -ne 0 ]
+    then
+        # they differ
+        rm "$tmp1" "$tmp2"
+        exit 1
+    fi
 fi
 
 rm "$tmp1" "$tmp2"
index 8721e4c333de3191a4f16c6f6fdc2c0cf76202c6..9aaadc168547e35d2a895436292da6746f13695a 100755 (executable)
@@ -14,24 +14,24 @@ echo >&2 "$PROGRAM_NAME: started from '$PROGRAM_FILE' with options: $*"
 
 if [ $(( ${BASH_VERSINFO[0]} )) -lt 4 ]
 then
-       echo >&2
-       echo >&2 "$PROGRAM_NAME: ERROR"
-       echo >&2 "BASH version 4 or later is required."
-       echo >&2 "You are running version: ${BASH_VERSION}"
-       echo >&2 "Please upgrade."
-       echo >&2
-       exit 1
+    echo >&2
+    echo >&2 "$PROGRAM_NAME: ERROR"
+    echo >&2 "BASH version 4 or later is required."
+    echo >&2 "You are running version: ${BASH_VERSION}"
+    echo >&2 "Please upgrade."
+    echo >&2
+    exit 1
 fi
 
 # check a few commands
 require_cmd() {
-       which "$1" >/dev/null
-       if [ $? -ne 0 ]
-               then
-               echo >&2 "$PROGRAM_NAME: ERROR: Command '$1' is not found in the system path."
-               return 1
-       fi
-       return 0
+    which "$1" >/dev/null
+    if [ $? -ne 0 ]
+        then
+        echo >&2 "$PROGRAM_NAME: ERROR: Command '$1' is not found in the system path."
+        return 1
+    fi
+    return 0
 }
 
 require_cmd date || exit 1
@@ -61,7 +61,7 @@ chartsd="$pluginsd/../charts.d"
 myconfig="$confd/$PROGRAM_NAME.conf"
 
 minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}"
-update_every=${minimum_update_frequency}       # this will be overwritten by the command line
+update_every=${minimum_update_frequency}    # this will be overwritten by the command line
 
 # work around for non BASH shells
 charts_create="_create"
@@ -106,50 +106,50 @@ check=0
 chart_only=
 while [ ! -z "$1" ]
 do
-       if [ "$1" = "check" ]
-       then
-               check=1
-               shift
-               continue
-       fi
-
-       if [ "$1" = "debug" -o "$1" = "all" ]
-       then
-               debug=1
-               shift
-               continue
-       fi
-
-       if [ -f "$chartsd/$1.chart.sh" ]
-       then
-               debug=1
-               chart_only="$( echo $1.chart.sh | sed "s/\.chart\.sh$//g" )"
-               shift
-               continue
-       fi
-
-       if [ -f "$chartsd/$1" ]
-       then
-               debug=1
-               chart_only="$( echo $1 | sed "s/\.chart\.sh$//g" )"
-               shift
-               continue
-       fi
-
-       # number check
-       n="$1"
-       x=$(( n ))
-       if [ "$x" = "$n" ]
-       then
-               shift
-               update_every=$x
-               [ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency
-               continue
-       fi
-
-       echo >&2 "Cannot understand parameter $1. Aborting."
-       echo "DISABLE"
-       exit 1
+    if [ "$1" = "check" ]
+    then
+        check=1
+        shift
+        continue
+    fi
+
+    if [ "$1" = "debug" -o "$1" = "all" ]
+    then
+        debug=1
+        shift
+        continue
+    fi
+
+    if [ -f "$chartsd/$1.chart.sh" ]
+    then
+        debug=1
+        chart_only="$( echo $1.chart.sh | sed "s/\.chart\.sh$//g" )"
+        shift
+        continue
+    fi
+
+    if [ -f "$chartsd/$1" ]
+    then
+        debug=1
+        chart_only="$( echo $1 | sed "s/\.chart\.sh$//g" )"
+        shift
+        continue
+    fi
+
+    # number check
+    n="$1"
+    x=$(( n ))
+    if [ "$x" = "$n" ]
+    then
+        shift
+        update_every=$x
+        [ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency
+        continue
+    fi
+
+    echo >&2 "Cannot understand parameter $1. Aborting."
+    echo "DISABLE"
+    exit 1
 done
 
 
@@ -157,26 +157,26 @@ done
 # load my configuration
 
 if [ -f "$myconfig" ]
-       then
-       . "$myconfig"
-       if [ $? -ne 0 ]
-       then
-               echo >&2 "$PROGRAM_NAME: cannot load $myconfig"
-               echo "DISABLE"
-               exit 1
-       fi
-       time_divisor=$((time_divisor))
-       [ $time_divisor -lt 10 ] && time_divisor=10
-       [ $time_divisor -gt 100 ] && time_divisor=100
+    then
+    . "$myconfig"
+    if [ $? -ne 0 ]
+    then
+        echo >&2 "$PROGRAM_NAME: cannot load $myconfig"
+        echo "DISABLE"
+        exit 1
+    fi
+    time_divisor=$((time_divisor))
+    [ $time_divisor -lt 10 ] && time_divisor=10
+    [ $time_divisor -gt 100 ] && time_divisor=100
 else
-       echo >&2 "$PROGRAM_NAME: configuration file '$myconfig' not found. Using defaults."
+    echo >&2 "$PROGRAM_NAME: configuration file '$myconfig' not found. Using defaults."
 fi
 
 if [ "$pause_method" = "suspend" ]
 then
-       # enable bash job control
-       # this is required for suspend to work
-       set -m
+    # enable bash job control
+    # this is required for suspend to work
+    set -m
 fi
 
 # we check for the timeout command, after we load our
@@ -185,22 +185,22 @@ fi
 # can emulate the timeout command we need:
 # > timeout SECONDS command ...
 if [ $check_for_timeout -eq 1 ]
-       then
-       require_cmd timeout || exit 1
+    then
+    require_cmd timeout || exit 1
 fi
 
 # -----------------------------------------------------------------------------
 # internal checks
 
 # netdata passes the requested update frequency as the first argument
-update_every=$(( update_every + 1 - 1))        # makes sure it is a number
+update_every=$(( update_every + 1 - 1)) # makes sure it is a number
 test $update_every -eq 0 && update_every=1 # if it is zero, make it 1
 
 # check the charts.d directory
 if [ ! -d "$chartsd" ]
-       then
-       echo >&2 "$PROGRAM_NAME: cannot find charts directory '$chartsd'"
-       echo "DISABLE"
+    then
+    echo >&2 "$PROGRAM_NAME: cannot find charts directory '$chartsd'"
+    echo "DISABLE"
 fi
 
 
@@ -210,13 +210,13 @@ fi
 # default sleep function
 LOOPSLEEPMS_HIGHRES=0
 loopsleepms() {
-       [ "$1" = "tellwork" ] && shift
-       sleep $1
+    [ "$1" = "tellwork" ] && shift
+    sleep $1
 }
 
 now_ms=
 current_time_ms() {
-       now_ms="$(date +'%s')000"
+    now_ms="$(date +'%s')000"
 }
 
 # if found and included, this file overwrites loopsleepms()
@@ -229,10 +229,10 @@ current_time_ms() {
 # library functions
 
 fixid() {
-       echo "$*" |\
-               tr -c "[A-Z][a-z][0-9]" "_" |\
-               sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" |\
-               tr "[A-Z]" "[a-z]"
+    echo "$*" |\
+        tr -c "[A-Z][a-z][0-9]" "_" |\
+        sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" |\
+        tr "[A-Z]" "[a-z]"
 }
 
 # convert any floating point number
@@ -241,57 +241,57 @@ fixid() {
 # so that no fork is necessary
 # the multiplier must be a power of 10
 float2int() {
-       local f m="$2" a b l v=($1)
-       f=${v[0]}
-
-       # echo >&2 "value='${1}' f='${f}', m='${m}'"
-
-       # the length of the multiplier - 1
-       l=$(( ${#m} - 1 ))
-
-       # check if the number is in scientific notation
-       if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]
-               then
-               # convert it to decimal
-               # unfortunately, this fork cannot be avoided
-               # if you know of a way to avoid it, please let me know
-               f=$(printf "%0.${l}f" ${f})
-       fi
-
-       # split the floating point number
-       # in integer (a) and decimal (b)
-       a=${f/.*/}
-       b=${f/*./}
-
-       # if the integer part is missing
-       # set it to zero
-       [ -z "${a}" ] && a="0"
-
-       # strip leading zeros from the integer part
-       # base 10 convertion
-       a=$((10#$a))
-
-       # check the length of the decimal part
-       # against the length of the multiplier
-       if [ ${#b} -gt ${l} ]
-               then
-               # too many digits - take the most significant
-               b=${b:0:${l}}
-
-       elif [ ${#b} -lt ${l} ]
-               then
-               # too few digits - pad with zero on the right
-               local z="00000000000000000000000" r=$((l - ${#b}))
-               b="${b}${z:0:${r}}"
-       fi
-
-       # strip leading zeros from the decimal part
-       # base 10 convertion
-       b=$((10#$b))
-
-       # store the result
-       FLOAT2INT_RESULT=$(( (a * m) + b ))
-       #echo >&2 "FLOAT2INT_RESULT='${FLOAT2INT_RESULT}'"
+    local f m="$2" a b l v=($1)
+    f=${v[0]}
+
+    # echo >&2 "value='${1}' f='${f}', m='${m}'"
+
+    # the length of the multiplier - 1
+    l=$(( ${#m} - 1 ))
+
+    # check if the number is in scientific notation
+    if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]
+        then
+        # convert it to decimal
+        # unfortunately, this fork cannot be avoided
+        # if you know of a way to avoid it, please let me know
+        f=$(printf "%0.${l}f" ${f})
+    fi
+
+    # split the floating point number
+    # in integer (a) and decimal (b)
+    a=${f/.*/}
+    b=${f/*./}
+
+    # if the integer part is missing
+    # set it to zero
+    [ -z "${a}" ] && a="0"
+
+    # strip leading zeros from the integer part
+    # base 10 convertion
+    a=$((10#$a))
+
+    # check the length of the decimal part
+    # against the length of the multiplier
+    if [ ${#b} -gt ${l} ]
+        then
+        # too many digits - take the most significant
+        b=${b:0:${l}}
+
+    elif [ ${#b} -lt ${l} ]
+        then
+        # too few digits - pad with zero on the right
+        local z="00000000000000000000000" r=$((l - ${#b}))
+        b="${b}${z:0:${r}}"
+    fi
+
+    # strip leading zeros from the decimal part
+    # base 10 convertion
+    b=$((10#$b))
+
+    # store the result
+    FLOAT2INT_RESULT=$(( (a * m) + b ))
+    #echo >&2 "FLOAT2INT_RESULT='${FLOAT2INT_RESULT}'"
 }
 
 
@@ -299,105 +299,105 @@ float2int() {
 # charts check functions
 
 all_charts() {
-       cd "$chartsd"
-       [ $? -ne 0 ] && echo >&2 "$PROGRAM_NAME: Cannot cd to $chartsd" && return 1
+    cd "$chartsd"
+    [ $? -ne 0 ] && echo >&2 "$PROGRAM_NAME: Cannot cd to $chartsd" && return 1
 
-       ls *.chart.sh | sed "s/\.chart\.sh$//g"
+    ls *.chart.sh | sed "s/\.chart\.sh$//g"
 }
 
 declare -A charts_enable_keyword=(
-             ['apache']="force"
-           ['cpu_apps']="force"
-            ['cpufreq']="force"
-            ['example']="force"
-               ['exim']="force"
-            ['hddtemp']="force"
-       ['load_average']="force"
-           ['mem_apps']="force"
-              ['mysql']="force"
-              ['nginx']="force"
-             ['phpfpm']="force"
-            ['postfix']="force"
-            ['sensors']="force"
-              ['squid']="force"
-             ['tomcat']="force"
-       )
+          ['apache']="force"
+        ['cpu_apps']="force"
+         ['cpufreq']="force"
+         ['example']="force"
+            ['exim']="force"
+         ['hddtemp']="force"
+    ['load_average']="force"
+        ['mem_apps']="force"
+           ['mysql']="force"
+           ['nginx']="force"
+          ['phpfpm']="force"
+         ['postfix']="force"
+         ['sensors']="force"
+           ['squid']="force"
+          ['tomcat']="force"
+    )
 
 all_enabled_charts() {
-       local charts= enabled= required=
-
-       # find all enabled charts
-
-       for chart in $( all_charts )
-       do
-               eval "enabled=\$$chart"
-               if [ -z "${enabled}" ]
-                       then
-                       enabled="${enable_all_charts}"
-               fi
-
-               required="${charts_enable_keyword[${chart}]}"
-               [ -z "${required}" ] && required="yes"
-
-               if [ ! "${enabled}" = "${required}" ]
-               then
-                       echo >&2 "$PROGRAM_NAME: '$chart' is NOT enabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)."
-               else
-                       [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' is enabled."
-                       local charts="$charts $chart"
-               fi
-       done
-
-       local charts2=
-       for chart in $charts
-       do
-               # check the enabled charts
-               local check="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()" )"
-               if [ -z "$check" ]
-               then
-                       echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
-                       continue
-               fi
-
-               local create="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()" )"
-               if [ -z "$create" ]
-               then
-                       echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
-                       continue
-               fi
-
-               local update="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()" )"
-               if [ -z "$update" ]
-               then
-                       echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
-                       continue
-               fi
-
-               # check its config
-               #if [ -f "$confd/$chart.conf" ]
-               #then
-               #       if [ ! -z "$( cat "$confd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ]
-               #       then
-               #               echo >&2 "$PROGRAM_NAME: chart's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
-               #               continue
-               #       fi
-               #fi
-
-               #if [ $dryrunner -eq 1 ]
-               #       then
-               #       "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$confd/$chart.conf" >/dev/null
-               #       if [ $? -ne 0 ]
-               #       then
-               #               echo >&2 "$PROGRAM_NAME: chart's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
-               #               continue
-               #       fi
-               #fi
-
-               local charts2="$charts2 $chart"
-       done
-
-       echo $charts2
-       [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: enabled charts: $charts2"
+    local charts= enabled= required=
+
+    # find all enabled charts
+
+    for chart in $( all_charts )
+    do
+        eval "enabled=\$$chart"
+        if [ -z "${enabled}" ]
+            then
+            enabled="${enable_all_charts}"
+        fi
+
+        required="${charts_enable_keyword[${chart}]}"
+        [ -z "${required}" ] && required="yes"
+
+        if [ ! "${enabled}" = "${required}" ]
+        then
+            echo >&2 "$PROGRAM_NAME: '$chart' is NOT enabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)."
+        else
+            [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' is enabled."
+            local charts="$charts $chart"
+        fi
+    done
+
+    local charts2=
+    for chart in $charts
+    do
+        # check the enabled charts
+        local check="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()" )"
+        if [ -z "$check" ]
+        then
+            echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
+            continue
+        fi
+
+        local create="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()" )"
+        if [ -z "$create" ]
+        then
+            echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
+            continue
+        fi
+
+        local update="$( cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()" )"
+        if [ -z "$update" ]
+        then
+            echo >&2 "$PROGRAM_NAME: chart '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
+            continue
+        fi
+
+        # check its config
+        #if [ -f "$confd/$chart.conf" ]
+        #then
+        #   if [ ! -z "$( cat "$confd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ]
+        #   then
+        #       echo >&2 "$PROGRAM_NAME: chart's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
+        #       continue
+        #   fi
+        #fi
+
+        #if [ $dryrunner -eq 1 ]
+        #   then
+        #   "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$confd/$chart.conf" >/dev/null
+        #   if [ $? -ne 0 ]
+        #   then
+        #       echo >&2 "$PROGRAM_NAME: chart's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
+        #       continue
+        #   fi
+        #fi
+
+        local charts2="$charts2 $chart"
+    done
+
+    echo $charts2
+    [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: enabled charts: $charts2"
 }
 
 
@@ -408,36 +408,36 @@ suffix_update_every="_update_every"
 active_charts=
 for chart in $( all_enabled_charts )
 do
-       [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart: '$chartsd/$chart.chart.sh'"
-       . "$chartsd/$chart.chart.sh"
-
-       if [ -f "$confd/charts.d/$chart.conf" ]
-       then
-               [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/charts.d/$chart.conf'"
-               . "$confd/charts.d/$chart.conf"
-       elif [ -f "$confd/$chart.conf" ]
-       then
-               [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/$chart.conf'"
-               . "$confd/$chart.conf"
-       else
-               echo >&2 "$PROGRAM_NAME: $chart: configuration file '$confd/charts.d/$chart.conf' not found. Using defaults."
-       fi
-
-       eval "dt=\$$chart$suffix_update_every"
-       dt=$(( dt + 1 - 1 )) # make sure it is a number
-       if [ $dt -lt $update_every ]
-       then
-               eval "$chart$suffix_update_every=$update_every"
-       fi
-
-       $chart$charts_check
-       if [ $? -eq 0 ]
-       then
-               [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' activated"
-               active_charts="$active_charts $chart"
-       else
-               echo >&2 "$PROGRAM_NAME: chart '$chart' check() function reports failure."
-       fi
+    [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart: '$chartsd/$chart.chart.sh'"
+    . "$chartsd/$chart.chart.sh"
+
+    if [ -f "$confd/charts.d/$chart.conf" ]
+    then
+        [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/charts.d/$chart.conf'"
+        . "$confd/charts.d/$chart.conf"
+    elif [ -f "$confd/$chart.conf" ]
+    then
+        [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: loading chart options: '$confd/$chart.conf'"
+        . "$confd/$chart.conf"
+    else
+        echo >&2 "$PROGRAM_NAME: $chart: configuration file '$confd/charts.d/$chart.conf' not found. Using defaults."
+    fi
+
+    eval "dt=\$$chart$suffix_update_every"
+    dt=$(( dt + 1 - 1 )) # make sure it is a number
+    if [ $dt -lt $update_every ]
+    then
+        eval "$chart$suffix_update_every=$update_every"
+    fi
+
+    $chart$charts_check
+    if [ $? -eq 0 ]
+    then
+        [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' activated"
+        active_charts="$active_charts $chart"
+    else
+        echo >&2 "$PROGRAM_NAME: chart '$chart' check() function reports failure."
+    fi
 done
 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts"
 
@@ -452,26 +452,26 @@ test $debug -eq 1 && debug_time=tellwork
 # if we only need a specific chart, remove all the others
 if [ ! -z "${chart_only}" ]
 then
-       [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: requested to run only for: '${chart_only}'"
-       check_charts=
-       for chart in $active_charts
-       do
-               if [ "$chart" = "$chart_only" ]
-               then
-                       check_charts="$chart"
-                       break
-               fi
-       done
-       active_charts="$check_charts"
+    [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: requested to run only for: '${chart_only}'"
+    check_charts=
+    for chart in $active_charts
+    do
+        if [ "$chart" = "$chart_only" ]
+        then
+            check_charts="$chart"
+            break
+        fi
+    done
+    active_charts="$check_charts"
 fi
 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: activated charts: $active_charts"
 
 # stop if we just need a pre-check
 if [ $check -eq 1 ]
 then
-       echo >&2 "CHECK RESULT"
-       echo >&2 "Will run the charts: $active_charts"
-       exit 0
+    echo >&2 "CHECK RESULT"
+    echo >&2 "Will run the charts: $active_charts"
+    exit 0
 fi
 
 # -----------------------------------------------------------------------------
@@ -479,13 +479,13 @@ fi
 
 TMP_DIR=
 chartsd_cleanup() {
-       cd /tmp
-       if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]
-       then
-               [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..."
-               rm -rf "$TMP_DIR"
-       fi
-       exit 0
+    cd /tmp
+    if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]
+    then
+        [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..."
+        rm -rf "$TMP_DIR"
+    fi
+    exit 0
 }
 trap chartsd_cleanup EXIT
 trap chartsd_cleanup SIGHUP
@@ -493,9 +493,9 @@ trap chartsd_cleanup INT
 
 if [ $UID = "0" ]
 then
-       TMP_DIR="$( mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX )"
+    TMP_DIR="$( mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX )"
 else
-       TMP_DIR="$( mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX )"
+    TMP_DIR="$( mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX )"
 fi
 
 cd "$TMP_DIR" || exit 1
@@ -506,15 +506,15 @@ cd "$TMP_DIR" || exit 1
 run_charts=
 for chart in $active_charts
 do
-       [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: Calling '$chart$charts_create()'..."
-       $chart$charts_create
-       if [ $? -eq 0 ]
-       then
-               run_charts="$run_charts $chart"
-               [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' has initialized."
-       else
-               echo >&2 "$PROGRAM_NAME: chart '$chart' function '$chart$charts_create()' reports failure."
-       fi
+    [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: Calling '$chart$charts_create()'..."
+    $chart$charts_create
+    if [ $? -eq 0 ]
+    then
+        run_charts="$run_charts $chart"
+        [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: '$chart' has initialized."
+    else
+        echo >&2 "$PROGRAM_NAME: chart '$chart' function '$chart$charts_create()' reports failure."
+    fi
 done
 [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: run_charts='$run_charts'"
 
@@ -523,131 +523,131 @@ done
 # update dimensions
 
 if [ -z "$run_charts" ]
-       then
-       echo >&2 "$PROGRAM_NAME: No charts to collect data from."
-       echo "DISABLE"
-       exit 1
+    then
+    echo >&2 "$PROGRAM_NAME: No charts to collect data from."
+    echo "DISABLE"
+    exit 1
 fi
 
 declare -A charts_last_update=() charts_update_every=() charts_next_update=() charts_run_counter=() charts_serial_failures=()
 global_update() {
-       local exit_at \
-               c=0 dt ret last_ms exec_start_ms exec_end_ms \
-               chart now_charts=() next_charts=($run_charts) \
-               next_ms x seconds millis
-
-       # return the current time in ms in $now_ms
-       current_time_ms
-
-       exit_at=$(( now_ms + (restart_timeout * 1000) ))
-
-       for chart in $run_charts
-       do
-               eval "charts_update_every[$chart]=\$$chart$suffix_update_every"
-               test -z "${charts_update_every[$chart]}" && charts_update_every[$charts]=$update_every
-               charts_last_update[$chart]=$((now_ms - (now_ms % (charts_update_every[$chart] * 1000) ) ))
-               charts_next_update[$chart]=$(( charts_last_update[$chart] + (charts_update_every[$chart] * 1000) ))
-               charts_run_counter[$chart]=0
-               charts_serial_failures[$chart]=0
-
-               echo "CHART netdata.plugin_chartsd_$chart '' 'Execution time for $chart plugin' 'milliseconds / run' charts.d netdata.plugin_charts area 145000 ${charts_update_every[$chart]}"
-               echo "DIMENSION run_time 'run time' absolute 1 1"
-       done
-
-       # the main loop
-       while [ "${#next_charts[@]}" -gt 0 ]
-       do
-               c=$((c + 1))
-               now_charts=("${next_charts[@]}")
-               next_charts=()
-
-               # return the current time in ms in $now_ms
-               current_time_ms
-
-               for chart in "${now_charts[@]}"
-               do
-                       # echo >&2 "DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}"
-                       if [ ${now_ms} -ge ${charts_next_update[$chart]} ]
-                       then
-                               last_ms=${charts_last_update[$chart]}
-                               dt=$(( (now_ms - last_ms) ))
-                               # echo >&2 "DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}, dt: ${dt}"
-
-                               charts_last_update[$chart]=${now_ms}
-
-                               while [ ${charts_next_update[$chart]} -lt ${now_ms} ]
-                               do
-                                       charts_next_update[$chart]=$(( charts_next_update[$chart] + (charts_update_every[$chart] * 1000) ))
-                               done
-
-                               # the first call should not give a duration
-                               # so that netdata calibrates to current time
-                               dt=$(( dt * 1000 ))
-                               charts_run_counter[$chart]=$(( charts_run_counter[$chart] + 1 ))
-                               if [ ${charts_run_counter[$chart]} -eq 1 ]
-                                       then
-                                       dt=
-                               fi
-
-                               exec_start_ms=$now_ms
-                               $chart$charts_update $dt
-                               ret=$?
-
-                               # return the current time in ms in $now_ms
-                               current_time_ms; exec_end_ms=$now_ms
-
-                               echo "BEGIN netdata.plugin_chartsd_$chart $dt"
-                               echo "SET run_time = $(( exec_end_ms - exec_start_ms ))"
-                               echo "END"
-
-                               if [ $ret -eq 0 ]
-                               then
-                                       charts_serial_failures[$chart]=0
-                                       next_charts+=($chart)
-                               else
-                                       charts_serial_failures[$chart]=$(( charts_serial_failures[$chart] + 1 ))
-
-                                       if [ ${charts_serial_failures[$chart]} -gt 10 ]
-                                               then
-                                               echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
-                                       else
-                                               echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reports failure. Will keep trying for a while."
-                                               next_charts+=($chart)
-                                       fi
-                               fi
-                       else
-                               next_charts+=($chart)
-                       fi
-               done
-
-               if [ "$pause_method" = "suspend" ]
-               then
-                       echo "STOPPING_WAKE_ME_UP_PLEASE"
-                       suspend || ( echo >&2 "$PROGRAM_NAME: suspend returned error $?, falling back to sleep."; loopsleepms $debug_time $update_every $time_divisor)
-               else
-                       # wait the time you are required to
-                       next_ms=$((now_ms + (update_every * 1000 * 100) ))
-                       for x in "${charts_next_update[@]}"; do [ ${x} -lt ${next_ms} ] && next_ms=${x}; done
-                       next_ms=$((next_ms - now_ms))
-
-                       if [ ${LOOPSLEEPMS_HIGHRES} -eq 1 -a ${next_ms} -gt 0 ]
-                               then
-                               seconds=$(( next_ms / 1000 ))
-                               millis=$(( next_ms % 1000 ))
-                               [ ${millis} -lt 10  ] && millis="0${millis}"
-                               [ ${millis} -lt 100 ] && millis="0${millis}"
-                               [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: sleeping for ${seconds}.${millis} seconds."
-                               sleep ${seconds}.${millis}
-                       else
-                               sleep $update_every
-                       fi
-               fi
-
-               test ${now_ms} -ge ${exit_at} && exit 0
-       done
-
-       echo >&2 "$PROGRAM_NAME: Nothing left to do. Disabling charts.d.plugin."
-       echo "DISABLE"
+    local exit_at \
+        c=0 dt ret last_ms exec_start_ms exec_end_ms \
+        chart now_charts=() next_charts=($run_charts) \
+        next_ms x seconds millis
+
+    # return the current time in ms in $now_ms
+    current_time_ms
+
+    exit_at=$(( now_ms + (restart_timeout * 1000) ))
+
+    for chart in $run_charts
+    do
+        eval "charts_update_every[$chart]=\$$chart$suffix_update_every"
+        test -z "${charts_update_every[$chart]}" && charts_update_every[$charts]=$update_every
+        charts_last_update[$chart]=$((now_ms - (now_ms % (charts_update_every[$chart] * 1000) ) ))
+        charts_next_update[$chart]=$(( charts_last_update[$chart] + (charts_update_every[$chart] * 1000) ))
+        charts_run_counter[$chart]=0
+        charts_serial_failures[$chart]=0
+
+        echo "CHART netdata.plugin_chartsd_$chart '' 'Execution time for $chart plugin' 'milliseconds / run' charts.d netdata.plugin_charts area 145000 ${charts_update_every[$chart]}"
+        echo "DIMENSION run_time 'run time' absolute 1 1"
+    done
+
+    # the main loop
+    while [ "${#next_charts[@]}" -gt 0 ]
+    do
+        c=$((c + 1))
+        now_charts=("${next_charts[@]}")
+        next_charts=()
+
+        # return the current time in ms in $now_ms
+        current_time_ms
+
+        for chart in "${now_charts[@]}"
+        do
+            # echo >&2 "DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}"
+            if [ ${now_ms} -ge ${charts_next_update[$chart]} ]
+            then
+                last_ms=${charts_last_update[$chart]}
+                dt=$(( (now_ms - last_ms) ))
+                # echo >&2 "DEBUG: chart: $chart last: ${charts_last_update[$chart]}, next: ${charts_next_update[$chart]}, now: ${now_ms}, dt: ${dt}"
+
+                charts_last_update[$chart]=${now_ms}
+
+                while [ ${charts_next_update[$chart]} -lt ${now_ms} ]
+                do
+                    charts_next_update[$chart]=$(( charts_next_update[$chart] + (charts_update_every[$chart] * 1000) ))
+                done
+
+                # the first call should not give a duration
+                # so that netdata calibrates to current time
+                dt=$(( dt * 1000 ))
+                charts_run_counter[$chart]=$(( charts_run_counter[$chart] + 1 ))
+                if [ ${charts_run_counter[$chart]} -eq 1 ]
+                    then
+                    dt=
+                fi
+
+                exec_start_ms=$now_ms
+                $chart$charts_update $dt
+                ret=$?
+
+                # return the current time in ms in $now_ms
+                current_time_ms; exec_end_ms=$now_ms
+
+                echo "BEGIN netdata.plugin_chartsd_$chart $dt"
+                echo "SET run_time = $(( exec_end_ms - exec_start_ms ))"
+                echo "END"
+
+                if [ $ret -eq 0 ]
+                then
+                    charts_serial_failures[$chart]=0
+                    next_charts+=($chart)
+                else
+                    charts_serial_failures[$chart]=$(( charts_serial_failures[$chart] + 1 ))
+
+                    if [ ${charts_serial_failures[$chart]} -gt 10 ]
+                        then
+                        echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
+                    else
+                        echo >&2 "$PROGRAM_NAME: chart '$chart' update() function reports failure. Will keep trying for a while."
+                        next_charts+=($chart)
+                    fi
+                fi
+            else
+                next_charts+=($chart)
+            fi
+        done
+
+        if [ "$pause_method" = "suspend" ]
+        then
+            echo "STOPPING_WAKE_ME_UP_PLEASE"
+            suspend || ( echo >&2 "$PROGRAM_NAME: suspend returned error $?, falling back to sleep."; loopsleepms $debug_time $update_every $time_divisor)
+        else
+            # wait the time you are required to
+            next_ms=$((now_ms + (update_every * 1000 * 100) ))
+            for x in "${charts_next_update[@]}"; do [ ${x} -lt ${next_ms} ] && next_ms=${x}; done
+            next_ms=$((next_ms - now_ms))
+
+            if [ ${LOOPSLEEPMS_HIGHRES} -eq 1 -a ${next_ms} -gt 0 ]
+                then
+                seconds=$(( next_ms / 1000 ))
+                millis=$(( next_ms % 1000 ))
+                [ ${millis} -lt 10  ] && millis="0${millis}"
+                [ ${millis} -lt 100 ] && millis="0${millis}"
+                [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: sleeping for ${seconds}.${millis} seconds."
+                sleep ${seconds}.${millis}
+            else
+                sleep $update_every
+            fi
+        fi
+
+        test ${now_ms} -ge ${exit_at} && exit 0
+    done
+
+    echo >&2 "$PROGRAM_NAME: Nothing left to do. Disabling charts.d.plugin."
+    echo "DISABLE"
 }
 
 global_update
index e98584e5d852d3643e5df7fbc3c4183f68bf5582..02ab694d26dd53af3a2981a9fc5a3c929201edd6 100644 (file)
@@ -7,9 +7,9 @@
 
 LOOPSLEEP_DATE="$(which date)"
 if [ -z "$LOOPSLEEP_DATE" ]
-       then
-       echo >&2 "$0: ERROR: Cannot find the command 'date' in the system path."
-       exit 1
+    then
+    echo >&2 "$0: ERROR: Cannot find the command 'date' in the system path."
+    exit 1
 fi
 
 LOOPSLEEPMS_LASTRUN=0
@@ -21,70 +21,70 @@ test "$($LOOPSLEEP_DATE +%N)" = "%N" && LOOPSLEEPMS_HIGHRES=0
 
 now_ms=
 current_time_ms() {
-       # if high resolution is not supported
-       # just sleep the time requested, in seconds
-       if [ $LOOPSLEEPMS_HIGHRES -eq 0 ]
-       then
-               now_ms="$($LOOPSLEEP_DATE +'%s')000"
-       else
-               now_ms="$(( $( $LOOPSLEEP_DATE +'%s * 1000 + %-N / 1000000' ) ))"
-       fi
+    # if high resolution is not supported
+    # just sleep the time requested, in seconds
+    if [ $LOOPSLEEPMS_HIGHRES -eq 0 ]
+    then
+        now_ms="$($LOOPSLEEP_DATE +'%s')000"
+    else
+        now_ms="$(( $( $LOOPSLEEP_DATE +'%s * 1000 + %-N / 1000000' ) ))"
+    fi
 }
 
 loopsleepms() {
-       local tellwork=0 t="$1" div s m now mstosleep
+    local tellwork=0 t="$1" div s m now mstosleep
 
-       if [ "$t" = "tellwork" ]
-       then
-               tellwork=1
-               shift
-               t="$1"
-       fi
-       div="${2-100}"
+    if [ "$t" = "tellwork" ]
+    then
+        tellwork=1
+        shift
+        t="$1"
+    fi
+    div="${2-100}"
 
-       # $t = the time in seconds to wait
+    # $t = the time in seconds to wait
 
-       # if high resolution is not supported
-       # just sleep the time requested, in seconds
-       if [ $LOOPSLEEPMS_HIGHRES -eq 0 ]
-       then
-               sleep $t
-               return
-       fi
+    # if high resolution is not supported
+    # just sleep the time requested, in seconds
+    if [ $LOOPSLEEPMS_HIGHRES -eq 0 ]
+    then
+        sleep $t
+        return
+    fi
 
-       # get the current time, in ms
-       # milliseconds since epoch (1-1-1970)
-       now="$(( $( $LOOPSLEEP_DATE +'%s * 1000 + %-N / 1000000' ) ))"
+    # get the current time, in ms
+    # milliseconds since epoch (1-1-1970)
+    now="$(( $( $LOOPSLEEP_DATE +'%s * 1000 + %-N / 1000000' ) ))"
 
-       # calculate required sleep in ms
-       t=$((t * 1000 * div / 100))
+    # calculate required sleep in ms
+    t=$((t * 1000 * div / 100))
 
-       # this is our first run
-       # just wait the requested time
-       test $LOOPSLEEPMS_LASTRUN -eq 0 && LOOPSLEEPMS_LASTRUN=$now
+    # this is our first run
+    # just wait the requested time
+    test $LOOPSLEEPMS_LASTRUN -eq 0 && LOOPSLEEPMS_LASTRUN=$now
 
-       # calculate ms since last run
-       LOOPSLEEPMS_LASTWORK=$((now - LOOPSLEEPMS_LASTRUN - LOOPSLEEPMS_LASTSLEEP))
-       # echo "# last loop's work took $LOOPSLEEPMS_LASTWORK ms"
+    # calculate ms since last run
+    LOOPSLEEPMS_LASTWORK=$((now - LOOPSLEEPMS_LASTRUN - LOOPSLEEPMS_LASTSLEEP))
+    # echo "# last loop's work took $LOOPSLEEPMS_LASTWORK ms"
 
-       # calculate ms to sleep
-       mstosleep=$(( t - LOOPSLEEPMS_LASTWORK ))
-       # echo "# mstosleep is $mstosleep ms"
+    # calculate ms to sleep
+    mstosleep=$(( t - LOOPSLEEPMS_LASTWORK ))
+    # echo "# mstosleep is $mstosleep ms"
 
-       # if we are too slow, sleep some time
-       test $mstosleep -lt 200 && mstosleep=200
+    # if we are too slow, sleep some time
+    test $mstosleep -lt 200 && mstosleep=200
 
-       s=$((mstosleep / 1000))
-       m=$((mstosleep - (s * 1000)))
+    s=$((mstosleep / 1000))
+    m=$((mstosleep - (s * 1000)))
 
-       test $tellwork -eq 1 && echo >&2 " >>> PERFORMANCE >>> WORK TOOK $LOOPSLEEPMS_LASTWORK ms ( $((LOOPSLEEPMS_LASTWORK * 100 / 1000)).$((LOOPSLEEPMS_LASTWORK % 10))% cpu ) >>> SLEEPING $mstosleep ms"
+    test $tellwork -eq 1 && echo >&2 " >>> PERFORMANCE >>> WORK TOOK $LOOPSLEEPMS_LASTWORK ms ( $((LOOPSLEEPMS_LASTWORK * 100 / 1000)).$((LOOPSLEEPMS_LASTWORK % 10))% cpu ) >>> SLEEPING $mstosleep ms"
 
-       # echo "# sleeping $s.$m"
-       # echo
-       sleep $s.$m
+    # echo "# sleeping $s.$m"
+    # echo
+    sleep $s.$m
 
-       # keep the values we need
-       # for our next run
-       LOOPSLEEPMS_LASTRUN=$now
-       LOOPSLEEPMS_LASTSLEEP=$mstosleep
+    # keep the values we need
+    # for our next run
+    LOOPSLEEPMS_LASTRUN=$now
+    LOOPSLEEPMS_LASTSLEEP=$mstosleep
 }
index 270351e4e5582cada1876edd54eb71d481d0c71b..21b04384e8ca87607e36cd747e9966d59351c3fa 100755 (executable)
@@ -40,60 +40,60 @@ var netdata = require('netdata');
 // configuration
 
 function pluginConfig(filename) {
-       var f = path.basename(filename);
+    var f = path.basename(filename);
 
-       // node.d.plugin configuration
-       var m = f.match('.plugin' + '$');
-       if(m !== null)
-               return netdata.options.paths.config + '/' + f.substring(0, m.index) + '.conf';
+    // node.d.plugin configuration
+    var m = f.match('.plugin' + '$');
+    if(m !== null)
+        return netdata.options.paths.config + '/' + f.substring(0, m.index) + '.conf';
 
-       // node.d modules configuration
-       m = f.match('.node.js' + '$');
-       if(m !== null)
-               return netdata.options.paths.config + '/node.d/' + f.substring(0, m.index) + '.conf';
+    // node.d modules configuration
+    m = f.match('.node.js' + '$');
+    if(m !== null)
+        return netdata.options.paths.config + '/node.d/' + f.substring(0, m.index) + '.conf';
 
-       return netdata.options.paths.config + '/node.d/' + f + '.conf';
+    return netdata.options.paths.config + '/node.d/' + f + '.conf';
 }
 
 // internal defaults
 extend(true, netdata.options, {
-       filename: path.basename(__filename),
+    filename: path.basename(__filename),
 
-       update_every: NETDATA_UPDATE_EVERY,
+    update_every: NETDATA_UPDATE_EVERY,
 
-       exit_after_ms: 3600 * 4 * 1000,
+    exit_after_ms: 3600 * 4 * 1000,
 
-       paths: {
-               plugins: NETDATA_PLUGINS_DIR,
-               config: NETDATA_CONFIG_DIR,
-               modules: [],
-       },
+    paths: {
+        plugins: NETDATA_PLUGINS_DIR,
+        config: NETDATA_CONFIG_DIR,
+        modules: [],
+    },
 
-       modules_enable_autodetect: true,
-       modules_enable_all: true,
-       modules: {},
+    modules_enable_autodetect: true,
+    modules_enable_all: true,
+    modules: {},
 });
 netdata.options.config_filename = pluginConfig(__filename);
 
 // load configuration file
 try {
-       netdata.options_loaded = JSON.parse(fs.readFileSync(netdata.options.config_filename, 'utf8'));
-       extend(true, netdata.options, netdata.options_loaded);
-       console.error('merged netdata object:');
-       console.error(netdata);
+    netdata.options_loaded = JSON.parse(fs.readFileSync(netdata.options.config_filename, 'utf8'));
+    extend(true, netdata.options, netdata.options_loaded);
+    console.error('merged netdata object:');
+    console.error(netdata);
 }
 catch(e) {
-       netdata.error('Cannot read configuration file ' + netdata.options.config_filename + ': ' + e.message + ', using internal defaults.');
-       netdata.options_loaded = undefined;
-       dumpError(e);
+    netdata.error('Cannot read configuration file ' + netdata.options.config_filename + ': ' + e.message + ', using internal defaults.');
+    netdata.options_loaded = undefined;
+    dumpError(e);
 }
 
 
 // apply module paths to node.js process
 function applyModulePaths() {
-       var len = netdata.options.paths.modules.length;
-       while(len--)
-               process.mainModule.paths.unshift(netdata.options.paths.modules[len]);
+    var len = netdata.options.paths.modules.length;
+    while(len--)
+        process.mainModule.paths.unshift(netdata.options.paths.modules[len]);
 }
 applyModulePaths();
 
@@ -102,165 +102,165 @@ applyModulePaths();
 // tracing
 
 function dumpError(err) {
-       if (typeof err === 'object') {
-               if (err.stack) {
-                       netdata.debug(err.stack);
-               }
-       }
+    if (typeof err === 'object') {
+        if (err.stack) {
+            netdata.debug(err.stack);
+        }
+    }
 }
 
 // --------------------------------------------------------------------------------------------------------------------
 // get command line arguments
 {
-       var found_myself = false;
-       var found_number = false;
-       var found_modules = false;
-       process.argv.forEach(function (val, index, array) {
-               netdata.debug('PARAM: ' + val);
-
-               if(!found_myself) {
-                       if(val === __filename)
-                               found_myself = true;
-               }
-               else {
-                       switch(val) {
-                               case 'debug':
-                                       netdata.options.DEBUG = true;
-                                       netdata.debug('DEBUG enabled');
-                                       break;
-
-                               default:
-                                       if(found_number === true) {
-                                               if(found_modules === false) {
-                                                       for(var i in netdata.options.modules)
-                                                               netdata.options.modules[i].enabled = false;
-                                               }
-
-                                               if(typeof netdata.options.modules[val] === 'undefined')
-                                                       netdata.options.modules[val] = {};
-
-                                               netdata.options.modules[val].enabled = true;
-                                               netdata.options.modules_enable_all = false;
-                                               netdata.debug('enabled module ' + val);
-                                       }
-                                       else {
-                                               try {
-                                                       var x = parseInt(val);
-                                                       if(x > 0) {
-                                                               netdata.options.update_every = x;
-                                                               if(netdata.options.update_every < NETDATA_UPDATE_EVERY) {
-                                                                       netdata.options.update_every = NETDATA_UPDATE_EVERY;
-                                                                       netdata.debug('Update frequency ' + x + 's is too low');
-                                                               }
-
-                                                               found_number = true;
-                                                               netdata.debug('Update frequency set to ' + netdata.options.update_every + ' seconds');
-                                                       }
-                                                       else netdata.error('Ignoring parameter: ' + val);
-                                               }
-                                               catch(e) {
-                                                       netdata.error('Cannot get value of parameter: ' + val);
-                                                       dumpError(e);
-                                               }
-                                       }
-                                       break;
-                       }
-               }
-       });
+    var found_myself = false;
+    var found_number = false;
+    var found_modules = false;
+    process.argv.forEach(function (val, index, array) {
+        netdata.debug('PARAM: ' + val);
+
+        if(!found_myself) {
+            if(val === __filename)
+                found_myself = true;
+        }
+        else {
+            switch(val) {
+                case 'debug':
+                    netdata.options.DEBUG = true;
+                    netdata.debug('DEBUG enabled');
+                    break;
+
+                default:
+                    if(found_number === true) {
+                        if(found_modules === false) {
+                            for(var i in netdata.options.modules)
+                                netdata.options.modules[i].enabled = false;
+                        }
+
+                        if(typeof netdata.options.modules[val] === 'undefined')
+                            netdata.options.modules[val] = {};
+
+                        netdata.options.modules[val].enabled = true;
+                        netdata.options.modules_enable_all = false;
+                        netdata.debug('enabled module ' + val);
+                    }
+                    else {
+                        try {
+                            var x = parseInt(val);
+                            if(x > 0) {
+                                netdata.options.update_every = x;
+                                if(netdata.options.update_every < NETDATA_UPDATE_EVERY) {
+                                    netdata.options.update_every = NETDATA_UPDATE_EVERY;
+                                    netdata.debug('Update frequency ' + x + 's is too low');
+                                }
+
+                                found_number = true;
+                                netdata.debug('Update frequency set to ' + netdata.options.update_every + ' seconds');
+                            }
+                            else netdata.error('Ignoring parameter: ' + val);
+                        }
+                        catch(e) {
+                            netdata.error('Cannot get value of parameter: ' + val);
+                            dumpError(e);
+                        }
+                    }
+                    break;
+            }
+        }
+    });
 }
 
 if(netdata.options.update_every < 1) {
-       netdata.debug('Adjusting update frequency to 1 second');
-       netdata.options.update_every = 1;
+    netdata.debug('Adjusting update frequency to 1 second');
+    netdata.options.update_every = 1;
 }
 
 // --------------------------------------------------------------------------------------------------------------------
 // find modules
 
 function findModules() {
-       var found = 0;
-
-       var files = fs.readdirSync(NODE_D_DIR);
-       var len = files.length;
-       while(len--) {
-               var m = files[len].match('.node.js' + '$');
-               if(m !== null) {
-                       var n = files[len].substring(0, m.index);
-
-                       if(typeof(netdata.options.modules[n]) === 'undefined')
-                               netdata.options.modules[n] = { name: n, enabled: netdata.options.modules_enable_all };
-
-                       if(netdata.options.modules[n].enabled === true) {
-                               netdata.options.modules[n].name = n;
-                               netdata.options.modules[n].filename = NODE_D_DIR + '/' + files[len];
-                               netdata.options.modules[n].loaded = false;
-
-                               if(typeof(netdata.options.modules[n].config_filename) !== 'string')
-                                       netdata.options.modules[n].config_filename = pluginConfig(files[len]);
-
-                               // load the module
-                               try {
-                                       netdata.debug('loading module ' + netdata.options.modules[n].filename);
-                                       netdata.options.modules[n].module = require(netdata.options.modules[n].filename);
-                                       netdata.options.modules[n].module.name = n;
-                                       netdata.debug('loaded module ' + netdata.options.modules[n].name + ' from ' + netdata.options.modules[n].filename);
-                               }
-                               catch(e) {
-                                       netdata.options.modules[n].enabled = false;
-                                       netdata.error('Cannot load module: ' + netdata.options.modules[n].filename + ' exception: ' + e);
-                                       dumpError(e);
-                                       continue;
-                               }
-
-                               // load its configuration
-                               var c = {
-                                       enable_autodetect: netdata.options.modules_enable_autodetect,
-                                       update_every: netdata.options.update_every
-                               };
-                               try {
-                                       netdata.debug('loading module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
-                                       var c2 = JSON.parse(fs.readFileSync(netdata.options.modules[n].config_filename, 'utf8'));
-                                       extend(true, c, c2);
-                                       netdata.debug('loaded module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
-                               }
-                               catch(e) {
-                                       netdata.error('Cannot load module\'s ' + netdata.options.modules[n].name + ' config from ' + netdata.options.modules[n].config_filename + ' exception: ' + e + ', using internal defaults.');
-                                       dumpError(e);
-                               }
-
-                               // call module auto-detection / configuration
-                               try {
-                                       netdata.modules_configuring++;
-                                       netdata.debug('Configuring module ' + netdata.options.modules[n].name);
-                                       var serv = netdata.configure(netdata.options.modules[n].module, c, function() {
-                                               netdata.debug('Configured module ' + netdata.options.modules[n].name);
-                                               netdata.modules_configuring--;
-                                       });
-
-                                       netdata.debug('Configuring module ' + netdata.options.modules[n].name + ' reports ' + serv + ' eligible services.');
-                               }
-                               catch(e) {
-                                       netdata.modules_configuring--;
-                                       netdata.options.modules[n].enabled = false;
-                                       netdata.error('Failed module auto-detection: ' + netdata.options.modules[n].name + ' exception: ' + e + ', disabling module.');
-                                       dumpError(e);
-                                       continue;
-                               }
-
-                               netdata.options.modules[n].loaded = true;
-                               found++;
-                       }
-               }
-       }
-
-       // netdata.debug(netdata.options.modules);
-       return found;
+    var found = 0;
+
+    var files = fs.readdirSync(NODE_D_DIR);
+    var len = files.length;
+    while(len--) {
+        var m = files[len].match('.node.js' + '$');
+        if(m !== null) {
+            var n = files[len].substring(0, m.index);
+
+            if(typeof(netdata.options.modules[n]) === 'undefined')
+                netdata.options.modules[n] = { name: n, enabled: netdata.options.modules_enable_all };
+
+            if(netdata.options.modules[n].enabled === true) {
+                netdata.options.modules[n].name = n;
+                netdata.options.modules[n].filename = NODE_D_DIR + '/' + files[len];
+                netdata.options.modules[n].loaded = false;
+
+                if(typeof(netdata.options.modules[n].config_filename) !== 'string')
+                    netdata.options.modules[n].config_filename = pluginConfig(files[len]);
+
+                // load the module
+                try {
+                    netdata.debug('loading module ' + netdata.options.modules[n].filename);
+                    netdata.options.modules[n].module = require(netdata.options.modules[n].filename);
+                    netdata.options.modules[n].module.name = n;
+                    netdata.debug('loaded module ' + netdata.options.modules[n].name + ' from ' + netdata.options.modules[n].filename);
+                }
+                catch(e) {
+                    netdata.options.modules[n].enabled = false;
+                    netdata.error('Cannot load module: ' + netdata.options.modules[n].filename + ' exception: ' + e);
+                    dumpError(e);
+                    continue;
+                }
+
+                // load its configuration
+                var c = {
+                    enable_autodetect: netdata.options.modules_enable_autodetect,
+                    update_every: netdata.options.update_every
+                };
+                try {
+                    netdata.debug('loading module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
+                    var c2 = JSON.parse(fs.readFileSync(netdata.options.modules[n].config_filename, 'utf8'));
+                    extend(true, c, c2);
+                    netdata.debug('loaded module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
+                }
+                catch(e) {
+                    netdata.error('Cannot load module\'s ' + netdata.options.modules[n].name + ' config from ' + netdata.options.modules[n].config_filename + ' exception: ' + e + ', using internal defaults.');
+                    dumpError(e);
+                }
+
+                // call module auto-detection / configuration
+                try {
+                    netdata.modules_configuring++;
+                    netdata.debug('Configuring module ' + netdata.options.modules[n].name);
+                    var serv = netdata.configure(netdata.options.modules[n].module, c, function() {
+                        netdata.debug('Configured module ' + netdata.options.modules[n].name);
+                        netdata.modules_configuring--;
+                    });
+
+                    netdata.debug('Configuring module ' + netdata.options.modules[n].name + ' reports ' + serv + ' eligible services.');
+                }
+                catch(e) {
+                    netdata.modules_configuring--;
+                    netdata.options.modules[n].enabled = false;
+                    netdata.error('Failed module auto-detection: ' + netdata.options.modules[n].name + ' exception: ' + e + ', disabling module.');
+                    dumpError(e);
+                    continue;
+                }
+
+                netdata.options.modules[n].loaded = true;
+                found++;
+            }
+        }
+    }
+
+    // netdata.debug(netdata.options.modules);
+    return found;
 }
 
 if(findModules() === 0) {
-       netdata.error('Cannot load any .node.js module from: ' + NODE_D_DIR);
-       netdata.disableNodePlugin();
-       process.exit(1);
+    netdata.error('Cannot load any .node.js module from: ' + NODE_D_DIR);
+    netdata.disableNodePlugin();
+    process.exit(1);
 }
 
 
@@ -268,14 +268,14 @@ if(findModules() === 0) {
 // start
 
 function start_when_configuring_ends() {
-       if(netdata.modules_configuring > 0) {
-               netdata.debug('Waiting modules configuration, still running ' + netdata.modules_configuring);
-               setTimeout(start_when_configuring_ends, 500);
-               return;
-       }
-
-       netdata.modules_configuring = 0;
-       netdata.start();
+    if(netdata.modules_configuring > 0) {
+        netdata.debug('Waiting modules configuration, still running ' + netdata.modules_configuring);
+        setTimeout(start_when_configuring_ends, 500);
+        return;
+    }
+
+    netdata.modules_configuring = 0;
+    netdata.start();
 }
 start_when_configuring_ends();
 
index a0edadac34605027fd90fd702b2cca4e7125810a..bff5217d28a449e81d96ec0fb55e34a14631045d 100755 (executable)
@@ -23,21 +23,21 @@ update_every=$((t))
 
 # allow the user to override our defaults
 if [ -f "${config_dir}/tc-qos-helper.conf" ]
-       then
-       source "${config_dir}/tc-qos-helper.conf"
+    then
+    source "${config_dir}/tc-qos-helper.conf"
 fi
 
 # default time function
 now_ms=
 current_time_ms() {
-       now_ms="$(date +'%s')000"
+    now_ms="$(date +'%s')000"
 }
 
 # default sleep function
 LOOPSLEEPMS_LASTWORK=0
 loopsleepms() {
-       [ "$1" = "tellwork" ] && shift
-       sleep $1
+    [ "$1" = "tellwork" ] && shift
+    sleep $1
 }
 
 # if found and included, this file overwrites loopsleepms()
@@ -45,49 +45,49 @@ loopsleepms() {
 . "${plugins_dir}/loopsleepms.sh.inc"
 
 if [ -z "${tc}" -o ! -x "${tc}" ]
-       then
-       echo >&2 "${PROGRAM_NAME}: Cannot find command 'tc' in this system."
-       exit 1
+    then
+    echo >&2 "${PROGRAM_NAME}: Cannot find command 'tc' in this system."
+    exit 1
 fi
 
 devices=
 fix_names=
 
 setclassname() {
-       echo "SETCLASSNAME $3 $2"
+    echo "SETCLASSNAME $3 $2"
 }
 
 show_tc() {
-       local x="${1}" interface_dev interface_classes interface_classes_monitor
-
-       echo "BEGIN ${x}"
-       ${tc} -s class show dev ${x}
-
-       # check FireQOS names for classes
-       if [ ! -z "${fix_names}" -a -f "${fireqos_run_dir}/ifaces/${x}" ]
-       then
-               name="$(<"${fireqos_run_dir}/ifaces/${x}")"
-               echo "SETDEVICENAME ${name}"
-
-               interface_dev=
-               interface_classes=
-               interface_classes_monitor=
-               source "${fireqos_run_dir}/${name}.conf"
-               for n in ${interface_classes_monitor}
-               do
-                       setclassname ${n//|/ }
-               done
-               [ ! -z "${interface_dev}" ] && echo "SETDEVICEGROUP ${interface_dev}"
-       fi
-       echo "END ${x}"
+    local x="${1}" interface_dev interface_classes interface_classes_monitor
+
+    echo "BEGIN ${x}"
+    ${tc} -s class show dev ${x}
+
+    # check FireQOS names for classes
+    if [ ! -z "${fix_names}" -a -f "${fireqos_run_dir}/ifaces/${x}" ]
+    then
+        name="$(<"${fireqos_run_dir}/ifaces/${x}")"
+        echo "SETDEVICENAME ${name}"
+
+        interface_dev=
+        interface_classes=
+        interface_classes_monitor=
+        source "${fireqos_run_dir}/${name}.conf"
+        for n in ${interface_classes_monitor}
+        do
+            setclassname ${n//|/ }
+        done
+        [ ! -z "${interface_dev}" ] && echo "SETDEVICEGROUP ${interface_dev}"
+    fi
+    echo "END ${x}"
 }
 
 all_devices() {
-       cat /proc/net/dev | grep ":" | cut -d ':' -f 1 | while read dev
-       do
-               l=$(${tc} class show dev ${dev} | wc -l)
-               [ $l -ne 0 ] && echo ${dev}
-       done
+    cat /proc/net/dev | grep ":" | cut -d ':' -f 1 | while read dev
+    do
+        l=$(${tc} class show dev ${dev} | wc -l)
+        [ $l -ne 0 ] && echo ${dev}
+    done
 }
 
 # update devices and class names
@@ -102,25 +102,25 @@ c=0
 gc=0
 while [ 1 ]
 do
-       fix_names=
-       c=$((c + 1))
-       gc=$((gc + 1))
+    fix_names=
+    c=$((c + 1))
+    gc=$((gc + 1))
 
-       if [ ${c} -le 1 -o ${c} -ge ${names_every} ]
-       then
-               c=1
-               fix_names="YES"
-               devices="$( all_devices )"
-       fi
+    if [ ${c} -le 1 -o ${c} -ge ${names_every} ]
+    then
+        c=1
+        fix_names="YES"
+        devices="$( all_devices )"
+    fi
 
-       for d in ${devices}
-       do
-               show_tc ${d}
-       done
+    for d in ${devices}
+    do
+        show_tc ${d}
+    done
 
-       echo "WORKTIME ${LOOPSLEEPMS_LASTWORK}"
+    echo "WORKTIME ${LOOPSLEEPMS_LASTWORK}"
 
-       loopsleepms ${update_every}
+    loopsleepms ${update_every}
 
-       [ ${gc} -gt ${exit_after} ] && exit 0
+    [ ${gc} -gt ${exit_after} ] && exit 0
 done
index 846e3c61ac7427c333aabb32f061b5db273dd176..8ec3ae0318b4b266f1dad18fc250af9058062da6 100644 (file)
@@ -7,13 +7,6 @@
  *
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-
-#include "dictionary.h"
-#include "main.h"
-#include "log.h"
 #include "common.h"
 
 struct myvalue {
index 68475eae09427b05d322dfe02aa86ac02c9e0f26..fd771a7a78e53f56d16db9af2e6a81370e5343fb 100755 (executable)
@@ -1,13 +1,15 @@
 
 /*
  * compile with
- *  gcc -O1 -ggdb -Wall -Wextra -I ../src/ -I ../ -o benchmark-registry benchmark-registry.c ../src/dictionary.o ../src/log.o ../src/avl.o ../src/common.o ../src/appconfig.o ../src/web_buffer.o ../src/storage_number.o ../src/rrd.o -pthread -luuid -lm -DHAVE_CONFIG_H -DVARLIB_DIR="\"/tmp\""
+ *  gcc -O1 -ggdb -Wall -Wextra -I ../src/ -I ../ -o benchmark-registry benchmark-registry.c ../src/dictionary.o ../src/log.o ../src/avl.o ../src/common.o ../src/appconfig.o ../src/web_buffer.o ../src/storage_number.o ../src/rrd.o ../src/health.o -pthread -luuid -lm -DHAVE_CONFIG_H -DVARLIB_DIR="\"/tmp\""
  */
 
 char *hostname = "me";
 
 #include "../src/registry.c"
 
+void netdata_cleanup_and_exit(int ret) { exit(ret); }
+
 // ----------------------------------------------------------------------------
 // TESTS
 
diff --git a/profile/test-eval.c b/profile/test-eval.c
new file mode 100644 (file)
index 0000000..ad521b7
--- /dev/null
@@ -0,0 +1,297 @@
+
+/*
+ * 1. build netdata (as normally)
+ * 2. cd profile/
+ * 3. compile with:
+ *    gcc -O1 -ggdb -Wall -Wextra -I ../src/ -I ../ -o test-eval test-eval.c ../src/log.o ../src/eval.o ../src/common.o ../src/web_buffer.o ../src/storage_number.o -pthread -lm
+ *
+ */
+
+#include "common.h"
+
+void netdata_cleanup_and_exit(int ret) { exit(ret); }
+
+/*
+void indent(int level, int show) {
+       int i = level;
+       while(i--) printf(" |  ");
+       if(show) printf(" \\_ ");
+       else printf(" \\_  ");
+}
+
+void print_node(EVAL_NODE *op, int level);
+
+void print_value(EVAL_VALUE *v, int level) {
+       indent(level, 0);
+
+       switch(v->type) {
+               case EVAL_VALUE_INVALID:
+                       printf("value (NOP)\n");
+                       break;
+
+               case EVAL_VALUE_NUMBER:
+                       printf("value %Lf (NUMBER)\n", v->number);
+                       break;
+
+               case EVAL_VALUE_EXPRESSION:
+                       printf("value (SUB-EXPRESSION)\n");
+                       print_node(v->expression, level+1);
+                       break;
+
+               default:
+                       printf("value (INVALID type %d)\n", v->type);
+                       break;
+
+       }
+}
+
+void print_node(EVAL_NODE *op, int level) {
+
+//     if(op->operator != EVAL_OPERATOR_NOP) {
+               indent(level, 1);
+               if(op->operator) printf("%c (node %d, precedence: %d)\n", op->operator, op->id, op->precedence);
+               else printf("NOP (node %d, precedence: %d)\n", op->id, op->precedence);
+//     }
+
+       int i = op->count;
+       while(i--) print_value(&op->ops[i], level + 1);
+}
+
+calculated_number evaluate(EVAL_NODE *op, int depth);
+
+calculated_number evaluate_value(EVAL_VALUE *v, int depth) {
+       switch(v->type) {
+               case EVAL_VALUE_NUMBER:
+                       return v->number;
+
+               case EVAL_VALUE_EXPRESSION:
+                       return evaluate(v->expression, depth);
+
+               default:
+                       fatal("I don't know how to handle EVAL_VALUE type %d", v->type);
+       }
+}
+
+void print_depth(int depth) {
+       static int count = 0;
+
+       printf("%d. ", ++count);
+       while(depth--) printf("    ");
+}
+
+calculated_number evaluate(EVAL_NODE *op, int depth) {
+       calculated_number n1, n2, r;
+
+       switch(op->operator) {
+               case EVAL_OPERATOR_SIGN_PLUS:
+                       r = evaluate_value(&op->ops[0], depth);
+                       break;
+
+               case EVAL_OPERATOR_SIGN_MINUS:
+                       r = -evaluate_value(&op->ops[0], depth);
+                       break;
+
+               case EVAL_OPERATOR_PLUS:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 + n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf + %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_MINUS:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 - n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf - %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_MULTIPLY:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 * n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf * %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_DIVIDE:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 / n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf / %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_NOT:
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       r = !n1;
+                       print_depth(depth);
+                       printf("%Lf = NOT %Lf\n", r, n1);
+                       break;
+
+               case EVAL_OPERATOR_AND:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 && n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf AND %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_OR:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 || n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf OR %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_GREATER_THAN_OR_EQUAL:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 >= n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf >= %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_LESS_THAN_OR_EQUAL:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 <= n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf <= %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_GREATER:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 > n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf > %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_LESS:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 < n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf < %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_NOT_EQUAL:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 != n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf <> %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_EQUAL:
+                       if(op->count != 2)
+                               fatal("Operator '%c' requires 2 values, but we have %d", op->operator, op->count);
+                       n1 = evaluate_value(&op->ops[0], depth);
+                       n2 = evaluate_value(&op->ops[1], depth);
+                       r = n1 == n2;
+                       print_depth(depth);
+                       printf("%Lf = %Lf == %Lf\n", r, n1, n2);
+                       break;
+
+               case EVAL_OPERATOR_EXPRESSION_OPEN:
+                       printf("BEGIN SUB-EXPRESSION\n");
+                       r = evaluate_value(&op->ops[0], depth + 1);
+                       printf("END SUB-EXPRESSION\n");
+                       break;
+
+               case EVAL_OPERATOR_NOP:
+               case EVAL_OPERATOR_VALUE:
+                       r = evaluate_value(&op->ops[0], depth);
+                       break;
+
+               default:
+                       error("I don't know how to handle operator '%c'", op->operator);
+                       r = 0;
+                       break;
+       }
+
+       return r;
+}
+
+
+void print_expression(EVAL_NODE *op, const char *failed_at, int error) {
+       if(op) {
+               printf("expression tree:\n");
+               print_node(op, 0);
+
+               printf("\nevaluation steps:\n");
+               evaluate(op, 0);
+               
+               int error;
+               calculated_number ret = expression_evaluate(op, &error);
+               printf("\ninternal evaluator:\nSTATUS: %d, RESULT = %Lf\n", error, ret);
+
+               expression_free(op);
+       }
+       else {
+               printf("error: %d, failed_at: '%s'\n", error, (failed_at)?failed_at:"<NONE>");
+       }
+}
+*/
+
+int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
+       (void)variable;
+       (void)hash;
+       (void)rc;
+       (void)result;
+
+       return 0;
+}
+
+int main(int argc, char **argv) {
+       if(argc != 2) {
+               fprintf(stderr, "I need an epxression (enclose it in single-quotes (') as a single parameter)\n");
+               exit(1);
+       }
+
+       const char *failed_at = NULL;
+       int error;
+
+       EVAL_EXPRESSION *exp = expression_parse(argv[1], &failed_at, &error);
+       if(!exp)
+               printf("\nPARSING FAILED\nExpression: '%s'\nParsing stopped at: '%s'\nParsing error code: %d (%s)\n", argv[1], (failed_at)?((*failed_at)?failed_at:"<END OF EXPRESSION>"):"<NONE>", error, expression_strerror(error));
+       
+       else {
+               printf("\nPARSING OK\nExpression: '%s'\nParsed as : '%s'\nParsing error code: %d (%s)\n", argv[1], exp->parsed_as, error, expression_strerror(error));
+
+               if(expression_evaluate(exp)) {
+                       printf("\nEvaluates to: %Lf\n\n", exp->result);
+               }
+               else {
+                       printf("\nEvaluation failed with code %d and message: %s\n\n", exp->error, buffer_tostring(exp->error_msg));
+               }
+               expression_free(exp);
+       }
+
+       return 0;
+}
index 664be8b0bcbd8363d250a3c252e74623f1d4bc36..8fa6d5bdf71a4d9b870a1a60323e686cbebcf92f 100644 (file)
@@ -32,7 +32,9 @@ netdata_SOURCES = \
        common.c common.h \
        daemon.c daemon.h \
        dictionary.c dictionary.h \
+       eval.c eval.h \
        global_statistics.c global_statistics.h \
+       health.c health.h \
        log.c log.h \
        main.c main.h \
        plugin_checks.c plugin_checks.h \
index 748c6eff179a10ec1bb85d6afbb266c9073c43d1..991b1c72e982c99b9d1ff16067b99f76566a0a0d 100644 (file)
@@ -1,22 +1,4 @@
-
-/*
- * TODO
- *
- * 1. Re-write this using DICTIONARY
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "avl.h"
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
 
 #define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
 
@@ -31,33 +13,33 @@ pthread_mutex_t config_mutex = PTHREAD_MUTEX_INITIALIZER;
 #define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
 
 struct config_value {
-       avl avl;                                // the index - this has to be first!
+    avl avl;                // the index - this has to be first!
 
-       uint8_t flags;
-       uint32_t hash;                  // a simple hash to speed up searching
-                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
+    uint8_t flags;
+    uint32_t hash;          // a simple hash to speed up searching
+                            // we first compare hashes, and only if the hashes are equal we do string comparisons
 
-       char *name;
-       char *value;
+    char *name;
+    char *value;
 
-       struct config_value *next; // config->mutex protects just this
+    struct config_value *next; // config->mutex protects just this
 };
 
 struct config {
-       avl avl;
+    avl avl;
 
-       uint32_t hash;                  // a simple hash to speed up searching
-                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
+    uint32_t hash;          // a simple hash to speed up searching
+                            // we first compare hashes, and only if the hashes are equal we do string comparisons
 
-       char *name;
+    char *name;
 
-       struct config *next;    // gloabl config_mutex protects just this
+    struct config *next;    // gloabl config_mutex protects just this
 
-       struct config_value *values;
-       avl_tree_lock values_index;
+    struct config_value *values;
+    avl_tree_lock values_index;
 
-       pthread_mutex_t mutex;  // this locks only the writers, to ensure atomic updates
-                                                       // readers are protected using the rwlock in avl_tree_lock
+    pthread_mutex_t mutex;  // this locks only the writers, to ensure atomic updates
+                            // readers are protected using the rwlock in avl_tree_lock
 } *config_root = NULL;
 
 
@@ -65,19 +47,19 @@ struct config {
 // locking
 
 static inline void config_global_write_lock(void) {
-       pthread_mutex_lock(&config_mutex);
+    pthread_mutex_lock(&config_mutex);
 }
 
 static inline void config_global_unlock(void) {
-       pthread_mutex_unlock(&config_mutex);
+    pthread_mutex_unlock(&config_mutex);
 }
 
 static inline void config_section_write_lock(struct config *co) {
-       pthread_mutex_lock(&co->mutex);
+    pthread_mutex_lock(&co->mutex);
 }
 
 static inline void config_section_unlock(struct config *co) {
-       pthread_mutex_unlock(&co->mutex);
+    pthread_mutex_unlock(&co->mutex);
 }
 
 
@@ -85,20 +67,20 @@ static inline void config_section_unlock(struct config *co) {
 // config name-value index
 
 static int config_value_compare(void* a, void* b) {
-       if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
-       else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
-       else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
+    if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
+    else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
+    else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
 }
 
 #define config_value_index_add(co, cv) avl_insert_lock(&((co)->values_index), (avl *)(cv))
 #define config_value_index_del(co, cv) avl_remove_lock(&((co)->values_index), (avl *)(cv))
 
 static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
-       struct config_value tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = (char *)name;
+    struct config_value tmp;
+    tmp.hash = (hash)?hash:simple_hash(name);
+    tmp.name = (char *)name;
 
-       return (struct config_value *)avl_search_lock(&(co->values_index), (avl *) &tmp);
+    return (struct config_value *)avl_search_lock(&(co->values_index), (avl *) &tmp);
 }
 
 
@@ -106,25 +88,25 @@ static struct config_value *config_value_index_find(struct config *co, const cha
 // config sections index
 
 static int config_compare(void* a, void* b) {
-       if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
-       else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
-       else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
+    if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
+    else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
+    else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
 }
 
 avl_tree_lock config_root_index = {
-               { NULL, config_compare },
-               AVL_LOCK_INITIALIZER
+        { NULL, config_compare },
+        AVL_LOCK_INITIALIZER
 };
 
 #define config_index_add(cfg) avl_insert_lock(&config_root_index, (avl *)(cfg))
 #define config_index_del(cfg) avl_remove_lock(&config_root_index, (avl *)(cfg))
 
 static struct config *config_index_find(const char *name, uint32_t hash) {
-       struct config tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = (char *)name;
+    struct config tmp;
+    tmp.hash = (hash)?hash:simple_hash(name);
+    tmp.name = (char *)name;
 
-       return (struct config *)avl_search_lock(&config_root_index, (avl *) &tmp);
+    return (struct config *)avl_search_lock(&config_root_index, (avl *) &tmp);
 }
 
 
@@ -132,34 +114,31 @@ static struct config *config_index_find(const char *name, uint32_t hash) {
 // config section methods
 
 static inline struct config *config_section_find(const char *section) {
-       return config_index_find(section, 0);
+    return config_index_find(section, 0);
 }
 
 static inline struct config *config_section_create(const char *section)
 {
-       debug(D_CONFIG, "Creating section '%s'.", section);
-
-       struct config *co = calloc(1, sizeof(struct config));
-       if(!co) fatal("Cannot allocate config");
+    debug(D_CONFIG, "Creating section '%s'.", section);
 
-       co->name = strdup(section);
-       if(!co->name) fatal("Cannot allocate config.name");
-       co->hash = simple_hash(co->name);
+    struct config *co = callocz(1, sizeof(struct config));
+    co->name = strdupz(section);
+    co->hash = simple_hash(co->name);
 
-       avl_init_lock(&co->values_index, config_value_compare);
+    avl_init_lock(&co->values_index, config_value_compare);
 
-       config_index_add(co);
+    config_index_add(co);
 
-       config_global_write_lock();
-       struct config *co2 = config_root;
-       if(co2) {
-               while (co2->next) co2 = co2->next;
-               co2->next = co;
-       }
-       else config_root = co;
-       config_global_unlock();
+    config_global_write_lock();
+    struct config *co2 = config_root;
+    if(co2) {
+        while (co2->next) co2 = co2->next;
+        co2->next = co;
+    }
+    else config_root = co;
+    config_global_unlock();
 
-       return co;
+    return co;
 }
 
 
@@ -168,229 +147,221 @@ static inline struct config *config_section_create(const char *section)
 
 static inline struct config_value *config_value_create(struct config *co, const char *name, const char *value)
 {
-       debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
-
-       struct config_value *cv = calloc(1, sizeof(struct config_value));
-       if(!cv) fatal("Cannot allocate config_value");
-
-       cv->name = strdup(name);
-       if(!cv->name) fatal("Cannot allocate config.name");
-       cv->hash = simple_hash(cv->name);
+    debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
 
-       cv->value = strdup(value);
-       if(!cv->value) fatal("Cannot allocate config.value");
+    struct config_value *cv = callocz(1, sizeof(struct config_value));
+    cv->name = strdupz(name);
+    cv->hash = simple_hash(cv->name);
+    cv->value = strdupz(value);
 
-       config_value_index_add(co, cv);
+    config_value_index_add(co, cv);
 
-       config_section_write_lock(co);
-       struct config_value *cv2 = co->values;
-       if(cv2) {
-               while (cv2->next) cv2 = cv2->next;
-               cv2->next = cv;
-       }
-       else co->values = cv;
-       config_section_unlock(co);
+    config_section_write_lock(co);
+    struct config_value *cv2 = co->values;
+    if(cv2) {
+        while (cv2->next) cv2 = cv2->next;
+        cv2->next = cv;
+    }
+    else co->values = cv;
+    config_section_unlock(co);
 
-       return cv;
+    return cv;
 }
 
 int config_exists(const char *section, const char *name) {
-       struct config_value *cv;
+    struct config_value *cv;
 
-       debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name);
+    debug(D_CONFIG, "request to get config in section '%s', name '%s'", section, name);
 
-       struct config *co = config_section_find(section);
-       if(!co) return 0;
+    struct config *co = config_section_find(section);
+    if(!co) return 0;
 
-       cv = config_value_index_find(co, name, 0);
-       if(!cv) return 0;
+    cv = config_value_index_find(co, name, 0);
+    if(!cv) return 0;
 
-       return 1;
+    return 1;
 }
 
 int config_rename(const char *section, const char *old, const char *new) {
-       struct config_value *cv, *cv2;
+    struct config_value *cv, *cv2;
 
-       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', new name '%s'", section, old, new);
 
-       struct config *co = config_section_find(section);
-       if(!co) return -1;
+    struct config *co = config_section_find(section);
+    if(!co) return -1;
 
-       config_section_write_lock(co);
+    config_section_write_lock(co);
 
-       cv = config_value_index_find(co, old, 0);
-       if(!cv) goto cleanup;
+    cv = config_value_index_find(co, old, 0);
+    if(!cv) goto cleanup;
 
-       cv2 = config_value_index_find(co, new, 0);
-       if(cv2) goto cleanup;
+    cv2 = config_value_index_find(co, new, 0);
+    if(cv2) goto cleanup;
 
-       config_value_index_del(co, cv);
+    config_value_index_del(co, cv);
 
-       free(cv->name);
-       cv->name = strdup(new);
-       if(!cv->name) fatal("Cannot allocate memory for config_rename()");
+    freez(cv->name);
+    cv->name = strdupz(new);
 
-       cv->hash = simple_hash(cv->name);
+    cv->hash = simple_hash(cv->name);
 
-       config_value_index_add(co, cv);
-       config_section_unlock(co);
+    config_value_index_add(co, cv);
+    config_section_unlock(co);
 
-       return 0;
+    return 0;
 
 cleanup:
-       config_section_unlock(co);
-       return -1;
+    config_section_unlock(co);
+    return -1;
 }
 
 char *config_get(const char *section, const char *name, const char *default_value)
 {
-       struct config_value *cv;
-
-       debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
-
-       struct config *co = config_section_find(section);
-       if(!co) co = config_section_create(section);
-
-       cv = config_value_index_find(co, name, 0);
-       if(!cv) {
-               cv = config_value_create(co, name, default_value);
-               if(!cv) return NULL;
-       }
-       cv->flags |= CONFIG_VALUE_USED;
-
-       if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
-               // this is a loaded value from the config file
-               // if it is different that the default, mark it
-               if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
-                       if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
-                       cv->flags |= CONFIG_VALUE_CHECKED;
-               }
-       }
-
-       return(cv->value);
+    struct config_value *cv;
+
+    debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
+
+    struct config *co = config_section_find(section);
+    if(!co) co = config_section_create(section);
+
+    cv = config_value_index_find(co, name, 0);
+    if(!cv) {
+        cv = config_value_create(co, name, default_value);
+        if(!cv) return NULL;
+    }
+    cv->flags |= CONFIG_VALUE_USED;
+
+    if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
+        // this is a loaded value from the config file
+        // if it is different that the default, mark it
+        if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
+            if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
+            cv->flags |= CONFIG_VALUE_CHECKED;
+        }
+    }
+
+    return(cv->value);
 }
 
 long long config_get_number(const char *section, const char *name, long long value)
 {
-       char buffer[100], *s;
-       sprintf(buffer, "%lld", value);
+    char buffer[100], *s;
+    sprintf(buffer, "%lld", value);
 
-       s = config_get(section, name, buffer);
-       if(!s) return value;
+    s = config_get(section, name, buffer);
+    if(!s) return value;
 
-       return strtoll(s, NULL, 0);
+    return strtoll(s, NULL, 0);
 }
 
 int config_get_boolean(const char *section, const char *name, int value)
 {
-       char *s;
-       if(value) s = "yes";
-       else s = "no";
+    char *s;
+    if(value) s = "yes";
+    else s = "no";
 
-       s = config_get(section, name, s);
-       if(!s) return value;
+    s = config_get(section, name, s);
+    if(!s) return value;
 
-       if(!strcmp(s, "yes") || !strcmp(s, "auto") || !strcmp(s, "on demand")) return 1;
-       return 0;
+    if(!strcmp(s, "yes") || !strcmp(s, "auto") || !strcmp(s, "on demand")) return 1;
+    return 0;
 }
 
 int config_get_boolean_ondemand(const char *section, const char *name, int value)
 {
-       char *s;
+    char *s;
 
-       if(value == CONFIG_ONDEMAND_ONDEMAND)
-               s = "auto";
+    if(value == CONFIG_ONDEMAND_ONDEMAND)
+        s = "auto";
 
-       else if(value == CONFIG_ONDEMAND_NO)
-               s = "no";
+    else if(value == CONFIG_ONDEMAND_NO)
+        s = "no";
 
-       else
-               s = "yes";
+    else
+        s = "yes";
 
-       s = config_get(section, name, s);
-       if(!s) return value;
+    s = config_get(section, name, s);
+    if(!s) return value;
 
-       if(!strcmp(s, "yes"))
-               return CONFIG_ONDEMAND_YES;
-       else if(!strcmp(s, "no"))
-               return CONFIG_ONDEMAND_NO;
-       else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
-               return CONFIG_ONDEMAND_ONDEMAND;
+    if(!strcmp(s, "yes"))
+        return CONFIG_ONDEMAND_YES;
+    else if(!strcmp(s, "no"))
+        return CONFIG_ONDEMAND_NO;
+    else if(!strcmp(s, "auto") || !strcmp(s, "on demand"))
+        return CONFIG_ONDEMAND_ONDEMAND;
 
-       return value;
+    return value;
 }
 
 const char *config_set_default(const char *section, const char *name, const char *value)
 {
-       struct config_value *cv;
+    struct config_value *cv;
 
-       debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
+    debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
 
-       struct config *co = config_section_find(section);
-       if(!co) return config_set(section, name, value);
+    struct config *co = config_section_find(section);
+    if(!co) return config_set(section, name, value);
 
-       cv = config_value_index_find(co, name, 0);
-       if(!cv) return config_set(section, name, value);
+    cv = config_value_index_find(co, name, 0);
+    if(!cv) return config_set(section, name, value);
 
-       cv->flags |= CONFIG_VALUE_USED;
+    cv->flags |= CONFIG_VALUE_USED;
 
-       if(cv->flags & CONFIG_VALUE_LOADED)
-               return cv->value;
+    if(cv->flags & CONFIG_VALUE_LOADED)
+        return cv->value;
 
-       if(strcmp(cv->value, value) != 0) {
-               cv->flags |= CONFIG_VALUE_CHANGED;
+    if(strcmp(cv->value, value) != 0) {
+        cv->flags |= CONFIG_VALUE_CHANGED;
 
-               free(cv->value);
-               cv->value = strdup(value);
-               if(!cv->value) fatal("Cannot allocate config.value");
-       }
+        freez(cv->value);
+        cv->value = strdupz(value);
+    }
 
-       return cv->value;
+    return cv->value;
 }
 
 const char *config_set(const char *section, const char *name, const char *value)
 {
-       struct config_value *cv;
+    struct config_value *cv;
 
-       debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
+    debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
 
-       struct config *co = config_section_find(section);
-       if(!co) co = config_section_create(section);
+    struct config *co = config_section_find(section);
+    if(!co) co = config_section_create(section);
 
-       cv = config_value_index_find(co, name, 0);
-       if(!cv) cv = config_value_create(co, name, value);
-       cv->flags |= CONFIG_VALUE_USED;
+    cv = config_value_index_find(co, name, 0);
+    if(!cv) cv = config_value_create(co, name, value);
+    cv->flags |= CONFIG_VALUE_USED;
 
-       if(strcmp(cv->value, value) != 0) {
-               cv->flags |= CONFIG_VALUE_CHANGED;
+    if(strcmp(cv->value, value) != 0) {
+        cv->flags |= CONFIG_VALUE_CHANGED;
 
-               free(cv->value);
-               cv->value = strdup(value);
-               if(!cv->value) fatal("Cannot allocate config.value");
-       }
+        freez(cv->value);
+        cv->value = strdupz(value);
+    }
 
-       return value;
+    return value;
 }
 
 long long config_set_number(const char *section, const char *name, long long value)
 {
-       char buffer[100];
-       sprintf(buffer, "%lld", value);
+    char buffer[100];
+    sprintf(buffer, "%lld", value);
 
-       config_set(section, name, buffer);
+    config_set(section, name, buffer);
 
-       return value;
+    return value;
 }
 
 int config_set_boolean(const char *section, const char *name, int value)
 {
-       char *s;
-       if(value) s = "yes";
-       else s = "no";
+    char *s;
+    if(value) s = "yes";
+    else s = "no";
 
-       config_set(section, name, s);
+    config_set(section, name, s);
 
-       return value;
+    return value;
 }
 
 
@@ -399,152 +370,155 @@ int config_set_boolean(const char *section, const char *name, int value)
 
 int load_config(char *filename, int overwrite_used)
 {
-       int line = 0;
-       struct config *co = NULL;
-
-       char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
-
-       if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
-       FILE *fp = fopen(filename, "r");
-       if(!fp) {
-               error("Cannot open file '%s'", filename);
-               return 0;
-       }
-
-       while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
-               buffer[CONFIG_FILE_LINE_MAX] = '\0';
-               line++;
-
-               s = trim(buffer);
-               if(!s) {
-                       debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
-                       continue;
-               }
-
-               int len = (int) strlen(s);
-               if(*s == '[' && s[len - 1] == ']') {
-                       // new section
-                       s[len - 1] = '\0';
-                       s++;
-
-                       co = config_section_find(s);
-                       if(!co) co = config_section_create(s);
-
-                       continue;
-               }
-
-               if(!co) {
-                       // line outside a section
-                       error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
-                       continue;
-               }
-
-               char *name = s;
-               char *value = strchr(s, '=');
-               if(!value) {
-                       error("Ignoring line %d ('%s'), there is no = in it.", line, s);
-                       continue;
-               }
-               *value = '\0';
-               value++;
-
-               name = trim(name);
-               value = trim(value);
-
-               if(!name) {
-                       error("Ignoring line %d, name is empty.", line);
-                       continue;
-               }
-               if(!value) {
-                       debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
-                       continue;
-               }
-
-               struct config_value *cv = config_value_index_find(co, name, 0);
-
-               if(!cv) cv = config_value_create(co, name, value);
-               else {
-                       if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
-                               debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
-                               free(cv->value);
-                               cv->value = strdup(value);
-                               if(!cv->value) fatal("Cannot allocate config.value");
-                       }
-                       else
-                               debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
-               }
-               cv->flags |= CONFIG_VALUE_LOADED;
-       }
-
-       fclose(fp);
-
-       return 1;
+    int line = 0;
+    struct config *co = NULL;
+
+    char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
+
+    if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
+    FILE *fp = fopen(filename, "r");
+    if(!fp) {
+        error("Cannot open file '%s'", filename);
+        return 0;
+    }
+
+    while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
+        buffer[CONFIG_FILE_LINE_MAX] = '\0';
+        line++;
+
+        s = trim(buffer);
+        if(!s) {
+            debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
+            continue;
+        }
+
+        int len = (int) strlen(s);
+        if(*s == '[' && s[len - 1] == ']') {
+            // new section
+            s[len - 1] = '\0';
+            s++;
+
+            co = config_section_find(s);
+            if(!co) co = config_section_create(s);
+
+            continue;
+        }
+
+        if(!co) {
+            // line outside a section
+            error("Ignoring line %d ('%s'), it is outside all sections.", line, s);
+            continue;
+        }
+
+        char *name = s;
+        char *value = strchr(s, '=');
+        if(!value) {
+            error("Ignoring line %d ('%s'), there is no = in it.", line, s);
+            continue;
+        }
+        *value = '\0';
+        value++;
+
+        name = trim(name);
+        value = trim(value);
+
+        if(!name) {
+            error("Ignoring line %d, name is empty.", line);
+            continue;
+        }
+        if(!value) {
+            debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
+            continue;
+        }
+
+        struct config_value *cv = config_value_index_find(co, name, 0);
+
+        if(!cv) cv = config_value_create(co, name, value);
+        else {
+            if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
+                debug(D_CONFIG, "Line %d, overwriting '%s/%s'.", line, co->name, cv->name);
+                freez(cv->value);
+                cv->value = strdupz(value);
+            }
+            else
+                debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
+        }
+        cv->flags |= CONFIG_VALUE_LOADED;
+    }
+
+    fclose(fp);
+
+    return 1;
 }
 
 void generate_config(BUFFER *wb, int only_changed)
 {
-       int i, pri;
-       struct config *co;
-       struct config_value *cv;
-
-       for(i = 0; i < 3 ;i++) {
-               switch(i) {
-                       case 0:
-                               buffer_strcat(wb,
-                                       "# NetData Configuration\n"
-                                       "# You can uncomment and change any of the options below.\n"
-                                       "# The value shown in the commented settings, is the default value.\n"
-                                       "\n# global netdata configuration\n");
-                               break;
-
-                       case 1:
-                               buffer_strcat(wb, "\n\n# per plugin configuration\n");
-                               break;
-
-                       case 2:
-                               buffer_strcat(wb, "\n\n# per chart configuration\n");
-                               break;
-               }
-
-               config_global_write_lock();
-               for(co = config_root; co ; co = co->next) {
-                       if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0 || strcmp(co->name, "registry") == 0) pri = 0;
-                       else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
-                       else pri = 2;
-
-                       if(i == pri) {
-                               int used = 0;
-                               int changed = 0;
-                               int count = 0;
-
-                               config_section_write_lock(co);
-                               for(cv = co->values; cv ; cv = cv->next) {
-                                       used += (cv->flags & CONFIG_VALUE_USED)?1:0;
-                                       changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
-                                       count++;
-                               }
-                               config_section_unlock(co);
-
-                               if(!count) continue;
-                               if(only_changed && !changed) continue;
-
-                               if(!used) {
-                                       buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
-                               }
-
-                               buffer_sprintf(wb, "\n[%s]\n", co->name);
-
-                               config_section_write_lock(co);
-                               for(cv = co->values; cv ; cv = cv->next) {
-
-                                       if(used && !(cv->flags & CONFIG_VALUE_USED)) {
-                                               buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
-                                       }
-                                       buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
-                               }
-                               config_section_unlock(co);
-                       }
-               }
-               config_global_unlock();
-       }
+    int i, pri;
+    struct config *co;
+    struct config_value *cv;
+
+    for(i = 0; i < 3 ;i++) {
+        switch(i) {
+            case 0:
+                buffer_strcat(wb,
+                    "# NetData Configuration\n"
+                    "# You can uncomment and change any of the options below.\n"
+                    "# The value shown in the commented settings, is the default value.\n"
+                    "\n# global netdata configuration\n");
+                break;
+
+            case 1:
+                buffer_strcat(wb, "\n\n# per plugin configuration\n");
+                break;
+
+            case 2:
+                buffer_strcat(wb, "\n\n# per chart configuration\n");
+                break;
+        }
+
+        config_global_write_lock();
+        for(co = config_root; co ; co = co->next) {
+            if(!strcmp(co->name, "global") ||
+                    !strcmp(co->name, "plugins") ||
+                    !strcmp(co->name, "registry") ||
+                    !strcmp(co->name, "health"))
+                pri = 0;
+            else if(!strncmp(co->name, "plugin:", 7)) pri = 1;
+            else pri = 2;
+
+            if(i == pri) {
+                int used = 0;
+                int changed = 0;
+                int count = 0;
+
+                config_section_write_lock(co);
+                for(cv = co->values; cv ; cv = cv->next) {
+                    used += (cv->flags & CONFIG_VALUE_USED)?1:0;
+                    changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
+                    count++;
+                }
+                config_section_unlock(co);
+
+                if(!count) continue;
+                if(only_changed && !changed) continue;
+
+                if(!used) {
+                    buffer_sprintf(wb, "\n# node '%s' is not used.", co->name);
+                }
+
+                buffer_sprintf(wb, "\n[%s]\n", co->name);
+
+                config_section_write_lock(co);
+                for(cv = co->values; cv ; cv = cv->next) {
+
+                    if(used && !(cv->flags & CONFIG_VALUE_USED)) {
+                        buffer_sprintf(wb, "\n\t# option '%s' is not used.\n", cv->name);
+                    }
+                    buffer_sprintf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
+                }
+                config_section_unlock(co);
+            }
+        }
+        config_global_unlock();
+    }
 }
index b5245e554ce876d626f4c808ef2fd461b5f1ce84..08aae8348891c08fda9298a9a68046b50a59b973 100644 (file)
@@ -1,5 +1,3 @@
-#include "web_buffer.h"
-
 #ifndef NETDATA_CONFIG_H
 #define NETDATA_CONFIG_H 1
 
index 6b43216cb71a23b0b466e4f215109c20dd56555c..301055e2a9fbac3a9ae53e1114369ed0e2c6e1c8 100644 (file)
@@ -1,44 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#include <sys/resource.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stdarg.h>
-#include <locale.h>
-#include <ctype.h>
-#include <fcntl.h>
-
-#include <malloc.h>
-#include <dirent.h>
-#include <arpa/inet.h>
-
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-
-#include "avl.h"
-
 #include "common.h"
-#include "log.h"
-#include "procfile.h"
-#include "../config.h"
-#include "web_buffer.h"
-
-#ifdef NETDATA_INTERNAL_CHECKS
-#include <sys/prctl.h>
-#endif
 
 #define MAX_COMPARE_NAME 100
 #define MAX_NAME 100
@@ -72,11 +32,12 @@ int show_guest_time = 0;
 int show_guest_time_old = 0;
 
 int enable_guest_charts = 0;
+int enable_file_charts = 1;
 
 // ----------------------------------------------------------------------------
 
 void netdata_cleanup_and_exit(int ret) {
-       exit(ret);
+    exit(ret);
 }
 
 
@@ -85,55 +46,55 @@ void netdata_cleanup_and_exit(int ret) {
 // to retrieve settings of the system
 
 long get_system_cpus(void) {
-       procfile *ff = NULL;
+    procfile *ff = NULL;
 
-       int processors = 0;
+    int processors = 0;
 
-       char filename[FILENAME_MAX + 1];
-       snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
+    char filename[FILENAME_MAX + 1];
+    snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
 
-       ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
-       if(!ff) return 1;
+    ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+    if(!ff) return 1;
 
-       ff = procfile_readall(ff);
-       if(!ff) {
-               procfile_close(ff);
-               return 1;
-       }
+    ff = procfile_readall(ff);
+    if(!ff) {
+        procfile_close(ff);
+        return 1;
+    }
 
-       unsigned int i;
-       for(i = 0; i < procfile_lines(ff); i++) {
-               if(!procfile_linewords(ff, i)) continue;
+    unsigned int i;
+    for(i = 0; i < procfile_lines(ff); i++) {
+        if(!procfile_linewords(ff, i)) continue;
 
-               if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
-       }
-       processors--;
-       if(processors < 1) processors = 1;
+        if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
+    }
+    processors--;
+    if(processors < 1) processors = 1;
 
-       procfile_close(ff);
-       return processors;
+    procfile_close(ff);
+    return processors;
 }
 
 pid_t get_system_pid_max(void) {
-       procfile *ff = NULL;
-       pid_t mpid = 32768;
-
-       char filename[FILENAME_MAX + 1];
-       snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", host_prefix);
-       ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
-       if(!ff) return mpid;
-
-       ff = procfile_readall(ff);
-       if(!ff) {
-               procfile_close(ff);
-               return mpid;
-       }
+    procfile *ff = NULL;
+    pid_t mpid = 32768;
+
+    char filename[FILENAME_MAX + 1];
+    snprintfz(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", host_prefix);
+    ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
+    if(!ff) return mpid;
+
+    ff = procfile_readall(ff);
+    if(!ff) {
+        procfile_close(ff);
+        return mpid;
+    }
 
-       mpid = (pid_t)atoi(procfile_lineword(ff, 0, 0));
-       if(!mpid) mpid = 32768;
+    mpid = (pid_t)atoi(procfile_lineword(ff, 0, 0));
+    if(!mpid) mpid = 32768;
 
-       procfile_close(ff);
-       return mpid;
+    procfile_close(ff);
+    return mpid;
 }
 
 // ----------------------------------------------------------------------------
@@ -141,68 +102,68 @@ pid_t get_system_pid_max(void) {
 // target is the structure that process data are aggregated
 
 struct target {
-       char compare[MAX_COMPARE_NAME + 1];
-       uint32_t comparehash;
-       size_t comparelen;
-
-       char id[MAX_NAME + 1];
-       uint32_t idhash;
-
-       char name[MAX_NAME + 1];
-
-       uid_t uid;
-       gid_t gid;
-
-       unsigned long long minflt;
-       unsigned long long cminflt;
-       unsigned long long majflt;
-       unsigned long long cmajflt;
-       unsigned long long utime;
-       unsigned long long stime;
-       unsigned long long gtime;
-       unsigned long long cutime;
-       unsigned long long cstime;
-       unsigned long long cgtime;
-       unsigned long long num_threads;
-       unsigned long long rss;
-
-       unsigned long long statm_size;
-       unsigned long long statm_resident;
-       unsigned long long statm_share;
-       unsigned long long statm_text;
-       unsigned long long statm_lib;
-       unsigned long long statm_data;
-       unsigned long long statm_dirty;
-
-       unsigned long long io_logical_bytes_read;
-       unsigned long long io_logical_bytes_written;
-       unsigned long long io_read_calls;
-       unsigned long long io_write_calls;
-       unsigned long long io_storage_bytes_read;
-       unsigned long long io_storage_bytes_written;
-       unsigned long long io_cancelled_write_bytes;
-
-       int *fds;
-       unsigned long long openfiles;
-       unsigned long long openpipes;
-       unsigned long long opensockets;
-       unsigned long long openinotifies;
-       unsigned long long openeventfds;
-       unsigned long long opentimerfds;
-       unsigned long long opensignalfds;
-       unsigned long long openeventpolls;
-       unsigned long long openother;
-
-       unsigned long processes;        // how many processes have been merged to this
-       int exposed;                            // if set, we have sent this to netdata
-       int hidden;                                     // if set, we set the hidden flag on the dimension
-       int debug;
-       int ends_with;
-       int starts_with;            // if set, the compare string matches only the
-                                                               // beginning of the command
-
-       struct target *target;          // the one that will be reported to netdata
-       struct target *next;
+    char compare[MAX_COMPARE_NAME + 1];
+    uint32_t comparehash;
+    size_t comparelen;
+
+    char id[MAX_NAME + 1];
+    uint32_t idhash;
+
+    char name[MAX_NAME + 1];
+
+    uid_t uid;
+    gid_t gid;
+
+    unsigned long long minflt;
+    unsigned long long cminflt;
+    unsigned long long majflt;
+    unsigned long long cmajflt;
+    unsigned long long utime;
+    unsigned long long stime;
+    unsigned long long gtime;
+    unsigned long long cutime;
+    unsigned long long cstime;
+    unsigned long long cgtime;
+    unsigned long long num_threads;
+    unsigned long long rss;
+
+    unsigned long long statm_size;
+    unsigned long long statm_resident;
+    unsigned long long statm_share;
+    unsigned long long statm_text;
+    unsigned long long statm_lib;
+    unsigned long long statm_data;
+    unsigned long long statm_dirty;
+
+    unsigned long long io_logical_bytes_read;
+    unsigned long long io_logical_bytes_written;
+    unsigned long long io_read_calls;
+    unsigned long long io_write_calls;
+    unsigned long long io_storage_bytes_read;
+    unsigned long long io_storage_bytes_written;
+    unsigned long long io_cancelled_write_bytes;
+
+    int *fds;
+    unsigned long long openfiles;
+    unsigned long long openpipes;
+    unsigned long long opensockets;
+    unsigned long long openinotifies;
+    unsigned long long openeventfds;
+    unsigned long long opentimerfds;
+    unsigned long long opensignalfds;
+    unsigned long long openeventpolls;
+    unsigned long long openother;
+
+    unsigned long processes;    // how many processes have been merged to this
+    int exposed;                // if set, we have sent this to netdata
+    int hidden;                 // if set, we set the hidden flag on the dimension
+    int debug;
+    int ends_with;
+    int starts_with;            // if set, the compare string matches only the
+                                // beginning of the command
+
+    struct target *target;      // the one that will be reported to netdata
+    struct target *next;
 };
 
 
@@ -219,230 +180,214 @@ struct target *groups_root_target = NULL;
 
 struct target *get_users_target(uid_t uid)
 {
-       struct target *w;
-       for(w = users_root_target ; w ; w = w->next)
-               if(w->uid == uid) return w;
-
-       w = calloc(sizeof(struct target), 1);
-       if(unlikely(!w)) {
-               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
-               return NULL;
-       }
+    struct target *w;
+    for(w = users_root_target ; w ; w = w->next)
+        if(w->uid == uid) return w;
 
-       snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
-       w->comparehash = simple_hash(w->compare);
-       w->comparelen = strlen(w->compare);
+    w = callocz(sizeof(struct target), 1);
+    snprintfz(w->compare, MAX_COMPARE_NAME, "%u", uid);
+    w->comparehash = simple_hash(w->compare);
+    w->comparelen = strlen(w->compare);
 
-       snprintfz(w->id, MAX_NAME, "%u", uid);
-       w->idhash = simple_hash(w->id);
+    snprintfz(w->id, MAX_NAME, "%u", uid);
+    w->idhash = simple_hash(w->id);
 
-       struct passwd *pw = getpwuid(uid);
-       if(!pw)
-               snprintfz(w->name, MAX_NAME, "%u", uid);
-       else
-               snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
+    struct passwd *pw = getpwuid(uid);
+    if(!pw)
+        snprintfz(w->name, MAX_NAME, "%u", uid);
+    else
+        snprintfz(w->name, MAX_NAME, "%s", pw->pw_name);
 
-       netdata_fix_chart_name(w->name);
+    netdata_fix_chart_name(w->name);
 
-       w->uid = uid;
+    w->uid = uid;
 
-       w->next = users_root_target;
-       users_root_target = w;
+    w->next = users_root_target;
+    users_root_target = w;
 
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: added uid %u ('%s') target\n", w->uid, w->name);
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: added uid %u ('%s') target\n", w->uid, w->name);
 
-       return w;
+    return w;
 }
 
 struct target *get_groups_target(gid_t gid)
 {
-       struct target *w;
-       for(w = groups_root_target ; w ; w = w->next)
-               if(w->gid == gid) return w;
+    struct target *w;
+    for(w = groups_root_target ; w ; w = w->next)
+        if(w->gid == gid) return w;
 
-       w = calloc(sizeof(struct target), 1);
-       if(unlikely(!w)) {
-               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
-               return NULL;
-       }
+    w = callocz(sizeof(struct target), 1);
+    snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
+    w->comparehash = simple_hash(w->compare);
+    w->comparelen = strlen(w->compare);
 
-       snprintfz(w->compare, MAX_COMPARE_NAME, "%u", gid);
-       w->comparehash = simple_hash(w->compare);
-       w->comparelen = strlen(w->compare);
+    snprintfz(w->id, MAX_NAME, "%u", gid);
+    w->idhash = simple_hash(w->id);
 
-       snprintfz(w->id, MAX_NAME, "%u", gid);
-       w->idhash = simple_hash(w->id);
+    struct group *gr = getgrgid(gid);
+    if(!gr)
+        snprintfz(w->name, MAX_NAME, "%u", gid);
+    else
+        snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
 
-       struct group *gr = getgrgid(gid);
-       if(!gr)
-               snprintfz(w->name, MAX_NAME, "%u", gid);
-       else
-               snprintfz(w->name, MAX_NAME, "%s", gr->gr_name);
+    netdata_fix_chart_name(w->name);
 
-       netdata_fix_chart_name(w->name);
+    w->gid = gid;
 
-       w->gid = gid;
+    w->next = groups_root_target;
+    groups_root_target = w;
 
-       w->next = groups_root_target;
-       groups_root_target = w;
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: added gid %u ('%s') target\n", w->gid, w->name);
 
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: added gid %u ('%s') target\n", w->gid, w->name);
-
-       return w;
+    return w;
 }
 
 // find or create a new target
 // there are targets that are just aggregated to other target (the second argument)
-struct target *get_apps_groups_target(const char *id, struct target *target)
-{
-       int tdebug = 0, thidden = 0, ends_with = 0;
-       const char *nid = id;
-
-       while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
-               if(nid[0] == '-') thidden = 1;
-               if(nid[0] == '+') tdebug = 1;
-               if(nid[0] == '*') ends_with = 1;
-               nid++;
-       }
-       uint32_t hash = simple_hash(id);
-
-       struct target *w, *last = apps_groups_root_target;
-       for(w = apps_groups_root_target ; w ; w = w->next) {
-               if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
-                       return w;
-
-               last = w;
-       }
-
-       w = calloc(sizeof(struct target), 1);
-       if(unlikely(!w)) {
-               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
-               return NULL;
-       }
-
-       strncpyz(w->id, nid, MAX_NAME);
-       w->idhash = simple_hash(w->id);
-
-       strncpyz(w->name, nid, MAX_NAME);
-
-       strncpyz(w->compare, nid, MAX_COMPARE_NAME);
-       int len = strlen(w->compare);
-       if(w->compare[len - 1] == '*') {
-               w->compare[len - 1] = '\0';
-               w->starts_with = 1;
-       }
-       w->ends_with = ends_with;
-
-       if(w->starts_with && w->ends_with)
-               proc_pid_cmdline_is_needed = 1;
-
-       w->comparehash = simple_hash(w->compare);
-       w->comparelen = strlen(w->compare);
-
-       w->hidden = thidden;
-       w->debug = tdebug;
-       w->target = target;
-
-       // append it, to maintain the order in apps_groups.conf
-       if(last) last->next = w;
-       else apps_groups_root_target = w;
-
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
-                       , w->id
-                               , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
-                               , w->target?w->target->id:w->id
-                               , (w->hidden)?"hidden":"-"
-                               , (w->debug)?"debug":"-"
-               );
-
-       return w;
+struct target *get_apps_groups_target(const char *id, struct target *target) {
+    int tdebug = 0, thidden = 0, ends_with = 0;
+    const char *nid = id;
+
+    while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
+        if(nid[0] == '-') thidden = 1;
+        if(nid[0] == '+') tdebug = 1;
+        if(nid[0] == '*') ends_with = 1;
+        nid++;
+    }
+    uint32_t hash = simple_hash(id);
+
+    struct target *w, *last = apps_groups_root_target;
+    for(w = apps_groups_root_target ; w ; w = w->next) {
+        if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
+            return w;
+
+        last = w;
+    }
+
+    w = callocz(sizeof(struct target), 1);
+    strncpyz(w->id, nid, MAX_NAME);
+    w->idhash = simple_hash(w->id);
+
+    strncpyz(w->name, nid, MAX_NAME);
+
+    strncpyz(w->compare, nid, MAX_COMPARE_NAME);
+    size_t len = strlen(w->compare);
+    if(w->compare[len - 1] == '*') {
+        w->compare[len - 1] = '\0';
+        w->starts_with = 1;
+    }
+    w->ends_with = ends_with;
+
+    if(w->starts_with && w->ends_with)
+        proc_pid_cmdline_is_needed = 1;
+
+    w->comparehash = simple_hash(w->compare);
+    w->comparelen = strlen(w->compare);
+
+    w->hidden = thidden;
+    w->debug = tdebug;
+    w->target = target;
+
+    // append it, to maintain the order in apps_groups.conf
+    if(last) last->next = w;
+    else apps_groups_root_target = w;
+
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: ADDING TARGET ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
+                , w->id
+                , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
+                , w->target?w->target->id:w->id
+                , (w->hidden)?"hidden":"-"
+                , (w->debug)?"debug":"-"
+        );
+
+    return w;
 }
 
 // read the apps_groups.conf file
 int read_apps_groups_conf(const char *name)
 {
-       char filename[FILENAME_MAX + 1];
-
-       snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
-
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
+    char filename[FILENAME_MAX + 1];
 
-       // ----------------------------------------
+    snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
 
-       procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
-       if(!ff) return 1;
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
 
-       procfile_set_quotes(ff, "'\"");
+    // ----------------------------------------
 
-       ff = procfile_readall(ff);
-       if(!ff) {
-               procfile_close(ff);
-               return 1;
-       }
+    procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
+    if(!ff) return 1;
 
-       unsigned long line, lines = procfile_lines(ff);
+    procfile_set_quotes(ff, "'\"");
 
-       for(line = 0; line < lines ;line++) {
-               unsigned long word, words = procfile_linewords(ff, line);
-               struct target *w = NULL;
-
-               char *t = procfile_lineword(ff, line, 0);
-               if(!t || !*t) continue;
+    ff = procfile_readall(ff);
+    if(!ff) {
+        procfile_close(ff);
+        return 1;
+    }
 
-               for(word = 0; word < words ;word++) {
-                       char *s = procfile_lineword(ff, line, word);
-                       if(!s || !*s) continue;
-                       if(*s == '#') break;
+    unsigned long line, lines = procfile_lines(ff);
 
-                       if(t == s) continue;
+    for(line = 0; line < lines ;line++) {
+        unsigned long word, words = procfile_linewords(ff, line);
+        struct target *w = NULL;
 
-                       struct target *n = get_apps_groups_target(s, w);
-                       if(!n) {
-                               error("Cannot create target '%s' (line %lu, word %lu)", s, line, word);
-                               continue;
-                       }
+        char *t = procfile_lineword(ff, line, 0);
+        if(!t || !*t) continue;
 
-                       if(!w) w = n;
-               }
+        for(word = 0; word < words ;word++) {
+            char *s = procfile_lineword(ff, line, word);
+            if(!s || !*s) continue;
+            if(*s == '#') break;
 
-               if(w) {
-                       int tdebug = 0, thidden = 0;
+            if(t == s) continue;
 
-                       while(t[0] == '-' || t[0] == '+') {
-                               if(t[0] == '-') thidden = 1;
-                               if(t[0] == '+') tdebug = 1;
-                               t++;
-                       }
+            struct target *n = get_apps_groups_target(s, w);
+            if(!n) {
+                error("Cannot create target '%s' (line %lu, word %lu)", s, line, word);
+                continue;
+            }
 
-                       strncpyz(w->name, t, MAX_NAME);
-                       w->hidden = thidden;
-                       w->debug = tdebug;
+            if(!w) w = n;
+        }
 
-                       if(unlikely(debug))
-                               fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
-                                               , w->name
-                                               , w->id
-                                               , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
-                                               , w->target?w->target->id:w->id
-                                               , (w->hidden)?"hidden":"-"
-                                               , (w->debug)?"debug":"-"
-                               );
-               }
-       }
+        if(w) {
+            int tdebug = 0, thidden = 0;
+
+            while(t[0] == '-' || t[0] == '+') {
+                if(t[0] == '-') thidden = 1;
+                if(t[0] == '+') tdebug = 1;
+                t++;
+            }
+
+            strncpyz(w->name, t, MAX_NAME);
+            w->hidden = thidden;
+            w->debug = tdebug;
+
+            if(unlikely(debug))
+                fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
+                        , w->name
+                        , w->id
+                        , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
+                        , w->target?w->target->id:w->id
+                        , (w->hidden)?"hidden":"-"
+                        , (w->debug)?"debug":"-"
+                );
+        }
+    }
 
-       procfile_close(ff);
+    procfile_close(ff);
 
-       apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
-       if(!apps_groups_default_target)
-               error("Cannot create default target");
-       else
-               strncpyz(apps_groups_default_target->name, "other", MAX_NAME);
+    apps_groups_default_target = get_apps_groups_target("p+!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
+    if(!apps_groups_default_target)
+        error("Cannot create default target");
+    else
+        strncpyz(apps_groups_default_target->name, "other", MAX_NAME);
 
-       return 0;
+    return 0;
 }
 
 
@@ -450,182 +395,183 @@ int read_apps_groups_conf(const char *name)
 // data to store for each pid
 // see: man proc
 
+#define PID_LOG_IO      0x00000001
+#define PID_LOG_STATM   0x00000002
+#define PID_LOG_CMDLINE 0x00000004
+#define PID_LOG_FDS     0x00000008
+#define PID_LOG_STAT    0x00000010
+
 struct pid_stat {
-       int32_t pid;
-       char comm[MAX_COMPARE_NAME + 1];
-       char cmdline[MAX_CMDLINE + 1];
-
-       // char state;
-       int32_t ppid;
-       // int32_t pgrp;
-       // int32_t session;
-       // int32_t tty_nr;
-       // int32_t tpgid;
-       // uint64_t flags;
-
-       // these are raw values collected
-       unsigned long long minflt_raw;
-       unsigned long long cminflt_raw;
-       unsigned long long majflt_raw;
-       unsigned long long cmajflt_raw;
-       unsigned long long utime_raw;
-       unsigned long long stime_raw;
-       unsigned long long gtime_raw; // guest_time
-       unsigned long long cutime_raw;
-       unsigned long long cstime_raw;
-       unsigned long long cgtime_raw; // cguest_time
-
-       // these are rates
-       unsigned long long minflt;
-       unsigned long long cminflt;
-       unsigned long long majflt;
-       unsigned long long cmajflt;
-       unsigned long long utime;
-       unsigned long long stime;
-       unsigned long long gtime;
-       unsigned long long cutime;
-       unsigned long long cstime;
-       unsigned long long cgtime;
-
-       // int64_t priority;
-       // int64_t nice;
-       int32_t num_threads;
-       // int64_t itrealvalue;
-       // unsigned long long starttime;
-       // unsigned long long vsize;
-       unsigned long long rss;
-       // unsigned long long rsslim;
-       // unsigned long long starcode;
-       // unsigned long long endcode;
-       // unsigned long long startstack;
-       // unsigned long long kstkesp;
-       // unsigned long long kstkeip;
-       // uint64_t signal;
-       // uint64_t blocked;
-       // uint64_t sigignore;
-       // uint64_t sigcatch;
-       // uint64_t wchan;
-       // uint64_t nswap;
-       // uint64_t cnswap;
-       // int32_t exit_signal;
-       // int32_t processor;
-       // uint32_t rt_priority;
-       // uint32_t policy;
-       // unsigned long long delayacct_blkio_ticks;
-
-       uid_t uid;
-       gid_t gid;
-
-       unsigned long long statm_size;
-       unsigned long long statm_resident;
-       unsigned long long statm_share;
-       unsigned long long statm_text;
-       unsigned long long statm_lib;
-       unsigned long long statm_data;
-       unsigned long long statm_dirty;
-
-       unsigned long long io_logical_bytes_read_raw;
-       unsigned long long io_logical_bytes_written_raw;
-       unsigned long long io_read_calls_raw;
-       unsigned long long io_write_calls_raw;
-       unsigned long long io_storage_bytes_read_raw;
-       unsigned long long io_storage_bytes_written_raw;
-       unsigned long long io_cancelled_write_bytes_raw;
-
-       unsigned long long io_logical_bytes_read;
-       unsigned long long io_logical_bytes_written;
-       unsigned long long io_read_calls;
-       unsigned long long io_write_calls;
-       unsigned long long io_storage_bytes_read;
-       unsigned long long io_storage_bytes_written;
-       unsigned long long io_cancelled_write_bytes;
-
-       int *fds;                                               // array of fds it uses
-       int fds_size;                                   // the size of the fds array
-
-       int children_count;                             // number of processes directly referencing this
-       int keep;                                               // 1 when we need to keep this process in memory even after it exited
-       int keeploops;                                  // increases by 1 every time keep is 1 and updated 0
-       int updated;                                    // 1 when the process is currently running
-       int merged;                                             // 1 when it has been merged to its parent
-       int new_entry;                                  // 1 when this is a new process, just saw for the first time
-       int read;                                               // 1 when we have already read this process for this iteration
-       int sortlist;                                   // higher numbers = top on the process tree
-                                                                       // each process gets a unique number
-
-       struct target *target;                  // app_groups.conf targets
-       struct target *user_target;             // uid based targets
-       struct target *group_target;    // gid based targets
-
-       unsigned long long stat_collected_usec;
-       unsigned long long last_stat_collected_usec;
-
-       unsigned long long io_collected_usec;
-       unsigned long long last_io_collected_usec;
-
-       char *stat_filename;
-       char *statm_filename;
-       char *io_filename;
-       char *cmdline_filename;
-
-       struct pid_stat *parent;
-       struct pid_stat *prev;
-       struct pid_stat *next;
+    int32_t pid;
+    char comm[MAX_COMPARE_NAME + 1];
+    char cmdline[MAX_CMDLINE + 1];
+
+    uint32_t log_thrown;
+
+    // char state;
+    int32_t ppid;
+    // int32_t pgrp;
+    // int32_t session;
+    // int32_t tty_nr;
+    // int32_t tpgid;
+    // uint64_t flags;
+
+    // these are raw values collected
+    unsigned long long minflt_raw;
+    unsigned long long cminflt_raw;
+    unsigned long long majflt_raw;
+    unsigned long long cmajflt_raw;
+    unsigned long long utime_raw;
+    unsigned long long stime_raw;
+    unsigned long long gtime_raw; // guest_time
+    unsigned long long cutime_raw;
+    unsigned long long cstime_raw;
+    unsigned long long cgtime_raw; // cguest_time
+
+    // these are rates
+    unsigned long long minflt;
+    unsigned long long cminflt;
+    unsigned long long majflt;
+    unsigned long long cmajflt;
+    unsigned long long utime;
+    unsigned long long stime;
+    unsigned long long gtime;
+    unsigned long long cutime;
+    unsigned long long cstime;
+    unsigned long long cgtime;
+
+    // int64_t priority;
+    // int64_t nice;
+    int32_t num_threads;
+    // int64_t itrealvalue;
+    // unsigned long long starttime;
+    // unsigned long long vsize;
+    unsigned long long rss;
+    // unsigned long long rsslim;
+    // unsigned long long starcode;
+    // unsigned long long endcode;
+    // unsigned long long startstack;
+    // unsigned long long kstkesp;
+    // unsigned long long kstkeip;
+    // uint64_t signal;
+    // uint64_t blocked;
+    // uint64_t sigignore;
+    // uint64_t sigcatch;
+    // uint64_t wchan;
+    // uint64_t nswap;
+    // uint64_t cnswap;
+    // int32_t exit_signal;
+    // int32_t processor;
+    // uint32_t rt_priority;
+    // uint32_t policy;
+    // unsigned long long delayacct_blkio_ticks;
+
+    uid_t uid;
+    gid_t gid;
+
+    unsigned long long statm_size;
+    unsigned long long statm_resident;
+    unsigned long long statm_share;
+    unsigned long long statm_text;
+    unsigned long long statm_lib;
+    unsigned long long statm_data;
+    unsigned long long statm_dirty;
+
+    unsigned long long io_logical_bytes_read_raw;
+    unsigned long long io_logical_bytes_written_raw;
+    unsigned long long io_read_calls_raw;
+    unsigned long long io_write_calls_raw;
+    unsigned long long io_storage_bytes_read_raw;
+    unsigned long long io_storage_bytes_written_raw;
+    unsigned long long io_cancelled_write_bytes_raw;
+
+    unsigned long long io_logical_bytes_read;
+    unsigned long long io_logical_bytes_written;
+    unsigned long long io_read_calls;
+    unsigned long long io_write_calls;
+    unsigned long long io_storage_bytes_read;
+    unsigned long long io_storage_bytes_written;
+    unsigned long long io_cancelled_write_bytes;
+
+    int *fds;                       // array of fds it uses
+    int fds_size;                   // the size of the fds array
+
+    int children_count;             // number of processes directly referencing this
+    int keep;                       // 1 when we need to keep this process in memory even after it exited
+    int keeploops;                  // increases by 1 every time keep is 1 and updated 0
+    int updated;                    // 1 when the process is currently running
+    int merged;                     // 1 when it has been merged to its parent
+    int new_entry;                  // 1 when this is a new process, just saw for the first time
+    int read;                       // 1 when we have already read this process for this iteration
+    int sortlist;                   // higher numbers = top on the process tree
+                                    // each process gets a unique number
+
+    struct target *target;          // app_groups.conf targets
+    struct target *user_target;     // uid based targets
+    struct target *group_target;    // gid based targets
+
+    unsigned long long stat_collected_usec;
+    unsigned long long last_stat_collected_usec;
+
+    unsigned long long io_collected_usec;
+    unsigned long long last_io_collected_usec;
+
+    char *stat_filename;
+    char *statm_filename;
+    char *io_filename;
+    char *cmdline_filename;
+
+    struct pid_stat *parent;
+    struct pid_stat *prev;
+    struct pid_stat *next;
 } *root_of_pids = NULL, **all_pids;
 
 long all_pids_count = 0;
 
 struct pid_stat *get_pid_entry(pid_t pid) {
-       if(all_pids[pid]) {
-               all_pids[pid]->new_entry = 0;
-               return all_pids[pid];
-       }
-
-       all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
-       if(!all_pids[pid]) {
-               error("Cannot allocate %zu bytes of memory", (size_t)sizeof(struct pid_stat));
-               return NULL;
-       }
+    if(all_pids[pid]) {
+        all_pids[pid]->new_entry = 0;
+        return all_pids[pid];
+    }
 
-       all_pids[pid]->fds = calloc(sizeof(int), 100);
-       if(!all_pids[pid]->fds)
-               error("Cannot allocate %zu bytes of memory", (size_t)(sizeof(int) * 100));
-       else all_pids[pid]->fds_size = 100;
+    all_pids[pid] = callocz(sizeof(struct pid_stat), 1);
+    all_pids[pid]->fds = callocz(sizeof(int), 100);
+    all_pids[pid]->fds_size = 100;
 
-       if(root_of_pids) root_of_pids->prev = all_pids[pid];
-       all_pids[pid]->next = root_of_pids;
-       root_of_pids = all_pids[pid];
+    if(root_of_pids) root_of_pids->prev = all_pids[pid];
+    all_pids[pid]->next = root_of_pids;
+    root_of_pids = all_pids[pid];
 
-       all_pids[pid]->pid = pid;
-       all_pids[pid]->new_entry = 1;
+    all_pids[pid]->pid = pid;
+    all_pids[pid]->new_entry = 1;
 
-       all_pids_count++;
+    all_pids_count++;
 
-       return all_pids[pid];
+    return all_pids[pid];
 }
 
 void del_pid_entry(pid_t pid) {
-       if(!all_pids[pid]) {
-               error("attempted to free pid %d that is not allocated.", pid);
-               return;
-       }
-
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
-
-       if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
-       if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
-       if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
-
-       if(all_pids[pid]->fds) free(all_pids[pid]->fds);
-       if(all_pids[pid]->stat_filename) free(all_pids[pid]->stat_filename);
-       if(all_pids[pid]->statm_filename) free(all_pids[pid]->statm_filename);
-       if(all_pids[pid]->io_filename) free(all_pids[pid]->io_filename);
-       if(all_pids[pid]->cmdline_filename) free(all_pids[pid]->cmdline_filename);
-       free(all_pids[pid]);
-
-       all_pids[pid] = NULL;
-       all_pids_count--;
+    if(!all_pids[pid]) {
+        error("attempted to free pid %d that is not allocated.", pid);
+        return;
+    }
+
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
+
+    if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
+    if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
+    if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
+
+    if(all_pids[pid]->fds) freez(all_pids[pid]->fds);
+    if(all_pids[pid]->stat_filename) freez(all_pids[pid]->stat_filename);
+    if(all_pids[pid]->statm_filename) freez(all_pids[pid]->statm_filename);
+    if(all_pids[pid]->io_filename) freez(all_pids[pid]->io_filename);
+    if(all_pids[pid]->cmdline_filename) freez(all_pids[pid]->cmdline_filename);
+    freez(all_pids[pid]);
+
+    all_pids[pid] = NULL;
+    all_pids_count--;
 }
 
 
@@ -633,162 +579,160 @@ void del_pid_entry(pid_t pid) {
 // update pids from proc
 
 int read_proc_pid_cmdline(struct pid_stat *p) {
-       
-       if(unlikely(!p->cmdline_filename)) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
-               if(!(p->cmdline_filename = strdup(filename)))
-                       fatal("Cannot allocate memory for filename '%s'", filename);
-       }
+    
+    if(unlikely(!p->cmdline_filename)) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
+        p->cmdline_filename = strdupz(filename);
+    }
 
-       int fd = open(p->cmdline_filename, O_RDONLY, 0666);
-       if(unlikely(fd == -1)) goto cleanup;
+    int fd = open(p->cmdline_filename, O_RDONLY, 0666);
+    if(unlikely(fd == -1)) goto cleanup;
 
-       int i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
-       close(fd);
+    ssize_t i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
+    close(fd);
 
-       if(unlikely(bytes <= 0)) goto cleanup;
+    if(unlikely(bytes < 0)) goto cleanup;
 
-       p->cmdline[bytes] = '\0';
-       for(i = 0; i < bytes ; i++)
-               if(unlikely(!p->cmdline[i])) p->cmdline[i] = ' ';
+    p->cmdline[bytes] = '\0';
+    for(i = 0; i < bytes ; i++)
+        if(unlikely(!p->cmdline[i])) p->cmdline[i] = ' ';
 
-       if(unlikely(debug))
-               fprintf(stderr, "Read file '%s' contents: %s\n", p->cmdline_filename, p->cmdline);
+    if(unlikely(debug))
+        fprintf(stderr, "Read file '%s' contents: %s\n", p->cmdline_filename, p->cmdline);
 
-       return 0;
+    return 1;
 
 cleanup:
-       // copy the command to the command line
-       strncpyz(p->cmdline, p->comm, MAX_CMDLINE);
-       return 0;
+    // copy the command to the command line
+    strncpyz(p->cmdline, p->comm, MAX_CMDLINE);
+    return 0;
 }
 
 int read_proc_pid_ownership(struct pid_stat *p) {
-       if(unlikely(!p->stat_filename)) {
-               error("pid %d does not have a stat_filename", p->pid);
-               return 1;
-       }
+    if(unlikely(!p->stat_filename)) {
+        error("pid %d does not have a stat_filename", p->pid);
+        return 0;
+    }
 
-       // ----------------------------------------
-       // read uid and gid
+    // ----------------------------------------
+    // read uid and gid
 
-       struct stat st;
-       if(stat(p->stat_filename, &st) != 0) {
-               error("Cannot stat file '%s'", p->stat_filename);
-               return 1;
-       }
+    struct stat st;
+    if(stat(p->stat_filename, &st) != 0) {
+        error("Cannot stat file '%s'", p->stat_filename);
+        return 1;
+    }
 
-       p->uid = st.st_uid;
-       p->gid = st.st_gid;
+    p->uid = st.st_uid;
+    p->gid = st.st_gid;
 
-       return 0;
+    return 1;
 }
 
 int read_proc_pid_stat(struct pid_stat *p) {
-       static procfile *ff = NULL;
-
-       if(unlikely(!p->stat_filename)) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
-               if(!(p->stat_filename = strdup(filename)))
-                       fatal("Cannot allocate memory for filename '%s'", filename);
-       }
-
-       int set_quotes = (!ff)?1:0;
-
-       ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
-       if(unlikely(!ff)) goto cleanup;
-
-       // if(set_quotes) procfile_set_quotes(ff, "()");
-       if(set_quotes) procfile_set_open_close(ff, "(", ")");
-
-       ff = procfile_readall(ff);
-       if(unlikely(!ff)) goto cleanup;
-
-       p->last_stat_collected_usec = p->stat_collected_usec;
-       p->stat_collected_usec = timems();
-       file_counter++;
-
-       // p->pid                       = atol(procfile_lineword(ff, 0, 0+i));
-
-       strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
-
-       // p->state                     = *(procfile_lineword(ff, 0, 2));
-       p->ppid                         = (int32_t) atol(procfile_lineword(ff, 0, 3));
-       // p->pgrp                      = atol(procfile_lineword(ff, 0, 4));
-       // p->session           = atol(procfile_lineword(ff, 0, 5));
-       // p->tty_nr            = atol(procfile_lineword(ff, 0, 6));
-       // p->tpgid                     = atol(procfile_lineword(ff, 0, 7));
-       // p->flags                     = strtoull(procfile_lineword(ff, 0, 8), NULL, 10);
-
-       unsigned long long last;
-
-       last = p->minflt_raw;
-       p->minflt_raw           = strtoull(procfile_lineword(ff, 0, 9), NULL, 10);
-       p->minflt = (p->minflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->cminflt_raw;
-       p->cminflt_raw          = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
-       p->cminflt = (p->cminflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->majflt_raw;
-       p->majflt_raw           = strtoull(procfile_lineword(ff, 0, 11), NULL, 10);
-       p->majflt = (p->majflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->cmajflt_raw;
-       p->cmajflt_raw          = strtoull(procfile_lineword(ff, 0, 12), NULL, 10);
-       p->cmajflt = (p->cmajflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->utime_raw;
-       p->utime_raw            = strtoull(procfile_lineword(ff, 0, 13), NULL, 10);
-       p->utime = (p->utime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->stime_raw;
-       p->stime_raw            = strtoull(procfile_lineword(ff, 0, 14), NULL, 10);
-       p->stime = (p->stime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->cutime_raw;
-       p->cutime_raw           = strtoull(procfile_lineword(ff, 0, 15), NULL, 10);
-       p->cutime = (p->cutime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       last = p->cstime_raw;
-       p->cstime_raw           = strtoull(procfile_lineword(ff, 0, 16), NULL, 10);
-       p->cstime = (p->cstime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-       // p->priority          = strtoull(procfile_lineword(ff, 0, 17), NULL, 10);
-       // p->nice                      = strtoull(procfile_lineword(ff, 0, 18), NULL, 10);
-       p->num_threads          = (int32_t) atol(procfile_lineword(ff, 0, 19));
-       // p->itrealvalue       = strtoull(procfile_lineword(ff, 0, 20), NULL, 10);
-       // p->starttime         = strtoull(procfile_lineword(ff, 0, 21), NULL, 10);
-       // p->vsize                     = strtoull(procfile_lineword(ff, 0, 22), NULL, 10);
-       p->rss                          = strtoull(procfile_lineword(ff, 0, 23), NULL, 10);
-       // p->rsslim            = strtoull(procfile_lineword(ff, 0, 24), NULL, 10);
-       // p->starcode          = strtoull(procfile_lineword(ff, 0, 25), NULL, 10);
-       // p->endcode           = strtoull(procfile_lineword(ff, 0, 26), NULL, 10);
-       // p->startstack        = strtoull(procfile_lineword(ff, 0, 27), NULL, 10);
-       // p->kstkesp           = strtoull(procfile_lineword(ff, 0, 28), NULL, 10);
-       // p->kstkeip           = strtoull(procfile_lineword(ff, 0, 29), NULL, 10);
-       // p->signal            = strtoull(procfile_lineword(ff, 0, 30), NULL, 10);
-       // p->blocked           = strtoull(procfile_lineword(ff, 0, 31), NULL, 10);
-       // p->sigignore         = strtoull(procfile_lineword(ff, 0, 32), NULL, 10);
-       // p->sigcatch          = strtoull(procfile_lineword(ff, 0, 33), NULL, 10);
-       // p->wchan                     = strtoull(procfile_lineword(ff, 0, 34), NULL, 10);
-       // p->nswap                     = strtoull(procfile_lineword(ff, 0, 35), NULL, 10);
-       // p->cnswap            = strtoull(procfile_lineword(ff, 0, 36), NULL, 10);
-       // p->exit_signal       = atol(procfile_lineword(ff, 0, 37));
-       // p->processor         = atol(procfile_lineword(ff, 0, 38));
-       // p->rt_priority       = strtoul(procfile_lineword(ff, 0, 39), NULL, 10);
-       // p->policy            = strtoul(procfile_lineword(ff, 0, 40), NULL, 10);
-       // p->delayacct_blkio_ticks = strtoull(procfile_lineword(ff, 0, 41), NULL, 10);
+    static procfile *ff = NULL;
+
+    if(unlikely(!p->stat_filename)) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
+        p->stat_filename = strdupz(filename);
+    }
+
+    int set_quotes = (!ff)?1:0;
+
+    ff = procfile_reopen(ff, p->stat_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+    if(unlikely(!ff)) goto cleanup;
+
+    // if(set_quotes) procfile_set_quotes(ff, "()");
+    if(set_quotes) procfile_set_open_close(ff, "(", ")");
+
+    ff = procfile_readall(ff);
+    if(unlikely(!ff)) goto cleanup;
+
+    p->last_stat_collected_usec = p->stat_collected_usec;
+    p->stat_collected_usec = time_usec();
+    file_counter++;
+
+    // p->pid           = atol(procfile_lineword(ff, 0, 0+i));
+
+    strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
+
+    // p->state         = *(procfile_lineword(ff, 0, 2));
+    p->ppid             = (int32_t) atol(procfile_lineword(ff, 0, 3));
+    // p->pgrp          = atol(procfile_lineword(ff, 0, 4));
+    // p->session       = atol(procfile_lineword(ff, 0, 5));
+    // p->tty_nr        = atol(procfile_lineword(ff, 0, 6));
+    // p->tpgid         = atol(procfile_lineword(ff, 0, 7));
+    // p->flags         = strtoull(procfile_lineword(ff, 0, 8), NULL, 10);
+
+    unsigned long long last;
+
+    last = p->minflt_raw;
+    p->minflt_raw       = strtoull(procfile_lineword(ff, 0, 9), NULL, 10);
+    p->minflt = (p->minflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->cminflt_raw;
+    p->cminflt_raw      = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
+    p->cminflt = (p->cminflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->majflt_raw;
+    p->majflt_raw       = strtoull(procfile_lineword(ff, 0, 11), NULL, 10);
+    p->majflt = (p->majflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->cmajflt_raw;
+    p->cmajflt_raw      = strtoull(procfile_lineword(ff, 0, 12), NULL, 10);
+    p->cmajflt = (p->cmajflt_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->utime_raw;
+    p->utime_raw        = strtoull(procfile_lineword(ff, 0, 13), NULL, 10);
+    p->utime = (p->utime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->stime_raw;
+    p->stime_raw        = strtoull(procfile_lineword(ff, 0, 14), NULL, 10);
+    p->stime = (p->stime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->cutime_raw;
+    p->cutime_raw       = strtoull(procfile_lineword(ff, 0, 15), NULL, 10);
+    p->cutime = (p->cutime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    last = p->cstime_raw;
+    p->cstime_raw       = strtoull(procfile_lineword(ff, 0, 16), NULL, 10);
+    p->cstime = (p->cstime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+    // p->priority      = strtoull(procfile_lineword(ff, 0, 17), NULL, 10);
+    // p->nice          = strtoull(procfile_lineword(ff, 0, 18), NULL, 10);
+    p->num_threads      = (int32_t) atol(procfile_lineword(ff, 0, 19));
+    // p->itrealvalue   = strtoull(procfile_lineword(ff, 0, 20), NULL, 10);
+    // p->starttime     = strtoull(procfile_lineword(ff, 0, 21), NULL, 10);
+    // p->vsize         = strtoull(procfile_lineword(ff, 0, 22), NULL, 10);
+    p->rss              = strtoull(procfile_lineword(ff, 0, 23), NULL, 10);
+    // p->rsslim        = strtoull(procfile_lineword(ff, 0, 24), NULL, 10);
+    // p->starcode      = strtoull(procfile_lineword(ff, 0, 25), NULL, 10);
+    // p->endcode       = strtoull(procfile_lineword(ff, 0, 26), NULL, 10);
+    // p->startstack    = strtoull(procfile_lineword(ff, 0, 27), NULL, 10);
+    // p->kstkesp       = strtoull(procfile_lineword(ff, 0, 28), NULL, 10);
+    // p->kstkeip       = strtoull(procfile_lineword(ff, 0, 29), NULL, 10);
+    // p->signal        = strtoull(procfile_lineword(ff, 0, 30), NULL, 10);
+    // p->blocked       = strtoull(procfile_lineword(ff, 0, 31), NULL, 10);
+    // p->sigignore     = strtoull(procfile_lineword(ff, 0, 32), NULL, 10);
+    // p->sigcatch      = strtoull(procfile_lineword(ff, 0, 33), NULL, 10);
+    // p->wchan         = strtoull(procfile_lineword(ff, 0, 34), NULL, 10);
+    // p->nswap         = strtoull(procfile_lineword(ff, 0, 35), NULL, 10);
+    // p->cnswap        = strtoull(procfile_lineword(ff, 0, 36), NULL, 10);
+    // p->exit_signal   = atol(procfile_lineword(ff, 0, 37));
+    // p->processor     = atol(procfile_lineword(ff, 0, 38));
+    // p->rt_priority   = strtoul(procfile_lineword(ff, 0, 39), NULL, 10);
+    // p->policy        = strtoul(procfile_lineword(ff, 0, 40), NULL, 10);
+    // p->delayacct_blkio_ticks = strtoull(procfile_lineword(ff, 0, 41), NULL, 10);
 
     if(enable_guest_charts) {
         last = p->gtime_raw;
-        p->gtime_raw           = strtoull(procfile_lineword(ff, 0, 42), NULL, 10);
+        p->gtime_raw        = strtoull(procfile_lineword(ff, 0, 42), NULL, 10);
         p->gtime = (p->gtime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
 
         last = p->cgtime_raw;
-        p->cgtime_raw          = strtoull(procfile_lineword(ff, 0, 43), NULL, 10);
+        p->cgtime_raw       = strtoull(procfile_lineword(ff, 0, 43), NULL, 10);
         p->cgtime = (p->cgtime_raw - last) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
 
         if (show_guest_time || p->gtime || p->cgtime) {
@@ -798,152 +742,150 @@ int read_proc_pid_stat(struct pid_stat *p) {
         }
     }
 
-       if(unlikely(debug || (p->target && p->target->debug)))
-               fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
-
-       if(unlikely(global_iterations_counter == 1)) {
-               p->minflt                       = 0;
-               p->cminflt                      = 0;
-               p->majflt                       = 0;
-               p->cmajflt                      = 0;
-               p->utime                        = 0;
-               p->stime                        = 0;
-               p->gtime                        = 0;
-               p->cutime                       = 0;
-               p->cstime                       = 0;
-               p->cgtime                       = 0;
-       }
+    if(unlikely(debug || (p->target && p->target->debug)))
+        fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
+
+    if(unlikely(global_iterations_counter == 1)) {
+        p->minflt           = 0;
+        p->cminflt          = 0;
+        p->majflt           = 0;
+        p->cmajflt          = 0;
+        p->utime            = 0;
+        p->stime            = 0;
+        p->gtime            = 0;
+        p->cutime           = 0;
+        p->cstime           = 0;
+        p->cgtime           = 0;
+    }
 
-       return 0;
+    return 1;
 
 cleanup:
-       p->minflt                       = 0;
-       p->cminflt                      = 0;
-       p->majflt                       = 0;
-       p->cmajflt                      = 0;
-       p->utime                        = 0;
-       p->stime                        = 0;
-       p->gtime                        = 0;
-       p->cutime                       = 0;
-       p->cstime                       = 0;
-       p->cgtime                       = 0;
-       p->num_threads          = 0;
-       p->rss                          = 0;
-       return 1;
+    p->minflt           = 0;
+    p->cminflt          = 0;
+    p->majflt           = 0;
+    p->cmajflt          = 0;
+    p->utime            = 0;
+    p->stime            = 0;
+    p->gtime            = 0;
+    p->cutime           = 0;
+    p->cstime           = 0;
+    p->cgtime           = 0;
+    p->num_threads      = 0;
+    p->rss              = 0;
+    return 0;
 }
 
 int read_proc_pid_statm(struct pid_stat *p) {
-       static procfile *ff = NULL;
+    static procfile *ff = NULL;
 
-       if(unlikely(!p->statm_filename)) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
-               if(!(p->statm_filename = strdup(filename)))
-                       fatal("Cannot allocate memory for filename '%s'", filename);
-       }
+    if(unlikely(!p->statm_filename)) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
+        p->statm_filename = strdupz(filename);
+    }
 
-       ff = procfile_reopen(ff, p->statm_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
-       if(unlikely(!ff)) goto cleanup;
+    ff = procfile_reopen(ff, p->statm_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+    if(unlikely(!ff)) goto cleanup;
 
-       ff = procfile_readall(ff);
-       if(unlikely(!ff)) goto cleanup;
+    ff = procfile_readall(ff);
+    if(unlikely(!ff)) goto cleanup;
 
-       file_counter++;
+    file_counter++;
 
-       p->statm_size                   = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
-       p->statm_resident               = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
-       p->statm_share                  = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
-       p->statm_text                   = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
-       p->statm_lib                    = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
-       p->statm_data                   = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
-       p->statm_dirty                  = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
+    p->statm_size           = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
+    p->statm_resident       = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+    p->statm_share          = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
+    p->statm_text           = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+    p->statm_lib            = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
+    p->statm_data           = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
+    p->statm_dirty          = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
 
-       return 0;
+    return 1;
 
 cleanup:
-       p->statm_size                   = 0;
-       p->statm_resident               = 0;
-       p->statm_share                  = 0;
-       p->statm_text                   = 0;
-       p->statm_lib                    = 0;
-       p->statm_data                   = 0;
-       p->statm_dirty                  = 0;
-       return 1;
+    p->statm_size           = 0;
+    p->statm_resident       = 0;
+    p->statm_share          = 0;
+    p->statm_text           = 0;
+    p->statm_lib            = 0;
+    p->statm_data           = 0;
+    p->statm_dirty          = 0;
+    return 0;
 }
 
 int read_proc_pid_io(struct pid_stat *p) {
-       static procfile *ff = NULL;
+    static procfile *ff = NULL;
 
-       if(unlikely(!p->io_filename)) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
-               if(!(p->io_filename = strdup(filename)))
-                       fatal("Cannot allocate memory for filename '%s'", filename);
-       }
+    if(unlikely(!p->io_filename)) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
+        p->io_filename = strdupz(filename);
+    }
 
-       // open the file
-       ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
-       if(unlikely(!ff)) goto cleanup;
+    // open the file
+    ff = procfile_reopen(ff, p->io_filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+    if(unlikely(!ff)) goto cleanup;
 
-       ff = procfile_readall(ff);
-       if(unlikely(!ff)) goto cleanup;
+    ff = procfile_readall(ff);
+    if(unlikely(!ff)) goto cleanup;
 
-       file_counter++;
+    file_counter++;
 
-       p->last_io_collected_usec = p->io_collected_usec;
-       p->io_collected_usec = timems();
+    p->last_io_collected_usec = p->io_collected_usec;
+    p->io_collected_usec = time_usec();
 
-       unsigned long long last;
+    unsigned long long last;
 
-       last = p->io_logical_bytes_read_raw;
-       p->io_logical_bytes_read_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
-       p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_logical_bytes_read_raw;
+    p->io_logical_bytes_read_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+    p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       last = p->io_logical_bytes_written_raw;
-       p->io_logical_bytes_written_raw = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
-       p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_logical_bytes_written_raw;
+    p->io_logical_bytes_written_raw = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
+    p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       last = p->io_read_calls_raw;
-       p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
-       p->io_read_calls = (p->io_read_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_read_calls_raw;
+    p->io_read_calls_raw = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
+    p->io_read_calls = (p->io_read_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       last = p->io_write_calls_raw;
-       p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
-       p->io_write_calls = (p->io_write_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_write_calls_raw;
+    p->io_write_calls_raw = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
+    p->io_write_calls = (p->io_write_calls_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       last = p->io_storage_bytes_read_raw;
-       p->io_storage_bytes_read_raw = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
-       p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_storage_bytes_read_raw;
+    p->io_storage_bytes_read_raw = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
+    p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       last = p->io_storage_bytes_written_raw;
-       p->io_storage_bytes_written_raw = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
-       p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_storage_bytes_written_raw;
+    p->io_storage_bytes_written_raw = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
+    p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       last = p->io_cancelled_write_bytes_raw;
-       p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
-       p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
+    last = p->io_cancelled_write_bytes_raw;
+    p->io_cancelled_write_bytes_raw = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
+    p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (1000000ULL * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec);
 
-       if(unlikely(global_iterations_counter == 1)) {
-               p->io_logical_bytes_read                = 0;
-               p->io_logical_bytes_written     = 0;
-               p->io_read_calls                                = 0;
-               p->io_write_calls                               = 0;
-               p->io_storage_bytes_read                = 0;
-               p->io_storage_bytes_written     = 0;
-               p->io_cancelled_write_bytes             = 0;
-       }
+    if(unlikely(global_iterations_counter == 1)) {
+        p->io_logical_bytes_read        = 0;
+        p->io_logical_bytes_written     = 0;
+        p->io_read_calls                = 0;
+        p->io_write_calls               = 0;
+        p->io_storage_bytes_read        = 0;
+        p->io_storage_bytes_written     = 0;
+        p->io_cancelled_write_bytes     = 0;
+    }
 
-       return 0;
+    return 1;
 
 cleanup:
-       p->io_logical_bytes_read                = 0;
-       p->io_logical_bytes_written     = 0;
-       p->io_read_calls                                = 0;
-       p->io_write_calls                               = 0;
-       p->io_storage_bytes_read                = 0;
-       p->io_storage_bytes_written     = 0;
-       p->io_cancelled_write_bytes             = 0;
-       return 1;
+    p->io_logical_bytes_read        = 0;
+    p->io_logical_bytes_written     = 0;
+    p->io_read_calls                = 0;
+    p->io_write_calls               = 0;
+    p->io_storage_bytes_read        = 0;
+    p->io_storage_bytes_written     = 0;
+    p->io_cancelled_write_bytes     = 0;
+    return 0;
 }
 
 unsigned long long global_utime = 0;
@@ -951,38 +893,38 @@ unsigned long long global_stime = 0;
 unsigned long long global_gtime = 0;
 
 int read_proc_stat() {
-       static char filename[FILENAME_MAX + 1] = "";
-       static procfile *ff = NULL;
-       static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0, collected_usec = 0, last_collected_usec = 0;
-
-       if(unlikely(!ff)) {
-               snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
-               ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
-               if(unlikely(!ff)) goto cleanup;
-       }
+    static char filename[FILENAME_MAX + 1] = "";
+    static procfile *ff = NULL;
+    static unsigned long long utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0, collected_usec = 0, last_collected_usec = 0;
+
+    if(unlikely(!ff)) {
+        snprintfz(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
+        ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
+        if(unlikely(!ff)) goto cleanup;
+    }
 
-       ff = procfile_readall(ff);
-       if(unlikely(!ff)) goto cleanup;
+    ff = procfile_readall(ff);
+    if(unlikely(!ff)) goto cleanup;
 
-       last_collected_usec = collected_usec;
-       collected_usec = timems();
+    last_collected_usec = collected_usec;
+    collected_usec = time_usec();
 
-       file_counter++;
+    file_counter++;
 
-       unsigned long long last;
+    unsigned long long last;
 
-       last = utime_raw;
-       utime_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
-       global_utime = (utime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
+    last = utime_raw;
+    utime_raw = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+    global_utime = (utime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
 
     // nice time, on user time
-       last = ntime_raw;
-       ntime_raw = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
-       global_utime += (ntime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
+    last = ntime_raw;
+    ntime_raw = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
+    global_utime += (ntime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
 
-       last = stime_raw;
-       stime_raw = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
-       global_stime = (stime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
+    last = stime_raw;
+    stime_raw = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+    global_stime = (stime_raw - last) * (1000000ULL * RATES_DETAIL) / (collected_usec - last_collected_usec);
 
     last = gtime_raw;
     gtime_raw = strtoull(procfile_lineword(ff, 0, 10), NULL, 10);
@@ -998,19 +940,19 @@ int read_proc_stat() {
         global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime;
     }
 
-       if(unlikely(global_iterations_counter == 1)) {
-               global_utime = 0;
-               global_stime = 0;
-               global_gtime = 0;
-       }
+    if(unlikely(global_iterations_counter == 1)) {
+        global_utime = 0;
+        global_stime = 0;
+        global_gtime = 0;
+    }
 
-       return 0;
+    return 1;
 
 cleanup:
-       global_utime = 0;
-       global_stime = 0;
-       global_gtime = 0;
-       return 1;
+    global_utime = 0;
+    global_stime = 0;
+    global_gtime = 0;
+    return 0;
 }
 
 
@@ -1022,15 +964,15 @@ cleanup:
 #define FILE_DESCRIPTORS_INCREASE_STEP 100
 
 struct file_descriptor {
-       avl avl;
+    avl avl;
 #ifdef NETDATA_INTERNAL_CHECKS
-       uint32_t magic;
+    uint32_t magic;
 #endif /* NETDATA_INTERNAL_CHECKS */
-       uint32_t hash;
-       const char *name;
-       int type;
-       int count;
-       int pos;
+    uint32_t hash;
+    const char *name;
+    int type;
+    int count;
+    int pos;
 } *all_files = NULL;
 
 int all_files_len = 0;
@@ -1038,38 +980,38 @@ int all_files_size = 0;
 
 int file_descriptor_compare(void* a, void* b) {
 #ifdef NETDATA_INTERNAL_CHECKS
-       if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
-               error("Corrupted index data detected. Please report this.");
+    if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
+        error("Corrupted index data detected. Please report this.");
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-       if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
-               return -1;
+    if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
+        return -1;
 
-       else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
-               return 1;
+    else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
+        return 1;
 
-       else
-               return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
+    else
+        return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
 }
 
 int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
 
 avl_tree all_files_index = {
-               NULL,
-               file_descriptor_compare
+        NULL,
+        file_descriptor_compare
 };
 
 static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
-       struct file_descriptor tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = name;
-       tmp.count = 0;
-       tmp.pos = 0;
+    struct file_descriptor tmp;
+    tmp.hash = (hash)?hash:simple_hash(name);
+    tmp.name = name;
+    tmp.count = 0;
+    tmp.pos = 0;
 #ifdef NETDATA_INTERNAL_CHECKS
-       tmp.magic = 0x0BADCAFE;
+    tmp.magic = 0x0BADCAFE;
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-       return (struct file_descriptor *)avl_search(&all_files_index, (avl *) &tmp);
+    return (struct file_descriptor *)avl_search(&all_files_index, (avl *) &tmp);
 }
 
 #define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd))
@@ -1087,526 +1029,526 @@ static struct file_descriptor *file_descriptor_find(const char *name, uint32_t h
 
 void file_descriptor_not_used(int id)
 {
-       if(id > 0 && id < all_files_size) {
+    if(id > 0 && id < all_files_size) {
 
 #ifdef NETDATA_INTERNAL_CHECKS
-               if(all_files[id].magic != 0x0BADCAFE) {
-                       error("Ignoring request to remove empty file id %d.", id);
-                       return;
-               }
+        if(all_files[id].magic != 0x0BADCAFE) {
+            error("Ignoring request to remove empty file id %d.", id);
+            return;
+        }
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count);
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: decreasing slot %d (count = %d).\n", id, all_files[id].count);
 
-               if(all_files[id].count > 0) {
-                       all_files[id].count--;
+        if(all_files[id].count > 0) {
+            all_files[id].count--;
 
-                       if(!all_files[id].count) {
-                               if(unlikely(debug))
-                                       fprintf(stderr, "apps.plugin:   >> slot %d is empty.\n", id);
+            if(!all_files[id].count) {
+                if(unlikely(debug))
+                    fprintf(stderr, "apps.plugin:   >> slot %d is empty.\n", id);
 
-                               file_descriptor_remove(&all_files[id]);
+                file_descriptor_remove(&all_files[id]);
 #ifdef NETDATA_INTERNAL_CHECKS
-                               all_files[id].magic = 0x00000000;
+                all_files[id].magic = 0x00000000;
 #endif /* NETDATA_INTERNAL_CHECKS */
-                               all_files_len--;
-                       }
-               }
-               else
-                       error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
-       }
-       else    error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
+                all_files_len--;
+            }
+        }
+        else
+            error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
+    }
+    else    error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
 }
 
 int file_descriptor_find_or_add(const char *name)
 {
-       static int last_pos = 0;
-       uint32_t hash = simple_hash(name);
-
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
-
-       struct file_descriptor *fd = file_descriptor_find(name, hash);
-       if(fd) {
-               // found
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin:   >> found on slot %d\n", fd->pos);
-
-               fd->count++;
-               return fd->pos;
-       }
-       // not found
-
-       // check we have enough memory to add it
-       if(!all_files || all_files_len == all_files_size) {
-               void *old = all_files;
-               int i;
-
-               // there is no empty slot
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
-
-               all_files = realloc(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
-
-               // if the address changed, we have to rebuild the index
-               // since all pointers are now invalid
-               if(old && old != (void *)all_files) {
-                       if(unlikely(debug))
-                               fprintf(stderr, "apps.plugin:   >> re-indexing.\n");
-
-                       all_files_index.root = NULL;
-                       for(i = 0; i < all_files_size; i++) {
-                               if(!all_files[i].count) continue;
-                               file_descriptor_add(&all_files[i]);
-                       }
-
-                       if(unlikely(debug))
-                               fprintf(stderr, "apps.plugin:   >> re-indexing done.\n");
-               }
-
-               for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
-                       all_files[i].count = 0;
-                       all_files[i].name = NULL;
+    static int last_pos = 0;
+    uint32_t hash = simple_hash(name);
+
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
+
+    struct file_descriptor *fd = file_descriptor_find(name, hash);
+    if(fd) {
+        // found
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin:   >> found on slot %d\n", fd->pos);
+
+        fd->count++;
+        return fd->pos;
+    }
+    // not found
+
+    // check we have enough memory to add it
+    if(!all_files || all_files_len == all_files_size) {
+        void *old = all_files;
+        int i;
+
+        // there is no empty slot
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
+
+        all_files = reallocz(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
+
+        // if the address changed, we have to rebuild the index
+        // since all pointers are now invalid
+        if(old && old != (void *)all_files) {
+            if(unlikely(debug))
+                fprintf(stderr, "apps.plugin:   >> re-indexing.\n");
+
+            all_files_index.root = NULL;
+            for(i = 0; i < all_files_size; i++) {
+                if(!all_files[i].count) continue;
+                file_descriptor_add(&all_files[i]);
+            }
+
+            if(unlikely(debug))
+                fprintf(stderr, "apps.plugin:   >> re-indexing done.\n");
+        }
+
+        for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
+            all_files[i].count = 0;
+            all_files[i].name = NULL;
 #ifdef NETDATA_INTERNAL_CHECKS
-                       all_files[i].magic = 0x00000000;
+            all_files[i].magic = 0x00000000;
 #endif /* NETDATA_INTERNAL_CHECKS */
-                       all_files[i].pos = i;
-               }
+            all_files[i].pos = i;
+        }
 
-               if(!all_files_size) all_files_len = 1;
-               all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
-       }
+        if(!all_files_size) all_files_len = 1;
+        all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
+    }
 
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin:   >> searching for empty slot.\n");
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin:   >> searching for empty slot.\n");
 
-       // search for an empty slot
-       int i, c;
-       for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
-               if(c >= all_files_size) c = 0;
-               if(c == 0) continue;
+    // search for an empty slot
+    int i, c;
+    for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
+        if(c >= all_files_size) c = 0;
+        if(c == 0) continue;
 
-               if(!all_files[c].count) {
-                       if(unlikely(debug))
-                               fprintf(stderr, "apps.plugin:   >> Examining slot %d.\n", c);
+        if(!all_files[c].count) {
+            if(unlikely(debug))
+                fprintf(stderr, "apps.plugin:   >> Examining slot %d.\n", c);
 
 #ifdef NETDATA_INTERNAL_CHECKS
-                       if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
-                               error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
+            if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
+                error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-                       if(unlikely(debug))
-                               fprintf(stderr, "apps.plugin:   >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
-
-                       if(all_files[c].name) free((void *)all_files[c].name);
-                       all_files[c].name = NULL;
-                       last_pos = c;
-                       break;
-               }
-       }
-       if(i == all_files_size) {
-               fatal("We should find an empty slot, but there isn't any");
-               exit(1);
-       }
-
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin:   >> updating slot %d.\n", c);
-
-       all_files_len++;
-
-       // else we have an empty slot in 'c'
-
-       int type;
-       if(name[0] == '/') type = FILETYPE_FILE;
-       else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
-       else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
-       else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
-       else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
-       else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
-       else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
-       else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
-       else if(strncmp(name, "anon_inode:", 11) == 0) {
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
-
-               type = FILETYPE_OTHER;
-       }
-       else {
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
-
-               type = FILETYPE_OTHER;
-       }
-
-       all_files[c].name = strdup(name);
-       all_files[c].hash = hash;
-       all_files[c].type = type;
-       all_files[c].pos  = c;
-       all_files[c].count = 1;
+            if(unlikely(debug))
+                fprintf(stderr, "apps.plugin:   >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
+
+            if(all_files[c].name) freez((void *)all_files[c].name);
+            all_files[c].name = NULL;
+            last_pos = c;
+            break;
+        }
+    }
+    if(i == all_files_size) {
+        fatal("We should find an empty slot, but there isn't any");
+        exit(1);
+    }
+
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin:   >> updating slot %d.\n", c);
+
+    all_files_len++;
+
+    // else we have an empty slot in 'c'
+
+    int type;
+    if(name[0] == '/') type = FILETYPE_FILE;
+    else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
+    else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
+    else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
+    else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
+    else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
+    else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
+    else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
+    else if(strncmp(name, "anon_inode:", 11) == 0) {
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
+
+        type = FILETYPE_OTHER;
+    }
+    else {
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
+
+        type = FILETYPE_OTHER;
+    }
+
+    all_files[c].name = strdupz(name);
+    all_files[c].hash = hash;
+    all_files[c].type = type;
+    all_files[c].pos  = c;
+    all_files[c].count = 1;
 #ifdef NETDATA_INTERNAL_CHECKS
-       all_files[c].magic = 0x0BADCAFE;
+    all_files[c].magic = 0x0BADCAFE;
 #endif /* NETDATA_INTERNAL_CHECKS */
-       file_descriptor_add(&all_files[c]);
+    file_descriptor_add(&all_files[c]);
 
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
 
-       return c;
+    return c;
 }
 
 int read_pid_file_descriptors(struct pid_stat *p) {
-       char dirname[FILENAME_MAX+1];
-
-       snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", host_prefix, p->pid);
-       DIR *fds = opendir(dirname);
-       if(fds) {
-               int c;
-               struct dirent *de;
-               char fdname[FILENAME_MAX + 1];
-               char linkname[FILENAME_MAX + 1];
-
-               // make the array negative
-               for(c = 0 ; c < p->fds_size ; c++)
-                       p->fds[c] = -p->fds[c];
-
-               while((de = readdir(fds))) {
-                       if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
-                               continue;
-
-                       // check if the fds array is small
-                       int fdid = atoi(de->d_name);
-                       if(fdid < 0) continue;
-                       if(fdid >= p->fds_size) {
-                               // it is small, extend it
-                               if(unlikely(debug))
-                                       fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
-
-                               p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
-                               if(!p->fds) {
-                                       fatal("Cannot re-allocate fds for %s", p->comm);
-                                       break;
-                               }
-
-                               // and initialize it
-                               for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
-                               p->fds_size = fdid + 100;
-                       }
-
-                       if(p->fds[fdid] == 0) {
-                               // we don't know this fd, get it
-
-                               sprintf(fdname, "%s/proc/%d/fd/%s", host_prefix, p->pid, de->d_name);
-                               ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
-                               if(l == -1) {
-                                       if(debug || (p->target && p->target->debug)) {
-                                               if(debug || (p->target && p->target->debug))
-                                                       error("Cannot read link %s", fdname);
-                                       }
-                                       continue;
-                               }
-                               linkname[l] = '\0';
-                               file_counter++;
-
-                               // if another process already has this, we will get
-                               // the same id
-                               p->fds[fdid] = file_descriptor_find_or_add(linkname);
-                       }
-
-                       // else make it positive again, we need it
-                       // of course, the actual file may have changed, but we don't care so much
-                       // FIXME: we could compare the inode as returned by readdir direct structure
-                       else p->fds[fdid] = -p->fds[fdid];
-               }
-               closedir(fds);
-
-               // remove all the negative file descriptors
-               for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
-                       file_descriptor_not_used(-p->fds[c]);
-                       p->fds[c] = 0;
-               }
-       }
-       else return 1;
-
-       return 0;
+    char dirname[FILENAME_MAX+1];
+
+    snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", host_prefix, p->pid);
+    DIR *fds = opendir(dirname);
+    if(fds) {
+        int c;
+        struct dirent *de;
+        char fdname[FILENAME_MAX + 1];
+        char linkname[FILENAME_MAX + 1];
+
+        // make the array negative
+        for(c = 0 ; c < p->fds_size ; c++)
+            p->fds[c] = -p->fds[c];
+
+        while((de = readdir(fds))) {
+            if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+                continue;
+
+            // check if the fds array is small
+            int fdid = atoi(de->d_name);
+            if(fdid < 0) continue;
+            if(fdid >= p->fds_size) {
+                // it is small, extend it
+                if(unlikely(debug))
+                    fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
+
+                p->fds = reallocz(p->fds, (fdid + 100) * sizeof(int));
+                if(!p->fds) {
+                    fatal("Cannot re-allocate fds for %s", p->comm);
+                    break;
+                }
+
+                // and initialize it
+                for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
+                p->fds_size = fdid + 100;
+            }
+
+            if(p->fds[fdid] == 0) {
+                // we don't know this fd, get it
+
+                sprintf(fdname, "%s/proc/%d/fd/%s", host_prefix, p->pid, de->d_name);
+                ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
+                if(l == -1) {
+                    if(debug || (p->target && p->target->debug)) {
+                        if(debug || (p->target && p->target->debug))
+                            error("Cannot read link %s", fdname);
+                    }
+                    continue;
+                }
+                linkname[l] = '\0';
+                file_counter++;
+
+                // if another process already has this, we will get
+                // the same id
+                p->fds[fdid] = file_descriptor_find_or_add(linkname);
+            }
+
+            // else make it positive again, we need it
+            // of course, the actual file may have changed, but we don't care so much
+            // FIXME: we could compare the inode as returned by readdir dirent structure
+            else p->fds[fdid] = -p->fds[fdid];
+        }
+        closedir(fds);
+
+        // remove all the negative file descriptors
+        for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
+            file_descriptor_not_used(-p->fds[c]);
+            p->fds[c] = 0;
+        }
+    }
+    else return 0;
+
+    return 1;
 }
 
 // ----------------------------------------------------------------------------
 
 int print_process_and_parents(struct pid_stat *p, unsigned long long time) {
-       char *prefix = "\\_ ";
-       int indent = 0;
-
-       if(p->parent)
-               indent = print_process_and_parents(p->parent, p->stat_collected_usec);
-       else
-               prefix = " > ";
-
-       char buffer[indent + 1];
-       int i;
-
-       for(i = 0; i < indent ;i++) buffer[i] = ' ';
-       buffer[i] = '\0';
-
-       fprintf(stderr, "  %s %s%s (%d %s %lld"
-               , buffer
-               , prefix
-               , p->comm
-               , p->pid
-               , p->updated?"running":"exited"
-               , (long long)p->stat_collected_usec - (long long)time
-               );
-
-       if(p->utime)   fprintf(stderr, " utime=%llu",   p->utime);
-       if(p->stime)   fprintf(stderr, " stime=%llu",   p->stime);
-       if(p->gtime)   fprintf(stderr, " gtime=%llu",   p->gtime);
-       if(p->cutime)  fprintf(stderr, " cutime=%llu",  p->cutime);
-       if(p->cstime)  fprintf(stderr, " cstime=%llu",  p->cstime);
-       if(p->cgtime)  fprintf(stderr, " cgtime=%llu",  p->cgtime);
-       if(p->minflt)  fprintf(stderr, " minflt=%llu",  p->minflt);
-       if(p->cminflt) fprintf(stderr, " cminflt=%llu", p->cminflt);
-       if(p->majflt)  fprintf(stderr, " majflt=%llu",  p->majflt);
-       if(p->cmajflt) fprintf(stderr, " cmajflt=%llu", p->cmajflt);
-       fprintf(stderr, ")\n");
-
-       return indent + 1;
+    char *prefix = "\\_ ";
+    int indent = 0;
+
+    if(p->parent)
+        indent = print_process_and_parents(p->parent, p->stat_collected_usec);
+    else
+        prefix = " > ";
+
+    char buffer[indent + 1];
+    int i;
+
+    for(i = 0; i < indent ;i++) buffer[i] = ' ';
+    buffer[i] = '\0';
+
+    fprintf(stderr, "  %s %s%s (%d %s %lld"
+        , buffer
+        , prefix
+        , p->comm
+        , p->pid
+        , p->updated?"running":"exited"
+        , (long long)p->stat_collected_usec - (long long)time
+        );
+
+    if(p->utime)   fprintf(stderr, " utime=%llu",   p->utime);
+    if(p->stime)   fprintf(stderr, " stime=%llu",   p->stime);
+    if(p->gtime)   fprintf(stderr, " gtime=%llu",   p->gtime);
+    if(p->cutime)  fprintf(stderr, " cutime=%llu",  p->cutime);
+    if(p->cstime)  fprintf(stderr, " cstime=%llu",  p->cstime);
+    if(p->cgtime)  fprintf(stderr, " cgtime=%llu",  p->cgtime);
+    if(p->minflt)  fprintf(stderr, " minflt=%llu",  p->minflt);
+    if(p->cminflt) fprintf(stderr, " cminflt=%llu", p->cminflt);
+    if(p->majflt)  fprintf(stderr, " majflt=%llu",  p->majflt);
+    if(p->cmajflt) fprintf(stderr, " cmajflt=%llu", p->cmajflt);
+    fprintf(stderr, ")\n");
+
+    return indent + 1;
 }
 
 void print_process_tree(struct pid_stat *p, char *msg) {
-       log_date(stderr);
-       fprintf(stderr, "%s: process %s (%d, %s) with parents:\n", msg, p->comm, p->pid, p->updated?"running":"exited");
-       print_process_and_parents(p, p->stat_collected_usec);
+    log_date(stderr);
+    fprintf(stderr, "%s: process %s (%d, %s) with parents:\n", msg, p->comm, p->pid, p->updated?"running":"exited");
+    print_process_and_parents(p, p->stat_collected_usec);
 }
 
 void find_lost_child_debug(struct pid_stat *pe, unsigned long long lost, int type) {
-       int found = 0;
-       struct pid_stat *p = NULL;
-
-       for(p = root_of_pids; p ; p = p->next) {
-               if(p == pe) continue;
-
-               switch(type) {
-                       case 1:
-                               if(p->cminflt > lost) {
-                                       fprintf(stderr, " > process %d (%s) could use the lost exited child minflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
-                                       found++;
-                               }
-                               break;
-                               
-                       case 2:
-                               if(p->cmajflt > lost) {
-                                       fprintf(stderr, " > process %d (%s) could use the lost exited child majflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
-                                       found++;
-                               }
-                               break;
-                               
-                       case 3:
-                               if(p->cutime > lost) {
-                                       fprintf(stderr, " > process %d (%s) could use the lost exited child utime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
-                                       found++;
-                               }
-                               break;
-
-                       case 4:
-                               if(p->cstime > lost) {
-                                       fprintf(stderr, " > process %d (%s) could use the lost exited child stime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
-                                       found++;
-                               }
-                               break;
-
-                       case 5:
-                               if(p->cgtime > lost) {
-                                       fprintf(stderr, " > process %d (%s) could use the lost exited child gtime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
-                                       found++;
-                               }
-                               break;
-               }
-       }
-
-       if(!found) {
-               switch(type) {
-                       case 1:
-                               fprintf(stderr, " > cannot find any process to use the lost exited child minflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
-                               break;
-                               
-                       case 2:
-                               fprintf(stderr, " > cannot find any process to use the lost exited child majflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
-                               break;
-                               
-                       case 3:
-                               fprintf(stderr, " > cannot find any process to use the lost exited child utime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
-                               break;
-
-                       case 4:
-                               fprintf(stderr, " > cannot find any process to use the lost exited child stime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
-                               break;
-
-                       case 5:
-                               fprintf(stderr, " > cannot find any process to use the lost exited child gtime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
-                               break;
-               }
-       }
+    int found = 0;
+    struct pid_stat *p = NULL;
+
+    for(p = root_of_pids; p ; p = p->next) {
+        if(p == pe) continue;
+
+        switch(type) {
+            case 1:
+                if(p->cminflt > lost) {
+                    fprintf(stderr, " > process %d (%s) could use the lost exited child minflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+                    found++;
+                }
+                break;
+                
+            case 2:
+                if(p->cmajflt > lost) {
+                    fprintf(stderr, " > process %d (%s) could use the lost exited child majflt %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+                    found++;
+                }
+                break;
+                
+            case 3:
+                if(p->cutime > lost) {
+                    fprintf(stderr, " > process %d (%s) could use the lost exited child utime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+                    found++;
+                }
+                break;
+
+            case 4:
+                if(p->cstime > lost) {
+                    fprintf(stderr, " > process %d (%s) could use the lost exited child stime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+                    found++;
+                }
+                break;
+
+            case 5:
+                if(p->cgtime > lost) {
+                    fprintf(stderr, " > process %d (%s) could use the lost exited child gtime %llu of process %d (%s)\n", p->pid, p->comm, lost, pe->pid, pe->comm);
+                    found++;
+                }
+                break;
+        }
+    }
+
+    if(!found) {
+        switch(type) {
+            case 1:
+                fprintf(stderr, " > cannot find any process to use the lost exited child minflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+                break;
+                
+            case 2:
+                fprintf(stderr, " > cannot find any process to use the lost exited child majflt %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+                break;
+                
+            case 3:
+                fprintf(stderr, " > cannot find any process to use the lost exited child utime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+                break;
+
+            case 4:
+                fprintf(stderr, " > cannot find any process to use the lost exited child stime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+                break;
+
+            case 5:
+                fprintf(stderr, " > cannot find any process to use the lost exited child gtime %llu of process %d (%s)\n", lost, pe->pid, pe->comm);
+                break;
+        }
+    }
 }
 
 unsigned long long remove_exited_child_from_parent(unsigned long long *field, unsigned long long *pfield) {
-       unsigned long long absorbed = 0;
-
-       if(*field > *pfield) {
-               absorbed += *pfield;
-               *field -= *pfield;
-               *pfield = 0;
-       }
-       else {
-               absorbed += *field;
-               *pfield -= *field;
-               *field = 0;
-       }
-
-       return absorbed;
+    unsigned long long absorbed = 0;
+
+    if(*field > *pfield) {
+        absorbed += *pfield;
+        *field -= *pfield;
+        *pfield = 0;
+    }
+    else {
+        absorbed += *field;
+        *pfield -= *field;
+        *field = 0;
+    }
+
+    return absorbed;
 }
 
 void process_exited_processes() {
-       struct pid_stat *p;
-
-       for(p = root_of_pids; p ; p = p->next) {
-               if(p->updated || !p->stat_collected_usec)
-                       continue;
-
-               struct pid_stat *pp = p->parent;
-
-               unsigned long long utime  = (p->utime_raw + p->cutime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-               unsigned long long stime  = (p->stime_raw + p->cstime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-               unsigned long long gtime  = (p->gtime_raw + p->cgtime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-               unsigned long long minflt = (p->minflt_raw + p->cminflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-               unsigned long long majflt = (p->majflt_raw + p->cmajflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
-
-               if(utime + stime + gtime + minflt + majflt == 0)
-                       continue;
-
-               if(unlikely(debug)) {
-                       log_date(stderr);
-                       fprintf(stderr, "Absorb %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
-                               , p->comm
-                               , p->pid
-                               , p->updated?"running":"exited"
-                               , utime
-                               , stime
-                               , gtime
-                               , minflt
-                               , majflt
-                               );
-                       print_process_tree(p, "Searching parents");
-               }
-
-               for(pp = p->parent; pp ; pp = pp->parent) {
-                       if(!pp->updated) continue;
-
-                       unsigned long long absorbed;
-                       absorbed = remove_exited_child_from_parent(&utime,  &pp->cutime);
-                       if(unlikely(debug && absorbed))
-                               fprintf(stderr, " > process %s (%d %s) absorbed %llu utime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
-
-                       absorbed = remove_exited_child_from_parent(&stime,  &pp->cstime);
-                       if(unlikely(debug && absorbed))
-                               fprintf(stderr, " > process %s (%d %s) absorbed %llu stime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
-
-                       absorbed = remove_exited_child_from_parent(&gtime,  &pp->cgtime);
-                       if(unlikely(debug && absorbed))
-                               fprintf(stderr, " > process %s (%d %s) absorbed %llu gtime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
-
-                       absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
-                       if(unlikely(debug && absorbed))
-                               fprintf(stderr, " > process %s (%d %s) absorbed %llu minflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
-
-                       absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
-                       if(unlikely(debug && absorbed))
-                               fprintf(stderr, " > process %s (%d %s) absorbed %llu majflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
-               }
-
-               if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
-                       if(unlikely(debug)) {
-                               if(utime)  find_lost_child_debug(p, utime,  3);
-                               if(stime)  find_lost_child_debug(p, stime,  4);
-                               if(gtime)  find_lost_child_debug(p, gtime,  5);
-                               if(minflt) find_lost_child_debug(p, minflt, 1);
-                               if(majflt) find_lost_child_debug(p, majflt, 2);
-                       }
-
-                       p->keep = 1;
-
-                       if(unlikely(debug))
-                               fprintf(stderr, " > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
-                                       , p->comm
-                                       , p->pid
-                                       , p->updated?"running":"exited"
-                                       , utime
-                                       , stime
-                                       , gtime
-                                       , minflt
-                                       , majflt
-                                       );
-
-                       for(pp = p->parent; pp ; pp = pp->parent) {
-                               if(pp->updated) break;
-                               pp->keep = 1;
-
-                               if(unlikely(debug))
-                                       fprintf(stderr, " > - KEEP - parent for another loop: %s (%d %s)\n"
-                                               , pp->comm
-                                               , pp->pid
-                                               , pp->updated?"running":"exited"
-                                               );
-                       }
-
-                       p->utime_raw   = utime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
-                       p->stime_raw   = stime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
-                       p->gtime_raw   = gtime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
-                       p->minflt_raw  = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
-                       p->majflt_raw  = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
-                       p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
-
-                       if(unlikely(debug))
-                               fprintf(stderr, "\n");
-               }
-               else if(unlikely(debug)) {
-                       fprintf(stderr, " > totally absorbed - DONE - %s (%d %s)\n"
-                               , p->comm
-                               , p->pid
-                               , p->updated?"running":"exited"
-                               );
-               }
-       }
+    struct pid_stat *p;
+
+    for(p = root_of_pids; p ; p = p->next) {
+        if(p->updated || !p->stat_collected_usec)
+            continue;
+
+        struct pid_stat *pp = p->parent;
+
+        unsigned long long utime  = (p->utime_raw + p->cutime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        unsigned long long stime  = (p->stime_raw + p->cstime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        unsigned long long gtime  = (p->gtime_raw + p->cgtime_raw)   * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        unsigned long long minflt = (p->minflt_raw + p->cminflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+        unsigned long long majflt = (p->majflt_raw + p->cmajflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
+
+        if(utime + stime + gtime + minflt + majflt == 0)
+            continue;
+
+        if(unlikely(debug)) {
+            log_date(stderr);
+            fprintf(stderr, "Absorb %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
+                , p->comm
+                , p->pid
+                , p->updated?"running":"exited"
+                , utime
+                , stime
+                , gtime
+                , minflt
+                , majflt
+                );
+            print_process_tree(p, "Searching parents");
+        }
+
+        for(pp = p->parent; pp ; pp = pp->parent) {
+            if(!pp->updated) continue;
+
+            unsigned long long absorbed;
+            absorbed = remove_exited_child_from_parent(&utime,  &pp->cutime);
+            if(unlikely(debug && absorbed))
+                fprintf(stderr, " > process %s (%d %s) absorbed %llu utime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, utime);
+
+            absorbed = remove_exited_child_from_parent(&stime,  &pp->cstime);
+            if(unlikely(debug && absorbed))
+                fprintf(stderr, " > process %s (%d %s) absorbed %llu stime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, stime);
+
+            absorbed = remove_exited_child_from_parent(&gtime,  &pp->cgtime);
+            if(unlikely(debug && absorbed))
+                fprintf(stderr, " > process %s (%d %s) absorbed %llu gtime (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, gtime);
+
+            absorbed = remove_exited_child_from_parent(&minflt, &pp->cminflt);
+            if(unlikely(debug && absorbed))
+                fprintf(stderr, " > process %s (%d %s) absorbed %llu minflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, minflt);
+
+            absorbed = remove_exited_child_from_parent(&majflt, &pp->cmajflt);
+            if(unlikely(debug && absorbed))
+                fprintf(stderr, " > process %s (%d %s) absorbed %llu majflt (remaining: %llu)\n", pp->comm, pp->pid, pp->updated?"running":"exited", absorbed, majflt);
+        }
+
+        if(unlikely(utime + stime + gtime + minflt + majflt > 0)) {
+            if(unlikely(debug)) {
+                if(utime)  find_lost_child_debug(p, utime,  3);
+                if(stime)  find_lost_child_debug(p, stime,  4);
+                if(gtime)  find_lost_child_debug(p, gtime,  5);
+                if(minflt) find_lost_child_debug(p, minflt, 1);
+                if(majflt) find_lost_child_debug(p, majflt, 2);
+            }
+
+            p->keep = 1;
+
+            if(unlikely(debug))
+                fprintf(stderr, " > remaining resources - KEEP - for another loop: %s (%d %s total resources: utime=%llu stime=%llu gtime=%llu minflt=%llu majflt=%llu)\n"
+                    , p->comm
+                    , p->pid
+                    , p->updated?"running":"exited"
+                    , utime
+                    , stime
+                    , gtime
+                    , minflt
+                    , majflt
+                    );
+
+            for(pp = p->parent; pp ; pp = pp->parent) {
+                if(pp->updated) break;
+                pp->keep = 1;
+
+                if(unlikely(debug))
+                    fprintf(stderr, " > - KEEP - parent for another loop: %s (%d %s)\n"
+                        , pp->comm
+                        , pp->pid
+                        , pp->updated?"running":"exited"
+                        );
+            }
+
+            p->utime_raw   = utime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+            p->stime_raw   = stime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+            p->gtime_raw   = gtime  * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+            p->minflt_raw  = minflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+            p->majflt_raw  = majflt * (p->stat_collected_usec - p->last_stat_collected_usec) / (1000000ULL * RATES_DETAIL);
+            p->cutime_raw = p->cstime_raw = p->cgtime_raw = p->cminflt_raw = p->cmajflt_raw = 0;
+
+            if(unlikely(debug))
+                fprintf(stderr, "\n");
+        }
+        else if(unlikely(debug)) {
+            fprintf(stderr, " > totally absorbed - DONE - %s (%d %s)\n"
+                , p->comm
+                , p->pid
+                , p->updated?"running":"exited"
+                );
+        }
+    }
 }
 
 void link_all_processes_to_their_parents(void) {
-       struct pid_stat *p, *pp;
-
-       // link all children to their parents
-       // and update children count on parents
-       for(p = root_of_pids; p ; p = p->next) {
-               // for each process found
-
-               p->sortlist = 0;
-               p->parent = NULL;
-
-               if(unlikely(!p->ppid)) {
-                       p->parent = NULL;
-                       continue;
-               }
-
-               pp = all_pids[p->ppid];
-               if(likely(pp)) {
-                       p->parent = pp;
-                       pp->children_count++;
-
-                       if(unlikely(debug || (p->target && p->target->debug)))
-                               fprintf(stderr, "apps.plugin: \tchild %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=%llu, stime=%llu, gtime=%llu, minflt=%llu, majflt=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
-               }
-               else {
-                       p->parent = NULL;
-                       error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
-               }
-       }
+    struct pid_stat *p, *pp;
+
+    // link all children to their parents
+    // and update children count on parents
+    for(p = root_of_pids; p ; p = p->next) {
+        // for each process found
+
+        p->sortlist = 0;
+        p->parent = NULL;
+
+        if(unlikely(!p->ppid)) {
+            p->parent = NULL;
+            continue;
+        }
+
+        pp = all_pids[p->ppid];
+        if(likely(pp)) {
+            p->parent = pp;
+            pp->children_count++;
+
+            if(unlikely(debug || (p->target && p->target->debug)))
+                fprintf(stderr, "apps.plugin: \tchild %d (%s, %s) on target '%s' has parent %d (%s, %s). Parent: utime=%llu, stime=%llu, gtime=%llu, minflt=%llu, majflt=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->updated?"running":"exited", (p->target)?p->target->name:"UNSET", pp->pid, pp->comm, pp->updated?"running":"exited", pp->utime, pp->stime, pp->gtime, pp->minflt, pp->majflt, pp->cutime, pp->cstime, pp->cgtime, pp->cminflt, pp->cmajflt);
+        }
+        else {
+            p->parent = NULL;
+            error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
+        }
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -1629,203 +1571,221 @@ void link_all_processes_to_their_parents(void) {
 
 static int compar_pid(const void *pid1, const void *pid2) {
 
-       struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
-       struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
+    struct pid_stat *p1 = all_pids[*((pid_t *)pid1)];
+    struct pid_stat *p2 = all_pids[*((pid_t *)pid2)];
 
-       if(p1->sortlist > p2->sortlist)
-               return -1;
-       else
-               return 1;
+    if(p1->sortlist > p2->sortlist)
+        return -1;
+    else
+        return 1;
+}
+
+static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
+    if(unlikely(!status)) {
+        // error("command failed log %u, errno %d", log, errno);
+
+        if(unlikely(debug || errno != ENOENT)) {
+            if(unlikely(debug || !(p->log_thrown & log))) {
+                p->log_thrown |= log;
+                switch(log) {
+                    case PID_LOG_IO:
+                        error("Cannot process %s/proc/%d/io (command '%s')", host_prefix, p->pid, p->comm);
+                        break;
+
+                    case PID_LOG_STATM:
+                        error("Cannot process %s/proc/%d/statm (command '%s')", host_prefix, p->pid, p->comm);
+                        break;
+
+                    case PID_LOG_CMDLINE:
+                        error("Cannot process %s/proc/%d/cmdline (command '%s')", host_prefix, p->pid, p->comm);
+                        break;
+
+                    case PID_LOG_FDS:
+                        error("Cannot process entries in %s/proc/%d/fd (command '%s')", host_prefix, p->pid, p->comm);
+                        break;
+
+                    case PID_LOG_STAT:
+                        break;
+
+                    default:
+                        error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
+                        break;
+                }
+            }
+        }
+        errno = 0;
+    }
+    else if(unlikely(p->log_thrown & log)) {
+        // error("unsetting log %u on pid %d", log, p->pid);
+        p->log_thrown &= ~log;
+    }
+
+    return status;
 }
 
 void collect_data_for_pid(pid_t pid) {
-       if(unlikely(pid <= 0 || pid > pid_max)) {
-               error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
-               return;
-       }
-
-       struct pid_stat *p = get_pid_entry(pid);
-       if(unlikely(!p || p->read)) return;
-       p->read             = 1;
-
-       // fprintf(stderr, "Reading process %d (%s), sortlist %d\n", p->pid, p->comm, p->sortlist);
-
-       // --------------------------------------------------------------------
-       // /proc/<pid>/stat
-
-       if(unlikely(read_proc_pid_stat(p))) {
-               if(errno != ENOENT || debug)
-                       error("Cannot process %s/proc/%d/stat (command '%s')", host_prefix, pid, p->comm);
-               else
-                       errno = 0;
-               // there is no reason to proceed if we cannot get its status
-               return;
-       }
-
-       read_proc_pid_ownership(p);
-
-       // check its parent pid
-       if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
-               error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
-               p->ppid = 0;
-       }
-
-       // --------------------------------------------------------------------
-       // /proc/<pid>/io
-
-       if(unlikely(read_proc_pid_io(p))) {
-               if(errno != ENOENT || debug)
-                       error("Cannot process %s/proc/%d/io (command '%s')", host_prefix, pid, p->comm);
-               else
-                       errno = 0;
-       }
-
-       // --------------------------------------------------------------------
-       // /proc/<pid>/statm
-
-       if(unlikely(read_proc_pid_statm(p))) {
-               if(errno != ENOENT || debug)
-                       error("Cannot process %s/proc/%d/statm (command '%s')", host_prefix, pid, p->comm);
-               else
-                       errno = 0;
-               // there is no reason to proceed if we cannot get its memory status
-               return;
-       }
-
-       // --------------------------------------------------------------------
-       // link it
-
-       // check if it is target
-       // we do this only once, the first time this pid is loaded
-       if(unlikely(p->new_entry)) {
-               // /proc/<pid>/cmdline
-               if(likely(proc_pid_cmdline_is_needed)) {
-                       if(unlikely(read_proc_pid_cmdline(p))) {
-                               if(errno != ENOENT || debug)
-                                       error("Cannot process %s/proc/%d/cmdline (command '%s')", host_prefix, pid, p->comm);
-                               else
-                                       errno = 0;
-                       }
-               }
-
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
-
-               uint32_t hash = simple_hash(p->comm);
-               size_t pclen  = strlen(p->comm);
-
-               struct target *w;
-               for(w = apps_groups_root_target; w ; w = w->next) {
-                       // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
-
-                       // find it - 4 cases:
-                       // 1. the target is not a pattern
-                       // 2. the target has the prefix
-                       // 3. the target has the suffix
-                       // 4. the target is something inside cmdline
-                       if(     (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
-                              || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
-                              || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
-                              || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
-                                       ) {
-                               if(w->target) p->target = w->target;
-                               else p->target = w;
-
-                               if(debug || (p->target && p->target->debug))
-                                       fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
-
-                               break;
-                       }
-               }
-       }
-
-       // --------------------------------------------------------------------
-       // /proc/<pid>/fd
-
-       if(unlikely(read_pid_file_descriptors(p))) {
-               if(errno != ENOENT || debug)
-                       error("Cannot process entries in %s/proc/%d/fd (command '%s')", host_prefix, pid, p->comm);
-               else
-                       errno = 0;
-       }
-
-       // --------------------------------------------------------------------
-       // done!
-
-       if(unlikely(debug && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
-               fprintf(stderr, "Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read\n", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
-
-       // mark it as updated
-       p->updated = 1;
-       p->keep = 0;
-       p->keeploops = 0;
+    if(unlikely(pid <= 0 || pid > pid_max)) {
+        error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
+        return;
+    }
+
+    struct pid_stat *p = get_pid_entry(pid);
+    if(unlikely(!p || p->read)) return;
+    p->read             = 1;
+
+    // fprintf(stderr, "Reading process %d (%s), sortlist %d\n", p->pid, p->comm, p->sortlist);
+
+    // --------------------------------------------------------------------
+    // /proc/<pid>/stat
+
+    if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p))))
+        // there is no reason to proceed if we cannot get its status
+        return;
+
+    read_proc_pid_ownership(p);
+
+    // check its parent pid
+    if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
+        error("Pid %d (command '%s') states invalid parent pid %d. Using 0.", pid, p->comm, p->ppid);
+        p->ppid = 0;
+    }
+
+    // --------------------------------------------------------------------
+    // /proc/<pid>/io
+
+    managed_log(p, PID_LOG_IO, read_proc_pid_io(p));
+
+    // --------------------------------------------------------------------
+    // /proc/<pid>/statm
+
+    if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p))))
+        // there is no reason to proceed if we cannot get its memory status
+        return;
+
+    // --------------------------------------------------------------------
+    // link it
+
+    // check if it is target
+    // we do this only once, the first time this pid is loaded
+    if(unlikely(p->new_entry)) {
+        // /proc/<pid>/cmdline
+        if(likely(proc_pid_cmdline_is_needed))
+            managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
+
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
+
+        uint32_t hash = simple_hash(p->comm);
+        size_t pclen  = strlen(p->comm);
+
+        struct target *w;
+        for(w = apps_groups_root_target; w ; w = w->next) {
+            // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
+
+            // find it - 4 cases:
+            // 1. the target is not a pattern
+            // 2. the target has the prefix
+            // 3. the target has the suffix
+            // 4. the target is something inside cmdline
+            if( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
+                   || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
+                   || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
+                   || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
+                    ) {
+                if(w->target) p->target = w->target;
+                else p->target = w;
+
+                if(debug || (p->target && p->target->debug))
+                    fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
+
+                break;
+            }
+        }
+    }
+
+    // --------------------------------------------------------------------
+    // /proc/<pid>/fd
+
+    if(enable_file_charts)
+            managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p));
+
+    // --------------------------------------------------------------------
+    // done!
+
+    if(unlikely(debug && include_exited_childs && all_pids_count && p->ppid && all_pids[p->ppid] && !all_pids[p->ppid]->read))
+        fprintf(stderr, "Read process %d (%s) sortlisted %d, but its parent %d (%s) sortlisted %d, is not read\n", p->pid, p->comm, p->sortlist, all_pids[p->ppid]->pid, all_pids[p->ppid]->comm, all_pids[p->ppid]->sortlist);
+
+    // mark it as updated
+    p->updated = 1;
+    p->keep = 0;
+    p->keeploops = 0;
 }
 
 int collect_data_for_all_processes_from_proc(void) {
-       struct pid_stat *p = NULL;
-
-       if(all_pids_count) {
-               // read parents before childs
-               // this is needed to prevent a situation where
-               // a child is found running, but until we read
-               // its parent, it has exited and its parent
-               // has accumulated its resources
-
-               long slc = 0;
-               for(p = root_of_pids; p ; p = p->next) {
-                       p->read             = 0;
-                       p->updated          = 0;
-                       p->new_entry        = 0;
-                       p->merged           = 0;
-                       p->children_count   = 0;
-                       p->parent           = NULL;
-
-                       all_pids_sortlist[slc++] = p->pid;
-               }
-
-               if(unlikely(slc != all_pids_count)) {
-                       error("Internal error: I was thinking I had %ld processes in my arrays, but it seems there are more.", all_pids_count);
-                       all_pids_count = slc;
-               }
-
-               if(include_exited_childs) {
-                       qsort((void *)all_pids_sortlist, all_pids_count, sizeof(pid_t), compar_pid);
-                       for(slc = 0; slc < all_pids_count; slc++)
-                               collect_data_for_pid(all_pids_sortlist[slc]);
-               }
-       }
-
-       char dirname[FILENAME_MAX + 1];
-
-       snprintfz(dirname, FILENAME_MAX, "%s/proc", host_prefix);
-       DIR *dir = opendir(dirname);
-       if(!dir) return 0;
-
-       struct dirent *file = NULL;
-
-       while((file = readdir(dir))) {
-               char *endptr = file->d_name;
-               pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
-
-               // make sure we read a valid number
-               if(unlikely(endptr == file->d_name || *endptr != '\0'))
-                       continue;
-
-               collect_data_for_pid(pid);
-       }
-       closedir(dir);
-
-       // normally this is done
-       // however we may have processes exited while we collected values
-       // so let's find the exited ones
-       // we do this by collecting the ownership of process
-       // if we manage to get the ownership, the process still runs
-
-       read_proc_stat();
-       link_all_processes_to_their_parents();
-       process_exited_processes();
-
-       return 1;
+    struct pid_stat *p = NULL;
+
+    if(all_pids_count) {
+        // read parents before childs
+        // this is needed to prevent a situation where
+        // a child is found running, but until we read
+        // its parent, it has exited and its parent
+        // has accumulated its resources
+
+        long slc = 0;
+        for(p = root_of_pids; p ; p = p->next) {
+            p->read             = 0;
+            p->updated          = 0;
+            p->new_entry        = 0;
+            p->merged           = 0;
+            p->children_count   = 0;
+            p->parent           = NULL;
+
+            all_pids_sortlist[slc++] = p->pid;
+        }
+
+        if(unlikely(slc != all_pids_count)) {
+            error("Internal error: I was thinking I had %ld processes in my arrays, but it seems there are more.", all_pids_count);
+            all_pids_count = slc;
+        }
+
+        if(include_exited_childs) {
+            qsort((void *)all_pids_sortlist, all_pids_count, sizeof(pid_t), compar_pid);
+            for(slc = 0; slc < all_pids_count; slc++)
+                collect_data_for_pid(all_pids_sortlist[slc]);
+        }
+    }
+
+    char dirname[FILENAME_MAX + 1];
+
+    snprintfz(dirname, FILENAME_MAX, "%s/proc", host_prefix);
+    DIR *dir = opendir(dirname);
+    if(!dir) return 0;
+
+    struct dirent *file = NULL;
+
+    while((file = readdir(dir))) {
+        char *endptr = file->d_name;
+        pid_t pid = (pid_t) strtoul(file->d_name, &endptr, 10);
+
+        // make sure we read a valid number
+        if(unlikely(endptr == file->d_name || *endptr != '\0'))
+            continue;
+
+        collect_data_for_pid(pid);
+    }
+    closedir(dir);
+
+    // normally this is done
+    // however we may have processes exited while we collected values
+    // so let's find the exited ones
+    // we do this by collecting the ownership of process
+    // if we manage to get the ownership, the process still runs
+
+    read_proc_stat();
+    link_all_processes_to_their_parents();
+    process_exited_processes();
+
+    return 1;
 }
 
 // ----------------------------------------------------------------------------
@@ -1845,374 +1805,371 @@ int collect_data_for_all_processes_from_proc(void) {
 // check: update_apps_groups_statistics()
 
 void cleanup_exited_pids(void) {
-       int c;
-       struct pid_stat *p = NULL;
-
-       for(p = root_of_pids; p ;) {
-               if(!p->updated && (!p->keep || p->keeploops > 0)) {
-//                     fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name,  p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
-
-                       if(unlikely(debug && (p->keep || p->keeploops)))
-                               fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm);
-
-                       for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
-                               file_descriptor_not_used(p->fds[c]);
-                               p->fds[c] = 0;
-                       }
-
-                       pid_t r = p->pid;
-                       p = p->next;
-                       del_pid_entry(r);
-               }
-               else {
-                       if(unlikely(p->keep)) p->keeploops++;
-                       p->keep = 0;
-                       p = p->next;
-               }
-       }
+    int c;
+    struct pid_stat *p = NULL;
+
+    for(p = root_of_pids; p ;) {
+        if(!p->updated && (!p->keep || p->keeploops > 0)) {
+//          fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name,  p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
+
+            if(unlikely(debug && (p->keep || p->keeploops)))
+                fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm);
+
+            for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
+                file_descriptor_not_used(p->fds[c]);
+                p->fds[c] = 0;
+            }
+
+            pid_t r = p->pid;
+            p = p->next;
+            del_pid_entry(r);
+        }
+        else {
+            if(unlikely(p->keep)) p->keeploops++;
+            p->keep = 0;
+            p = p->next;
+        }
+    }
 }
 
 void apply_apps_groups_targets_inheritance(void) {
-       struct pid_stat *p = NULL;
-
-       // children that do not have a target
-       // inherit their target from their parent
-       int found = 1, loops = 0;
-       while(found) {
-               if(unlikely(debug)) loops++;
-               found = 0;
-               for(p = root_of_pids; p ; p = p->next) {
-                       // if this process does not have a target
-                       // and it has a parent
-                       // and its parent has a target
-                       // then, set the parent's target to this process
-                       if(unlikely(!p->target && p->parent && p->parent->target)) {
-                               p->target = p->parent->target;
-                               found++;
-
-                               if(debug || (p->target && p->target->debug))
-                                       fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
-                       }
-               }
-       }
-
-       // find all the procs with 0 childs and merge them to their parents
-       // repeat, until nothing more can be done.
-       int sortlist = 1;
-       found = 1;
-       while(found) {
-               if(unlikely(debug)) loops++;
-               found = 0;
-
-               for(p = root_of_pids; p ; p = p->next) {
-                       if(unlikely(!p->sortlist && !p->children_count))
-                               p->sortlist = sortlist++;
-
-                       // if this process does not have any children
-                       // and is not already merged
-                       // and has a parent
-                       // and its parent has children
-                       // and the target of this process and its parent is the same, or the parent does not have a target
-                       // and its parent is not init
-                       // then, mark them as merged.
-                       if(unlikely(
-                                       !p->children_count
-                                       && !p->merged
-                                       && p->parent
-                                       && p->parent->children_count
-                                       && (p->target == p->parent->target || !p->parent->target)
-                                       && p->ppid != 1
-                               )) {
-                               p->parent->children_count--;
-                               p->merged = 1;
-
-                               // the parent inherits the child's target, if it does not have a target itself
-                               if(unlikely(p->target && !p->parent->target)) {
-                                       p->parent->target = p->target;
-
-                                       if(debug || (p->target && p->target->debug))
-                                               fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
-                               }
-
-                               found++;
-                       }
-               }
-
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: TARGET INHERITANCE: merged %d processes\n", found);
-       }
-
-       // init goes always to default target
-       if(all_pids[1])
-               all_pids[1]->target = apps_groups_default_target;
-
-       // give a default target on all top level processes
-       if(unlikely(debug)) loops++;
-       for(p = root_of_pids; p ; p = p->next) {
-               // if the process is not merged itself
-               // then is is a top level process
-               if(unlikely(!p->merged && !p->target))
-                       p->target = apps_groups_default_target;
-
-               // make sure all processes have a sortlist
-               if(unlikely(!p->sortlist))
-                       p->sortlist = sortlist++;
-       }
-
-       if(all_pids[1])
-               all_pids[1]->sortlist = sortlist++;
-
-       // give a target to all merged child processes
-       found = 1;
-       while(found) {
-               if(unlikely(debug)) loops++;
-               found = 0;
-               for(p = root_of_pids; p ; p = p->next) {
-                       if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
-                               p->target = p->parent->target;
-                               found++;
-
-                               if(debug || (p->target && p->target->debug))
-                                       fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
-                       }
-               }
-       }
-
-       if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: apply_apps_groups_targets_inheritance() made %d loops on the process tree\n", loops);
+    struct pid_stat *p = NULL;
+
+    // children that do not have a target
+    // inherit their target from their parent
+    int found = 1, loops = 0;
+    while(found) {
+        if(unlikely(debug)) loops++;
+        found = 0;
+        for(p = root_of_pids; p ; p = p->next) {
+            // if this process does not have a target
+            // and it has a parent
+            // and its parent has a target
+            // then, set the parent's target to this process
+            if(unlikely(!p->target && p->parent && p->parent->target)) {
+                p->target = p->parent->target;
+                found++;
+
+                if(debug || (p->target && p->target->debug))
+                    fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s).\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
+            }
+        }
+    }
+
+    // find all the procs with 0 childs and merge them to their parents
+    // repeat, until nothing more can be done.
+    int sortlist = 1;
+    found = 1;
+    while(found) {
+        if(unlikely(debug)) loops++;
+        found = 0;
+
+        for(p = root_of_pids; p ; p = p->next) {
+            if(unlikely(!p->sortlist && !p->children_count))
+                p->sortlist = sortlist++;
+
+            // if this process does not have any children
+            // and is not already merged
+            // and has a parent
+            // and its parent has children
+            // and the target of this process and its parent is the same, or the parent does not have a target
+            // and its parent is not init
+            // then, mark them as merged.
+            if(unlikely(
+                    !p->children_count
+                    && !p->merged
+                    && p->parent
+                    && p->parent->children_count
+                    && (p->target == p->parent->target || !p->parent->target)
+                    && p->ppid != 1
+                )) {
+                p->parent->children_count--;
+                p->merged = 1;
+
+                // the parent inherits the child's target, if it does not have a target itself
+                if(unlikely(p->target && !p->parent->target)) {
+                    p->parent->target = p->target;
+
+                    if(debug || (p->target && p->target->debug))
+                        fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
+                }
+
+                found++;
+            }
+        }
+
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: TARGET INHERITANCE: merged %d processes\n", found);
+    }
+
+    // init goes always to default target
+    if(all_pids[1])
+        all_pids[1]->target = apps_groups_default_target;
+
+    // give a default target on all top level processes
+    if(unlikely(debug)) loops++;
+    for(p = root_of_pids; p ; p = p->next) {
+        // if the process is not merged itself
+        // then is is a top level process
+        if(unlikely(!p->merged && !p->target))
+            p->target = apps_groups_default_target;
+
+        // make sure all processes have a sortlist
+        if(unlikely(!p->sortlist))
+            p->sortlist = sortlist++;
+    }
+
+    if(all_pids[1])
+        all_pids[1]->sortlist = sortlist++;
+
+    // give a target to all merged child processes
+    found = 1;
+    while(found) {
+        if(unlikely(debug)) loops++;
+        found = 0;
+        for(p = root_of_pids; p ; p = p->next) {
+            if(unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
+                p->target = p->parent->target;
+                found++;
+
+                if(debug || (p->target && p->target->debug))
+                    fprintf(stderr, "apps.plugin: \t\tTARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.\n", p->target->name, p->pid, p->comm, p->parent->pid, p->parent->comm);
+            }
+        }
+    }
+
+    if(unlikely(debug))
+        fprintf(stderr, "apps.plugin: apply_apps_groups_targets_inheritance() made %d loops on the process tree\n", loops);
 }
 
 long zero_all_targets(struct target *root) {
-       struct target *w;
-       long count = 0;
-
-       for (w = root; w ; w = w->next) {
-               count++;
-
-               if(w->fds) free(w->fds);
-               w->fds = NULL;
-
-               w->minflt = 0;
-               w->majflt = 0;
-               w->utime = 0;
-               w->stime = 0;
-               w->gtime = 0;
-               w->cminflt = 0;
-               w->cmajflt = 0;
-               w->cutime = 0;
-               w->cstime = 0;
-               w->cgtime = 0;
-               w->num_threads = 0;
-               w->rss = 0;
-               w->processes = 0;
-
-               w->statm_size = 0;
-               w->statm_resident = 0;
-               w->statm_share = 0;
-               w->statm_text = 0;
-               w->statm_lib = 0;
-               w->statm_data = 0;
-               w->statm_dirty = 0;
-
-               w->io_logical_bytes_read = 0;
-               w->io_logical_bytes_written = 0;
-               w->io_read_calls = 0;
-               w->io_write_calls = 0;
-               w->io_storage_bytes_read = 0;
-               w->io_storage_bytes_written = 0;
-               w->io_cancelled_write_bytes = 0;
-       }
-
-       return count;
+    struct target *w;
+    long count = 0;
+
+    for (w = root; w ; w = w->next) {
+        count++;
+
+        if(w->fds) freez(w->fds);
+        w->fds = NULL;
+
+        w->minflt = 0;
+        w->majflt = 0;
+        w->utime = 0;
+        w->stime = 0;
+        w->gtime = 0;
+        w->cminflt = 0;
+        w->cmajflt = 0;
+        w->cutime = 0;
+        w->cstime = 0;
+        w->cgtime = 0;
+        w->num_threads = 0;
+        w->rss = 0;
+        w->processes = 0;
+
+        w->statm_size = 0;
+        w->statm_resident = 0;
+        w->statm_share = 0;
+        w->statm_text = 0;
+        w->statm_lib = 0;
+        w->statm_data = 0;
+        w->statm_dirty = 0;
+
+        w->io_logical_bytes_read = 0;
+        w->io_logical_bytes_written = 0;
+        w->io_read_calls = 0;
+        w->io_write_calls = 0;
+        w->io_storage_bytes_read = 0;
+        w->io_storage_bytes_written = 0;
+        w->io_cancelled_write_bytes = 0;
+    }
+
+    return count;
 }
 
 void aggregate_pid_on_target(struct target *w, struct pid_stat *p, struct target *o) {
-       (void)o;
-
-       if(unlikely(!w->fds)) {
-               w->fds = calloc(sizeof(int), (size_t) all_files_size);
-               if(unlikely(!w->fds))
-                       error("Cannot allocate memory for fds in %s", w->name);
-       }
-
-       if(likely(p->updated)) {
-               w->cutime  += p->cutime;
-               w->cstime  += p->cstime;
-               w->cgtime  += p->cgtime;
-               w->cminflt += p->cminflt;
-               w->cmajflt += p->cmajflt;
-
-               w->utime  += p->utime;
-               w->stime  += p->stime;
-               w->gtime  += p->gtime;
-               w->minflt += p->minflt;
-               w->majflt += p->majflt;
-
-               w->rss += p->rss;
-
-               w->statm_size += p->statm_size;
-               w->statm_resident += p->statm_resident;
-               w->statm_share += p->statm_share;
-               w->statm_text += p->statm_text;
-               w->statm_lib += p->statm_lib;
-               w->statm_data += p->statm_data;
-               w->statm_dirty += p->statm_dirty;
-
-               w->io_logical_bytes_read    += p->io_logical_bytes_read;
-               w->io_logical_bytes_written += p->io_logical_bytes_written;
-               w->io_read_calls            += p->io_read_calls;
-               w->io_write_calls           += p->io_write_calls;
-               w->io_storage_bytes_read    += p->io_storage_bytes_read;
-               w->io_storage_bytes_written += p->io_storage_bytes_written;
-               w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
-
-               w->processes++;
-               w->num_threads += p->num_threads;
-
-               if(likely(w->fds)) {
-                       int c;
-                       for(c = 0; c < p->fds_size ;c++) {
-                               if(p->fds[c] == 0) continue;
-
-                               if(likely(p->fds[c] < all_files_size)) {
-                                       if(w->fds) w->fds[p->fds[c]]++;
-                               }
-                               else
-                                       error("Invalid fd number %d", p->fds[c]);
-                       }
-               }
-
-               if(unlikely(debug || w->debug))
-                       fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
-       }
+    (void)o;
+
+    if(unlikely(!w->fds))
+        w->fds = callocz(sizeof(int), (size_t) all_files_size);
+
+    if(likely(p->updated)) {
+        w->cutime  += p->cutime;
+        w->cstime  += p->cstime;
+        w->cgtime  += p->cgtime;
+        w->cminflt += p->cminflt;
+        w->cmajflt += p->cmajflt;
+
+        w->utime  += p->utime;
+        w->stime  += p->stime;
+        w->gtime  += p->gtime;
+        w->minflt += p->minflt;
+        w->majflt += p->majflt;
+
+        w->rss += p->rss;
+
+        w->statm_size += p->statm_size;
+        w->statm_resident += p->statm_resident;
+        w->statm_share += p->statm_share;
+        w->statm_text += p->statm_text;
+        w->statm_lib += p->statm_lib;
+        w->statm_data += p->statm_data;
+        w->statm_dirty += p->statm_dirty;
+
+        w->io_logical_bytes_read    += p->io_logical_bytes_read;
+        w->io_logical_bytes_written += p->io_logical_bytes_written;
+        w->io_read_calls            += p->io_read_calls;
+        w->io_write_calls           += p->io_write_calls;
+        w->io_storage_bytes_read    += p->io_storage_bytes_read;
+        w->io_storage_bytes_written += p->io_storage_bytes_written;
+        w->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
+
+        w->processes++;
+        w->num_threads += p->num_threads;
+
+        if(likely(w->fds)) {
+            int c;
+            for(c = 0; c < p->fds_size ;c++) {
+                if(p->fds[c] == 0) continue;
+
+                if(likely(p->fds[c] < all_files_size)) {
+                    if(w->fds) w->fds[p->fds[c]]++;
+                }
+                else
+                    error("Invalid fd number %d", p->fds[c]);
+            }
+        }
+
+        if(unlikely(debug || w->debug))
+            fprintf(stderr, "apps.plugin: \taggregating '%s' pid %d on target '%s' utime=%llu, stime=%llu, gtime=%llu, cutime=%llu, cstime=%llu, cgtime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, w->name, p->utime, p->stime, p->gtime, p->cutime, p->cstime, p->cgtime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
+    }
 }
 
 void count_targets_fds(struct target *root) {
-       int c;
-       struct target *w;
-
-       for (w = root; w ; w = w->next) {
-               if(!w->fds) continue;
-
-               w->openfiles = 0;
-               w->openpipes = 0;
-               w->opensockets = 0;
-               w->openinotifies = 0;
-               w->openeventfds = 0;
-               w->opentimerfds = 0;
-               w->opensignalfds = 0;
-               w->openeventpolls = 0;
-               w->openother = 0;
-
-               for(c = 1; c < all_files_size ;c++) {
-                       if(w->fds[c] > 0)
-                               switch(all_files[c].type) {
-                               case FILETYPE_FILE:
-                                       w->openfiles++;
-                                       break;
-
-                               case FILETYPE_PIPE:
-                                       w->openpipes++;
-                                       break;
-
-                               case FILETYPE_SOCKET:
-                                       w->opensockets++;
-                                       break;
-
-                               case FILETYPE_INOTIFY:
-                                       w->openinotifies++;
-                                       break;
-
-                               case FILETYPE_EVENTFD:
-                                       w->openeventfds++;
-                                       break;
-
-                               case FILETYPE_TIMERFD:
-                                       w->opentimerfds++;
-                                       break;
-
-                               case FILETYPE_SIGNALFD:
-                                       w->opensignalfds++;
-                                       break;
-
-                               case FILETYPE_EVENTPOLL:
-                                       w->openeventpolls++;
-                                       break;
-
-                               default:
-                                       w->openother++;
-                       }
-               }
-
-               free(w->fds);
-               w->fds = NULL;
-       }
+    int c;
+    struct target *w;
+
+    for (w = root; w ; w = w->next) {
+        if(!w->fds) continue;
+
+        w->openfiles = 0;
+        w->openpipes = 0;
+        w->opensockets = 0;
+        w->openinotifies = 0;
+        w->openeventfds = 0;
+        w->opentimerfds = 0;
+        w->opensignalfds = 0;
+        w->openeventpolls = 0;
+        w->openother = 0;
+
+        for(c = 1; c < all_files_size ;c++) {
+            if(w->fds[c] > 0)
+                switch(all_files[c].type) {
+                case FILETYPE_FILE:
+                    w->openfiles++;
+                    break;
+
+                case FILETYPE_PIPE:
+                    w->openpipes++;
+                    break;
+
+                case FILETYPE_SOCKET:
+                    w->opensockets++;
+                    break;
+
+                case FILETYPE_INOTIFY:
+                    w->openinotifies++;
+                    break;
+
+                case FILETYPE_EVENTFD:
+                    w->openeventfds++;
+                    break;
+
+                case FILETYPE_TIMERFD:
+                    w->opentimerfds++;
+                    break;
+
+                case FILETYPE_SIGNALFD:
+                    w->opensignalfds++;
+                    break;
+
+                case FILETYPE_EVENTPOLL:
+                    w->openeventpolls++;
+                    break;
+
+                default:
+                    w->openother++;
+            }
+        }
+
+        freez(w->fds);
+        w->fds = NULL;
+    }
 }
 
 void calculate_netdata_statistics(void) {
-       apply_apps_groups_targets_inheritance();
+    apply_apps_groups_targets_inheritance();
 
-       zero_all_targets(users_root_target);
-       zero_all_targets(groups_root_target);
-       apps_groups_targets = zero_all_targets(apps_groups_root_target);
+    zero_all_targets(users_root_target);
+    zero_all_targets(groups_root_target);
+    apps_groups_targets = zero_all_targets(apps_groups_root_target);
 
-       // this has to be done, before the cleanup
-       struct pid_stat *p = NULL;
-       struct target *w = NULL, *o = NULL;
+    // this has to be done, before the cleanup
+    struct pid_stat *p = NULL;
+    struct target *w = NULL, *o = NULL;
 
-       // concentrate everything on the apps_groups_targets
-       for(p = root_of_pids; p ; p = p->next) {
+    // concentrate everything on the apps_groups_targets
+    for(p = root_of_pids; p ; p = p->next) {
 
-               // --------------------------------------------------------------------
-               // apps_groups targets
-               if(likely(p->target))
-                       aggregate_pid_on_target(p->target, p, NULL);
-               else
-                       error("pid %d %s was left without a target!", p->pid, p->comm);
+        // --------------------------------------------------------------------
+        // apps_groups targets
+        if(likely(p->target))
+            aggregate_pid_on_target(p->target, p, NULL);
+        else
+            error("pid %d %s was left without a target!", p->pid, p->comm);
 
 
-               // --------------------------------------------------------------------
-               // user targets
-               o = p->user_target;
-               if(likely(p->user_target && p->user_target->uid == p->uid))
-                       w = p->user_target;
-               else {
-                       if(unlikely(debug && p->user_target))
-                                       fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched user from %u (%s) to %u.\n", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
+        // --------------------------------------------------------------------
+        // user targets
+        o = p->user_target;
+        if(likely(p->user_target && p->user_target->uid == p->uid))
+            w = p->user_target;
+        else {
+            if(unlikely(debug && p->user_target))
+                    fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched user from %u (%s) to %u.\n", p->pid, p->comm, p->user_target->uid, p->user_target->name, p->uid);
 
-                       w = p->user_target = get_users_target(p->uid);
-               }
+            w = p->user_target = get_users_target(p->uid);
+        }
 
-               if(likely(w))
-                       aggregate_pid_on_target(w, p, o);
-               else
-                       error("pid %d %s was left without a user target!", p->pid, p->comm);
+        if(likely(w))
+            aggregate_pid_on_target(w, p, o);
+        else
+            error("pid %d %s was left without a user target!", p->pid, p->comm);
 
 
-               // --------------------------------------------------------------------
-               // group targets
-               o = p->group_target;
-               if(likely(p->group_target && p->group_target->gid == p->gid))
-                       w = p->group_target;
-               else {
-                       if(unlikely(debug && p->group_target))
-                                       fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched group from %u (%s) to %u.\n", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
+        // --------------------------------------------------------------------
+        // group targets
+        o = p->group_target;
+        if(likely(p->group_target && p->group_target->gid == p->gid))
+            w = p->group_target;
+        else {
+            if(unlikely(debug && p->group_target))
+                    fprintf(stderr, "apps.plugin: \t\tpid %d (%s) switched group from %u (%s) to %u.\n", p->pid, p->comm, p->group_target->gid, p->group_target->name, p->gid);
 
-                       w = p->group_target = get_groups_target(p->gid);
-               }
+            w = p->group_target = get_groups_target(p->gid);
+        }
 
-               if(likely(w))
-                       aggregate_pid_on_target(w, p, o);
-               else
-                       error("pid %d %s was left without a group target!", p->pid, p->comm);
+        if(likely(w))
+            aggregate_pid_on_target(w, p, o);
+        else
+            error("pid %d %s was left without a group target!", p->pid, p->comm);
 
-       }
+    }
 
-       count_targets_fds(apps_groups_root_target);
-       count_targets_fds(users_root_target);
-       count_targets_fds(groups_root_target);
+    count_targets_fds(apps_groups_root_target);
+    count_targets_fds(users_root_target);
+    count_targets_fds(groups_root_target);
 
-       cleanup_exited_pids();
+    cleanup_exited_pids();
 }
 
 // ----------------------------------------------------------------------------
@@ -2222,383 +2179,385 @@ BUFFER *output = NULL;
 int print_calculated_number(char *str, calculated_number value) { (void)str; (void)value; return 0; }
 
 static inline void send_BEGIN(const char *type, const char *id, unsigned long long usec) {
-       // fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec);
-       buffer_strcat(output, "BEGIN ");
-       buffer_strcat(output, type);
-       buffer_strcat(output, ".");
-       buffer_strcat(output, id);
-       buffer_strcat(output, " ");
-       buffer_print_llu(output, usec);
-       buffer_strcat(output, "\n");
+    // fprintf(stdout, "BEGIN %s.%s %llu\n", type, id, usec);
+    buffer_strcat(output, "BEGIN ");
+    buffer_strcat(output, type);
+    buffer_strcat(output, ".");
+    buffer_strcat(output, id);
+    buffer_strcat(output, " ");
+    buffer_print_llu(output, usec);
+    buffer_strcat(output, "\n");
 }
 
 static inline void send_SET(const char *name, unsigned long long value) {
-       // fprintf(stdout, "SET %s = %llu\n", name, value);
-       buffer_strcat(output, "SET ");
-       buffer_strcat(output, name);
-       buffer_strcat(output, " = ");
-       buffer_print_llu(output, value);
-       buffer_strcat(output, "\n");
+    // fprintf(stdout, "SET %s = %llu\n", name, value);
+    buffer_strcat(output, "SET ");
+    buffer_strcat(output, name);
+    buffer_strcat(output, " = ");
+    buffer_print_llu(output, value);
+    buffer_strcat(output, "\n");
 }
 
 static inline void send_END(void) {
-       // fprintf(stdout, "END\n");
-       buffer_strcat(output, "END\n");
+    // fprintf(stdout, "END\n");
+    buffer_strcat(output, "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;
 
 unsigned long long send_resource_usage_to_netdata() {
-       static struct timeval last = { 0, 0 };
-       static struct rusage me_last;
-
-       struct timeval now;
-       struct rusage me;
-
-       unsigned long long usec;
-       unsigned long long cpuuser;
-       unsigned long long cpusyst;
-
-       if(!last.tv_sec) {
-               gettimeofday(&last, NULL);
-               getrusage(RUSAGE_SELF, &me_last);
-
-               // the first time, give a zero to allow
-               // netdata calibrate to the current time
-               // usec = update_every * 1000000ULL;
-               usec = 0ULL;
-               cpuuser = 0;
-               cpusyst = 0;
-       }
-       else {
-               gettimeofday(&now, NULL);
-               getrusage(RUSAGE_SELF, &me);
-
-               usec = usecdiff(&now, &last);
-               cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
-               cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
-
-               bcopy(&now, &last, sizeof(struct timeval));
-               bcopy(&me, &me_last, sizeof(struct rusage));
-       }
-
-       buffer_sprintf(output,
-               "BEGIN netdata.apps_cpu %llu\n"
-               "SET user = %llu\n"
-               "SET system = %llu\n"
-               "END\n"
-               "BEGIN netdata.apps_files %llu\n"
-               "SET files = %llu\n"
-               "SET pids = %ld\n"
-               "SET fds = %d\n"
-               "SET targets = %ld\n"
-               "END\n"
-               "BEGIN netdata.apps_fix %llu\n"
-               "SET utime = %llu\n"
-               "SET stime = %llu\n"
-               "SET gtime = %llu\n"
-               "SET minflt = %llu\n"
-               "SET majflt = %llu\n"
-               "END\n"
-               , usec
-               , cpuuser
-               , cpusyst
-               , usec
-               , file_counter
-               , all_pids_count
-               , all_files_len
-               , apps_groups_targets
-               , usec
-               , (unsigned long long)(utime_fix_ratio   * 100 * RATES_DETAIL)
-               , (unsigned long long)(stime_fix_ratio   * 100 * RATES_DETAIL)
-               , (unsigned long long)(gtime_fix_ratio   * 100 * RATES_DETAIL)
-               , (unsigned long long)(minflt_fix_ratio  * 100 * RATES_DETAIL)
-               , (unsigned long long)(majflt_fix_ratio  * 100 * RATES_DETAIL)
-               );
-
-       if(include_exited_childs)
-               buffer_sprintf(output,
-                       "BEGIN netdata.apps_children_fix %llu\n"
-                       "SET cutime = %llu\n"
-                       "SET cstime = %llu\n"
-                       "SET cgtime = %llu\n"
-                       "SET cminflt = %llu\n"
-                       "SET cmajflt = %llu\n"
-                       "END\n"
-                       , usec
-                       , (unsigned long long)(cutime_fix_ratio  * 100 * RATES_DETAIL)
-                       , (unsigned long long)(cstime_fix_ratio  * 100 * RATES_DETAIL)
-                       , (unsigned long long)(cgtime_fix_ratio  * 100 * RATES_DETAIL)
-                       , (unsigned long long)(cminflt_fix_ratio * 100 * RATES_DETAIL)
-                       , (unsigned long long)(cmajflt_fix_ratio * 100 * RATES_DETAIL)
-                       );
-
-       return usec;
+    static struct timeval last = { 0, 0 };
+    static struct rusage me_last;
+
+    struct timeval now;
+    struct rusage me;
+
+    unsigned long long usec;
+    unsigned long long cpuuser;
+    unsigned long long cpusyst;
+
+    if(!last.tv_sec) {
+        gettimeofday(&last, NULL);
+        getrusage(RUSAGE_SELF, &me_last);
+
+        // the first time, give a zero to allow
+        // netdata calibrate to the current time
+        // usec = update_every * 1000000ULL;
+        usec = 0ULL;
+        cpuuser = 0;
+        cpusyst = 0;
+    }
+    else {
+        gettimeofday(&now, NULL);
+        getrusage(RUSAGE_SELF, &me);
+
+        usec = usec_dt(&now, &last);
+        cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
+        cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
+
+        bcopy(&now, &last, sizeof(struct timeval));
+        bcopy(&me, &me_last, sizeof(struct rusage));
+    }
+
+    buffer_sprintf(output,
+        "BEGIN netdata.apps_cpu %llu\n"
+        "SET user = %llu\n"
+        "SET system = %llu\n"
+        "END\n"
+        "BEGIN netdata.apps_files %llu\n"
+        "SET files = %llu\n"
+        "SET pids = %ld\n"
+        "SET fds = %d\n"
+        "SET targets = %ld\n"
+        "END\n"
+        "BEGIN netdata.apps_fix %llu\n"
+        "SET utime = %llu\n"
+        "SET stime = %llu\n"
+        "SET gtime = %llu\n"
+        "SET minflt = %llu\n"
+        "SET majflt = %llu\n"
+        "END\n"
+        , usec
+        , cpuuser
+        , cpusyst
+        , usec
+        , file_counter
+        , all_pids_count
+        , all_files_len
+        , apps_groups_targets
+        , usec
+        , (unsigned long long)(utime_fix_ratio   * 100 * RATES_DETAIL)
+        , (unsigned long long)(stime_fix_ratio   * 100 * RATES_DETAIL)
+        , (unsigned long long)(gtime_fix_ratio   * 100 * RATES_DETAIL)
+        , (unsigned long long)(minflt_fix_ratio  * 100 * RATES_DETAIL)
+        , (unsigned long long)(majflt_fix_ratio  * 100 * RATES_DETAIL)
+        );
+
+    if(include_exited_childs)
+        buffer_sprintf(output,
+            "BEGIN netdata.apps_children_fix %llu\n"
+            "SET cutime = %llu\n"
+            "SET cstime = %llu\n"
+            "SET cgtime = %llu\n"
+            "SET cminflt = %llu\n"
+            "SET cmajflt = %llu\n"
+            "END\n"
+            , usec
+            , (unsigned long long)(cutime_fix_ratio  * 100 * RATES_DETAIL)
+            , (unsigned long long)(cstime_fix_ratio  * 100 * RATES_DETAIL)
+            , (unsigned long long)(cgtime_fix_ratio  * 100 * RATES_DETAIL)
+            , (unsigned long long)(cminflt_fix_ratio * 100 * RATES_DETAIL)
+            , (unsigned long long)(cmajflt_fix_ratio * 100 * RATES_DETAIL)
+            );
+
+    return usec;
 }
 
 void normalize_data(struct target *root) {
-       struct target *w;
-
-       // childs processing introduces spikes
-       // here we try to eliminate them by disabling childs processing either for specific dimensions
-       // or entirely. Of course, either way, we disable it just a single iteration.
-
-       unsigned long long max = processors * hz * RATES_DETAIL;
-       unsigned long long utime = 0, cutime = 0, stime = 0, cstime = 0, gtime = 0, cgtime = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
-
-       if(global_utime > max) global_utime = max;
-       if(global_stime > max) global_stime = max;
-       if(global_gtime > max) global_gtime = max;
-
-       for(w = root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               utime   += w->utime;
-               stime   += w->stime;
-               gtime   += w->gtime;
-               cutime  += w->cutime;
-               cstime  += w->cstime;
-               cgtime  += w->cgtime;
-
-               minflt  += w->minflt;
-               majflt  += w->majflt;
-               cminflt += w->cminflt;
-               cmajflt += w->cmajflt;
-       }
-
-       if((global_utime || global_stime || global_gtime) && (utime || stime || gtime)) {
-               if(global_utime + global_stime + global_gtime > utime + cutime + stime + cstime + gtime + cgtime) {
-                       // everything we collected fits
-                       utime_fix_ratio  =
-                       stime_fix_ratio  =
-                       gtime_fix_ratio  =
-                       cutime_fix_ratio =
-                       cstime_fix_ratio =
-                       cgtime_fix_ratio = 1.0; //(double)(global_utime + global_stime) / (double)(utime + cutime + stime + cstime);
-               }
-               else if(global_utime + global_stime > utime + stime) {
-                       // childrens resources are too high
-                       // lower only the children resources
-                       utime_fix_ratio  =
-                       stime_fix_ratio  =
-                       gtime_fix_ratio  = 1.0;
-                       cutime_fix_ratio =
-                       cstime_fix_ratio =
-                       cgtime_fix_ratio = (double)((global_utime + global_stime) - (utime + stime)) / (double)(cutime + cstime);
-               }
-               else {
-                       // even running processes are unrealistic
-                       // zero the children resources
-                       // lower the running processes resources
-                       utime_fix_ratio  =
-                       stime_fix_ratio  =
-                       gtime_fix_ratio  = (double)(global_utime + global_stime) / (double)(utime + stime);
-                       cutime_fix_ratio =
-                       cstime_fix_ratio =
-                       cgtime_fix_ratio = 0.0;
-               }
-       }
-       else {
-               utime_fix_ratio  =
-               stime_fix_ratio  =
-               gtime_fix_ratio  =
-               cutime_fix_ratio =
-               cstime_fix_ratio =
-               cgtime_fix_ratio = 0.0;
-       }
-
-       if(utime_fix_ratio  > 1.0) utime_fix_ratio  = 1.0;
-       if(cutime_fix_ratio > 1.0) cutime_fix_ratio = 1.0;
-       if(stime_fix_ratio  > 1.0) stime_fix_ratio  = 1.0;
-       if(cstime_fix_ratio > 1.0) cstime_fix_ratio = 1.0;
-       if(gtime_fix_ratio  > 1.0) gtime_fix_ratio  = 1.0;
-       if(cgtime_fix_ratio > 1.0) cgtime_fix_ratio = 1.0;
-
-       // if(utime_fix_ratio  < 0.0) utime_fix_ratio  = 0.0;
-       // if(cutime_fix_ratio < 0.0) cutime_fix_ratio = 0.0;
-       // if(stime_fix_ratio  < 0.0) stime_fix_ratio  = 0.0;
-       // if(cstime_fix_ratio < 0.0) cstime_fix_ratio = 0.0;
-       // if(gtime_fix_ratio  < 0.0) gtime_fix_ratio  = 0.0;
-       // if(cgtime_fix_ratio < 0.0) cgtime_fix_ratio = 0.0;
-
-       // FIXME
-       // we use cpu time to normalize page faults
-       // the problem is that to find the proper max values
-       // for page faults we have to parse /proc/vmstat
-       // which is quite big to do it again (netdata does it already)
-       //
-       // a better solution could be to somehow have netdata
-       // do this normalization for us
-
-       if(utime || stime || gtime)
-               majflt_fix_ratio =
-               minflt_fix_ratio = (double)(utime * utime_fix_ratio + stime * stime_fix_ratio + gtime * gtime_fix_ratio) / (double)(utime + stime + gtime);
-       else
-               minflt_fix_ratio =
-               majflt_fix_ratio = 1.0;
-
-       if(cutime || cstime || cgtime)
-               cmajflt_fix_ratio =
-               cminflt_fix_ratio = (double)(cutime * cutime_fix_ratio + cstime * cstime_fix_ratio + cgtime * cgtime_fix_ratio) / (double)(cutime + cstime + cgtime);
-       else
-               cminflt_fix_ratio =
-               cmajflt_fix_ratio = 1.0;
-
-       // the report
-
-       if(unlikely(debug)) {
-               fprintf(stderr,
-                       "SYSTEM: u=%llu s=%llu g=%llu "
-                       "COLLECTED: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
-                       "DELTA: u=%lld s=%lld g=%lld "
-                       "FIX: u=%0.2f s=%0.2f g=%0.2f cu=%0.2f cs=%0.2f cg=%0.2f "
-                       "FINALLY: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
-                       "\n"
-                       , global_utime
-                       , global_stime
-                       , global_gtime
-                       , utime
-                       , stime
-                       , gtime
-                       , cutime
-                       , cstime
-                       , cgtime
-                       , (long long)utime + (long long)cutime - (long long)global_utime
-                       , (long long)stime + (long long)cstime - (long long)global_stime
-                       , (long long)gtime + (long long)cgtime - (long long)global_gtime
-                       , utime_fix_ratio
-                       , stime_fix_ratio
-                       , gtime_fix_ratio
-                       , cutime_fix_ratio
-                       , cstime_fix_ratio
-                       , cgtime_fix_ratio
-                       , (unsigned long long)(utime * utime_fix_ratio)
-                       , (unsigned long long)(stime * stime_fix_ratio)
-                       , (unsigned long long)(gtime * gtime_fix_ratio)
-                       , (unsigned long long)(cutime * cutime_fix_ratio)
-                       , (unsigned long long)(cstime * cstime_fix_ratio)
-                       , (unsigned long long)(cgtime * cgtime_fix_ratio)
-                       );
-       }
+    struct target *w;
+
+    // childs processing introduces spikes
+    // here we try to eliminate them by disabling childs processing either for specific dimensions
+    // or entirely. Of course, either way, we disable it just a single iteration.
+
+    unsigned long long max = processors * hz * RATES_DETAIL;
+    unsigned long long utime = 0, cutime = 0, stime = 0, cstime = 0, gtime = 0, cgtime = 0, minflt = 0, cminflt = 0, majflt = 0, cmajflt = 0;
+
+    if(global_utime > max) global_utime = max;
+    if(global_stime > max) global_stime = max;
+    if(global_gtime > max) global_gtime = max;
+
+    for(w = root; w ; w = w->next) {
+        if(w->target || (!w->processes && !w->exposed)) continue;
+
+        utime   += w->utime;
+        stime   += w->stime;
+        gtime   += w->gtime;
+        cutime  += w->cutime;
+        cstime  += w->cstime;
+        cgtime  += w->cgtime;
+
+        minflt  += w->minflt;
+        majflt  += w->majflt;
+        cminflt += w->cminflt;
+        cmajflt += w->cmajflt;
+    }
+
+    if((global_utime || global_stime || global_gtime) && (utime || stime || gtime)) {
+        if(global_utime + global_stime + global_gtime > utime + cutime + stime + cstime + gtime + cgtime) {
+            // everything we collected fits
+            utime_fix_ratio  =
+            stime_fix_ratio  =
+            gtime_fix_ratio  =
+            cutime_fix_ratio =
+            cstime_fix_ratio =
+            cgtime_fix_ratio = 1.0; //(double)(global_utime + global_stime) / (double)(utime + cutime + stime + cstime);
+        }
+        else if(global_utime + global_stime > utime + stime) {
+            // childrens resources are too high
+            // lower only the children resources
+            utime_fix_ratio  =
+            stime_fix_ratio  =
+            gtime_fix_ratio  = 1.0;
+            cutime_fix_ratio =
+            cstime_fix_ratio =
+            cgtime_fix_ratio = (double)((global_utime + global_stime) - (utime + stime)) / (double)(cutime + cstime);
+        }
+        else {
+            // even running processes are unrealistic
+            // zero the children resources
+            // lower the running processes resources
+            utime_fix_ratio  =
+            stime_fix_ratio  =
+            gtime_fix_ratio  = (double)(global_utime + global_stime) / (double)(utime + stime);
+            cutime_fix_ratio =
+            cstime_fix_ratio =
+            cgtime_fix_ratio = 0.0;
+        }
+    }
+    else {
+        utime_fix_ratio  =
+        stime_fix_ratio  =
+        gtime_fix_ratio  =
+        cutime_fix_ratio =
+        cstime_fix_ratio =
+        cgtime_fix_ratio = 0.0;
+    }
+
+    if(utime_fix_ratio  > 1.0) utime_fix_ratio  = 1.0;
+    if(cutime_fix_ratio > 1.0) cutime_fix_ratio = 1.0;
+    if(stime_fix_ratio  > 1.0) stime_fix_ratio  = 1.0;
+    if(cstime_fix_ratio > 1.0) cstime_fix_ratio = 1.0;
+    if(gtime_fix_ratio  > 1.0) gtime_fix_ratio  = 1.0;
+    if(cgtime_fix_ratio > 1.0) cgtime_fix_ratio = 1.0;
+
+    // if(utime_fix_ratio  < 0.0) utime_fix_ratio  = 0.0;
+    // if(cutime_fix_ratio < 0.0) cutime_fix_ratio = 0.0;
+    // if(stime_fix_ratio  < 0.0) stime_fix_ratio  = 0.0;
+    // if(cstime_fix_ratio < 0.0) cstime_fix_ratio = 0.0;
+    // if(gtime_fix_ratio  < 0.0) gtime_fix_ratio  = 0.0;
+    // if(cgtime_fix_ratio < 0.0) cgtime_fix_ratio = 0.0;
+
+    // FIXME
+    // we use cpu time to normalize page faults
+    // the problem is that to find the proper max values
+    // for page faults we have to parse /proc/vmstat
+    // which is quite big to do it again (netdata does it already)
+    //
+    // a better solution could be to somehow have netdata
+    // do this normalization for us
+
+    if(utime || stime || gtime)
+        majflt_fix_ratio =
+        minflt_fix_ratio = (double)(utime * utime_fix_ratio + stime * stime_fix_ratio + gtime * gtime_fix_ratio) / (double)(utime + stime + gtime);
+    else
+        minflt_fix_ratio =
+        majflt_fix_ratio = 1.0;
+
+    if(cutime || cstime || cgtime)
+        cmajflt_fix_ratio =
+        cminflt_fix_ratio = (double)(cutime * cutime_fix_ratio + cstime * cstime_fix_ratio + cgtime * cgtime_fix_ratio) / (double)(cutime + cstime + cgtime);
+    else
+        cminflt_fix_ratio =
+        cmajflt_fix_ratio = 1.0;
+
+    // the report
+
+    if(unlikely(debug)) {
+        fprintf(stderr,
+            "SYSTEM: u=%llu s=%llu g=%llu "
+            "COLLECTED: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
+            "DELTA: u=%lld s=%lld g=%lld "
+            "FIX: u=%0.2f s=%0.2f g=%0.2f cu=%0.2f cs=%0.2f cg=%0.2f "
+            "FINALLY: u=%llu s=%llu g=%llu cu=%llu cs=%llu cg=%llu "
+            "\n"
+            , global_utime
+            , global_stime
+            , global_gtime
+            , utime
+            , stime
+            , gtime
+            , cutime
+            , cstime
+            , cgtime
+            , (long long)utime + (long long)cutime - (long long)global_utime
+            , (long long)stime + (long long)cstime - (long long)global_stime
+            , (long long)gtime + (long long)cgtime - (long long)global_gtime
+            , utime_fix_ratio
+            , stime_fix_ratio
+            , gtime_fix_ratio
+            , cutime_fix_ratio
+            , cstime_fix_ratio
+            , cgtime_fix_ratio
+            , (unsigned long long)(utime * utime_fix_ratio)
+            , (unsigned long long)(stime * stime_fix_ratio)
+            , (unsigned long long)(gtime * gtime_fix_ratio)
+            , (unsigned long long)(cutime * cutime_fix_ratio)
+            , (unsigned long long)(cstime * cstime_fix_ratio)
+            , (unsigned long long)(cgtime * cgtime_fix_ratio)
+            );
+    }
 }
 
 void send_collected_data_to_netdata(struct target *root, const char *type, unsigned long long usec) {
-       struct target *w;
-
-       send_BEGIN(type, "cpu", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (unsigned long long)(w->stime * stime_fix_ratio) + (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio) + (unsigned long long)(w->cstime * cstime_fix_ratio) + (unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
-       }
-       send_END();
-
-       send_BEGIN(type, "cpu_user", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio)):0ULL));
-       }
-       send_END();
-
-       send_BEGIN(type, "cpu_system", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, (unsigned long long)(w->stime * stime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cstime * cstime_fix_ratio)):0ULL));
-       }
-       send_END();
-
-       if(show_guest_time) {
-               send_BEGIN(type, "cpu_guest", usec);
-               for (w = root; w ; w = w->next) {
-                       if(unlikely(w->exposed))
-                               send_SET(w->name, (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
-               }
-               send_END();
-       }
-
-       send_BEGIN(type, "threads", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->num_threads);
-       }
-       send_END();
-
-       send_BEGIN(type, "processes", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->processes);
-       }
-       send_END();
-
-       send_BEGIN(type, "mem", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, (w->statm_resident > w->statm_share)?(w->statm_resident - w->statm_share):0ULL);
-       }
-       send_END();
-
-       send_BEGIN(type, "minor_faults", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, (unsigned long long)(w->minflt * minflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cminflt * cminflt_fix_ratio)):0ULL));
-       }
-       send_END();
-
-       send_BEGIN(type, "major_faults", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, (unsigned long long)(w->majflt * majflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cmajflt * cmajflt_fix_ratio)):0ULL));
-       }
-       send_END();
-
-       send_BEGIN(type, "lreads", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->io_logical_bytes_read);
-       }
-       send_END();
-
-       send_BEGIN(type, "lwrites", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->io_logical_bytes_written);
-       }
-       send_END();
-
-       send_BEGIN(type, "preads", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->io_storage_bytes_read);
-       }
-       send_END();
-
-       send_BEGIN(type, "pwrites", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->io_storage_bytes_written);
-       }
-       send_END();
-
-       send_BEGIN(type, "files", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->openfiles);
-       }
-       send_END();
-
-       send_BEGIN(type, "sockets", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->opensockets);
-       }
-       send_END();
-
-       send_BEGIN(type, "pipes", usec);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       send_SET(w->name, w->openpipes);
-       }
-       send_END();
+    struct target *w;
+
+    send_BEGIN(type, "cpu", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (unsigned long long)(w->stime * stime_fix_ratio) + (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio) + (unsigned long long)(w->cstime * cstime_fix_ratio) + (unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
+    }
+    send_END();
+
+    send_BEGIN(type, "cpu_user", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, (unsigned long long)(w->utime * utime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cutime * cutime_fix_ratio)):0ULL));
+    }
+    send_END();
+
+    send_BEGIN(type, "cpu_system", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, (unsigned long long)(w->stime * stime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cstime * cstime_fix_ratio)):0ULL));
+    }
+    send_END();
+
+    if(show_guest_time) {
+        send_BEGIN(type, "cpu_guest", usec);
+        for (w = root; w ; w = w->next) {
+            if(unlikely(w->exposed))
+                send_SET(w->name, (unsigned long long)(w->gtime * gtime_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cgtime * cgtime_fix_ratio)):0ULL));
+        }
+        send_END();
+    }
+
+    send_BEGIN(type, "threads", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, w->num_threads);
+    }
+    send_END();
+
+    send_BEGIN(type, "processes", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, w->processes);
+    }
+    send_END();
+
+    send_BEGIN(type, "mem", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, (w->statm_resident > w->statm_share)?(w->statm_resident - w->statm_share):0ULL);
+    }
+    send_END();
+
+    send_BEGIN(type, "minor_faults", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, (unsigned long long)(w->minflt * minflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cminflt * cminflt_fix_ratio)):0ULL));
+    }
+    send_END();
+
+    send_BEGIN(type, "major_faults", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, (unsigned long long)(w->majflt * majflt_fix_ratio) + (include_exited_childs?((unsigned long long)(w->cmajflt * cmajflt_fix_ratio)):0ULL));
+    }
+    send_END();
+
+    send_BEGIN(type, "lreads", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, w->io_logical_bytes_read);
+    }
+    send_END();
+
+    send_BEGIN(type, "lwrites", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, w->io_logical_bytes_written);
+    }
+    send_END();
+
+    send_BEGIN(type, "preads", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, w->io_storage_bytes_read);
+    }
+    send_END();
+
+    send_BEGIN(type, "pwrites", usec);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            send_SET(w->name, w->io_storage_bytes_written);
+    }
+    send_END();
+
+    if(enable_file_charts) {
+        send_BEGIN(type, "files", usec);
+        for (w = root; w; w = w->next) {
+            if (unlikely(w->exposed))
+                send_SET(w->name, w->openfiles);
+        }
+        send_END();
+
+        send_BEGIN(type, "sockets", usec);
+        for (w = root; w; w = w->next) {
+            if (unlikely(w->exposed))
+                send_SET(w->name, w->opensockets);
+        }
+        send_END();
+
+        send_BEGIN(type, "pipes", usec);
+        for (w = root; w; w = w->next) {
+            if (unlikely(w->exposed))
+                send_SET(w->name, w->openpipes);
+        }
+        send_END();
+    }
 }
 
 
@@ -2607,121 +2566,126 @@ void send_collected_data_to_netdata(struct target *root, const char *type, unsig
 
 void send_charts_updates_to_netdata(struct target *root, const char *type, const char *title)
 {
-       struct target *w;
-       int newly_added = 0;
-
-       for(w = root ; w ; w = w->next) {
-               if (w->target) continue;
-
-               if (!w->exposed && w->processes) {
-                       newly_added++;
-                       w->exposed = 1;
-                       if (debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
-               }
-       }
-
-       // nothing more to show
-       if(!newly_added && show_guest_time == show_guest_time_old) return;
-
-       // we have something new to show
-       // update the charts
-       buffer_sprintf(output, "CHART %s.cpu '' '%s CPU Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : "");
-       }
-
-       buffer_sprintf(output, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
-       }
-
-       buffer_sprintf(output, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       buffer_sprintf(output, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       buffer_sprintf(output, "CHART %s.cpu_user '' '%s CPU User Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
-       }
-
-       buffer_sprintf(output, "CHART %s.cpu_system '' '%s CPU System Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
-       }
-
-       if(show_guest_time) {
-               buffer_sprintf(output, "CHART %s.cpu_guest '' '%s CPU Guest Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20022 %d\n", type, title, (processors * 100), processors, (processors > 1) ? "s" : "", type, update_every);
-               for (w = root; w; w = w->next) {
-                       if(unlikely(w->exposed))
-                               buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
-               }
-       }
-
-       buffer_sprintf(output, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
-       }
-
-       buffer_sprintf(output, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
-       }
-
-       buffer_sprintf(output, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
-       }
-
-       buffer_sprintf(output, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
-       }
-
-       buffer_sprintf(output, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
-       }
-
-       buffer_sprintf(output, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
-       }
-
-       buffer_sprintf(output, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       buffer_sprintf(output, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       buffer_sprintf(output, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type, title, type, update_every);
-       for (w = root; w ; w = w->next) {
-               if(unlikely(w->exposed))
-                       buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
+    struct target *w;
+    int newly_added = 0;
+
+    for(w = root ; w ; w = w->next) {
+        if (w->target) continue;
+
+        if (!w->exposed && w->processes) {
+            newly_added++;
+            w->exposed = 1;
+            if (debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
+        }
+    }
+
+    // nothing more to show
+    if(!newly_added && show_guest_time == show_guest_time_old) return;
+
+    // we have something new to show
+    // update the charts
+    buffer_sprintf(output, "CHART %s.cpu '' '%s CPU Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu stacked 20001 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu %s\n", w->name, hz * RATES_DETAIL / 100, w->hidden ? "hidden" : "");
+    }
+
+    buffer_sprintf(output, "CHART %s.mem '' '%s Dedicated Memory (w/o shared)' 'MB' mem %s.mem stacked 20003 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
+    }
+
+    buffer_sprintf(output, "CHART %s.threads '' '%s Threads' 'threads' processes %s.threads stacked 20005 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+    }
+
+    buffer_sprintf(output, "CHART %s.processes '' '%s Processes' 'processes' processes %s.processes stacked 20004 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+    }
+
+    buffer_sprintf(output, "CHART %s.cpu_user '' '%s CPU User Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_user stacked 20020 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
+    }
+
+    buffer_sprintf(output, "CHART %s.cpu_system '' '%s CPU System Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20021 %d\n", type, title, (processors * 100), processors, (processors>1)?"s":"", type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
+    }
+
+    if(show_guest_time) {
+        buffer_sprintf(output, "CHART %s.cpu_guest '' '%s CPU Guest Time (%d%% = %d core%s)' 'cpu time %%' cpu %s.cpu_system stacked 20022 %d\n", type, title, (processors * 100), processors, (processors > 1) ? "s" : "", type, update_every);
+        for (w = root; w; w = w->next) {
+            if(unlikely(w->exposed))
+                buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, hz * RATES_DETAIL / 100LLU);
+        }
+    }
+
+    buffer_sprintf(output, "CHART %s.major_faults '' '%s Major Page Faults (swap read)' 'page faults/s' swap %s.major_faults stacked 20010 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
+    }
+
+    buffer_sprintf(output, "CHART %s.minor_faults '' '%s Minor Page Faults' 'page faults/s' mem %s.minor_faults stacked 20011 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL);
+    }
+
+    buffer_sprintf(output, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+    }
+
+    buffer_sprintf(output, "CHART %s.lwrites '' '%s I/O Logical Writes' 'kilobytes/s' disk %s.lwrites stacked 20042 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+    }
+
+    buffer_sprintf(output, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+    }
+
+    buffer_sprintf(output, "CHART %s.pwrites '' '%s Disk Writes' 'kilobytes/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every);
+    for (w = root; w ; w = w->next) {
+        if(unlikely(w->exposed))
+            buffer_sprintf(output, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL);
+    }
+
+    if(enable_file_charts) {
+        buffer_sprintf(output, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type,
+                       title, type, update_every);
+        for (w = root; w; w = w->next) {
+            if (unlikely(w->exposed))
+                buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+        }
+
+        buffer_sprintf(output, "CHART %s.sockets '' '%s Open Sockets' 'open sockets' net %s.sockets stacked 20051 %d\n",
+                       type, title, type, update_every);
+        for (w = root; w; w = w->next) {
+            if (unlikely(w->exposed))
+                buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+        }
+
+        buffer_sprintf(output, "CHART %s.pipes '' '%s Pipes' 'open pipes' processes %s.pipes stacked 20053 %d\n", type,
+                       title, type, update_every);
+        for (w = root; w; w = w->next) {
+            if (unlikely(w->exposed))
+                buffer_sprintf(output, "DIMENSION %s '' absolute 1 1\n", w->name);
+        }
+    }
 }
 
 
@@ -2730,33 +2694,33 @@ void send_charts_updates_to_netdata(struct target *root, const char *type, const
 
 void parse_args(int argc, char **argv)
 {
-       int i, freq = 0;
-       char *name = NULL;
-
-       for(i = 1; i < argc; i++) {
-               if(!freq) {
-                       int n = atoi(argv[i]);
-                       if(n > 0) {
-                               freq = n;
-                               continue;
-                       }
-               }
-
-               if(strcmp("debug", argv[i]) == 0) {
-                       debug = 1;
-                       // debug_flags = 0xffffffff;
-                       continue;
-               }
-
-               if(strcmp("no-childs", argv[i]) == 0 || strcmp("without-childs", argv[i]) == 0) {
-                       include_exited_childs = 0;
-                       continue;
-               }
-
-               if(strcmp("with-childs", argv[i]) == 0) {
-                       include_exited_childs = 1;
-                       continue;
-               }
+    int i, freq = 0;
+    char *name = NULL;
+
+    for(i = 1; i < argc; i++) {
+        if(!freq) {
+            int n = atoi(argv[i]);
+            if(n > 0) {
+                freq = n;
+                continue;
+            }
+        }
+
+        if(strcmp("debug", argv[i]) == 0) {
+            debug = 1;
+            // debug_flags = 0xffffffff;
+            continue;
+        }
+
+        if(strcmp("no-childs", argv[i]) == 0 || strcmp("without-childs", argv[i]) == 0) {
+            include_exited_childs = 0;
+            continue;
+        }
+
+        if(strcmp("with-childs", argv[i]) == 0) {
+            include_exited_childs = 1;
+            continue;
+        }
 
         if(strcmp("with-guest", argv[i]) == 0) {
             enable_guest_charts = 1;
@@ -2768,7 +2732,17 @@ void parse_args(int argc, char **argv)
             continue;
         }
 
-               if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
+        if(strcmp("with-files", argv[i]) == 0) {
+            enable_file_charts = 1;
+            continue;
+        }
+
+        if(strcmp("no-files", argv[i]) == 0 || strcmp("without-files", argv[i]) == 0) {
+            enable_file_charts = 0;
+            continue;
+        }
+
+        if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
             fprintf(stderr,
                     "apps.plugin\n"
                     "(C) 2016 Costa Tsaousis"
@@ -2790,6 +2764,10 @@ void parse_args(int argc, char **argv)
                     "without-guest     enable / disable reporting guest charts\n"
                     "                  (default is disabled)\n"
                     "\n"
+                    "with-files\n"
+                    "without-files     enable / disable reporting files, sockets, pipes\n"
+                    "                  (default is enabled)\n"
+                    "\n"
                     "NAME              read apps_NAME.conf instead of\n"
                     "                  apps_groups.conf\n"
                     "                  (default NAME=groups)\n"
@@ -2798,175 +2776,163 @@ void parse_args(int argc, char **argv)
         }
 
         if(!name) {
-                       name = argv[i];
-                       continue;
-               }
+            name = argv[i];
+            continue;
+        }
 
-               error("Cannot understand option %s", argv[i]);
-               exit(1);
-       }
+        error("Cannot understand option %s", argv[i]);
+        exit(1);
+    }
 
-       if(freq > 0) update_every = freq;
-       if(!name) name = "groups";
+    if(freq > 0) update_every = freq;
+    if(!name) name = "groups";
 
-       if(read_apps_groups_conf(name)) {
-               error("Cannot read process groups %s", name);
-               exit(1);
-       }
+    if(read_apps_groups_conf(name)) {
+        error("Cannot read process groups %s", name);
+        exit(1);
+    }
 }
 
 int main(int argc, char **argv)
 {
-       // debug_flags = D_PROCFILE;
+    // debug_flags = D_PROCFILE;
 
-       // set the name for logging
-       program_name = "apps.plugin";
+    // set the name for logging
+    program_name = "apps.plugin";
 
-       // disable syslog for apps.plugin
-       error_log_syslog = 0;
+    info("started on pid %d", getpid());
 
-       // set errors flood protection to 100 logs per hour
-       error_log_errors_per_period = 100;
-       error_log_throttle_period = 3600;
+    // disable syslog for apps.plugin
+    error_log_syslog = 0;
 
-       host_prefix = getenv("NETDATA_HOST_PREFIX");
-       if(host_prefix == NULL) {
-               info("NETDATA_HOST_PREFIX is not passed from netdata");
-               host_prefix = "";
-       }
-       else info("Found NETDATA_HOST_PREFIX='%s'", host_prefix);
+    // set errors flood protection to 100 logs per hour
+    error_log_errors_per_period = 100;
+    error_log_throttle_period = 3600;
 
-       config_dir = getenv("NETDATA_CONFIG_DIR");
-       if(config_dir == NULL) {
-               info("NETDATA_CONFIG_DIR is not passed from netdata");
-               config_dir = CONFIG_DIR;
-       }
-       else info("Found NETDATA_CONFIG_DIR='%s'", config_dir);
+    host_prefix = getenv("NETDATA_HOST_PREFIX");
+    if(host_prefix == NULL) {
+        // info("NETDATA_HOST_PREFIX is not passed from netdata");
+        host_prefix = "";
+    }
+    // else info("Found NETDATA_HOST_PREFIX='%s'", host_prefix);
+
+    config_dir = getenv("NETDATA_CONFIG_DIR");
+    if(config_dir == NULL) {
+        // info("NETDATA_CONFIG_DIR is not passed from netdata");
+        config_dir = CONFIG_DIR;
+    }
+    // else info("Found NETDATA_CONFIG_DIR='%s'", config_dir);
 
 #ifdef NETDATA_INTERNAL_CHECKS
-       if(debug_flags != 0) {
-               struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
-               if(setrlimit(RLIMIT_CORE, &rl) != 0)
-                       info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
-               prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
-       }
+    if(debug_flags != 0) {
+        struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
+        if(setrlimit(RLIMIT_CORE, &rl) != 0)
+            info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+        prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+    }
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-       procfile_adaptive_initial_allocation = 1;
-
-       time_t started_t = time(NULL);
-       time_t current_t;
-       get_HZ();
-       pid_max = get_system_pid_max();
-       processors = get_system_cpus();
-
-       parse_args(argc, argv);
-
-       all_pids_sortlist = calloc(sizeof(pid_t), (size_t)pid_max);
-       if(!all_pids_sortlist) {
-               error("Cannot allocate %zu bytes of memory.", sizeof(pid_t) * pid_max);
-               printf("DISABLE\n");
-               exit(1);
-       }
-
-       all_pids = calloc(sizeof(struct pid_stat *), (size_t) pid_max);
-       if(!all_pids) {
-               error("Cannot allocate %zu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
-               printf("DISABLE\n");
-               exit(1);
-       }
-
-       output = buffer_create(1024);
-       if(!output)
-               fatal("Cannot create BUFFER.");
-
-       buffer_sprintf(output,
-               "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
-               "DIMENSION user '' incremental 1 1000\n"
-               "DIMENSION system '' incremental 1 1000\n"
-               "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
-               "DIMENSION files '' incremental 1 1\n"
-               "DIMENSION pids '' absolute 1 1\n"
-               "DIMENSION fds '' absolute 1 1\n"
-               "DIMENSION targets '' absolute 1 1\n"
-               "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n"
-               "DIMENSION utime '' absolute 1 %2$llu\n"
-               "DIMENSION stime '' absolute 1 %2$llu\n"
-               "DIMENSION gtime '' absolute 1 %2$llu\n"
-               "DIMENSION minflt '' absolute 1 %2$llu\n"
-               "DIMENSION majflt '' absolute 1 %2$llu\n"
-               , update_every
-               , RATES_DETAIL
-               );
-
-       if(include_exited_childs)
-               buffer_sprintf(output,
-                       "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n"
-                       "DIMENSION cutime '' absolute 1 %2$llu\n"
-                       "DIMENSION cstime '' absolute 1 %2$llu\n"
-                       "DIMENSION cgtime '' absolute 1 %2$llu\n"
-                       "DIMENSION cminflt '' absolute 1 %2$llu\n"
-                       "DIMENSION cmajflt '' absolute 1 %2$llu\n"
-                       , update_every
-                       , RATES_DETAIL
-                       );
+    procfile_adaptive_initial_allocation = 1;
+
+    time_t started_t = time(NULL);
+    time_t current_t;
+    get_HZ();
+    pid_max = get_system_pid_max();
+    processors = get_system_cpus();
+
+    parse_args(argc, argv);
+
+    all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max);
+    all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max);
+
+    output = buffer_create(1024);
+    buffer_sprintf(output,
+        "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n"
+        "DIMENSION user '' incremental 1 1000\n"
+        "DIMENSION system '' incremental 1 1000\n"
+        "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n"
+        "DIMENSION files '' incremental 1 1\n"
+        "DIMENSION pids '' absolute 1 1\n"
+        "DIMENSION fds '' absolute 1 1\n"
+        "DIMENSION targets '' absolute 1 1\n"
+        "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n"
+        "DIMENSION utime '' absolute 1 %2$llu\n"
+        "DIMENSION stime '' absolute 1 %2$llu\n"
+        "DIMENSION gtime '' absolute 1 %2$llu\n"
+        "DIMENSION minflt '' absolute 1 %2$llu\n"
+        "DIMENSION majflt '' absolute 1 %2$llu\n"
+        , update_every
+        , RATES_DETAIL
+        );
+
+    if(include_exited_childs)
+        buffer_sprintf(output,
+            "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n"
+            "DIMENSION cutime '' absolute 1 %2$llu\n"
+            "DIMENSION cstime '' absolute 1 %2$llu\n"
+            "DIMENSION cgtime '' absolute 1 %2$llu\n"
+            "DIMENSION cminflt '' absolute 1 %2$llu\n"
+            "DIMENSION cmajflt '' absolute 1 %2$llu\n"
+            , update_every
+            , RATES_DETAIL
+            );
 
 #ifndef PROFILING_MODE
-       unsigned long long sunext = (time(NULL) - (time(NULL) % update_every) + update_every) * 1000000ULL;
-       unsigned long long sunow;
+    unsigned long long sunext = (time(NULL) - (time(NULL) % update_every) + update_every) * 1000000ULL;
+    unsigned long long sunow;
 #endif /* PROFILING_MODE */
 
-       global_iterations_counter = 1;
-       for(;1; global_iterations_counter++) {
+    global_iterations_counter = 1;
+    for(;1; global_iterations_counter++) {
 #ifndef PROFILING_MODE
-               // delay until it is our time to run
-               while((sunow = timems()) < sunext)
-                       usecsleep(sunext - sunow);
+        // delay until it is our time to run
+        while((sunow = time_usec()) < sunext)
+            sleep_usec(sunext - sunow);
 
-               // find the next time we need to run
-               while(timems() > sunext)
-                       sunext += update_every * 1000000ULL;
+        // find the next time we need to run
+        while(time_usec() > sunext)
+            sunext += update_every * 1000000ULL;
 #endif /* PROFILING_MODE */
 
-               if(!collect_data_for_all_processes_from_proc()) {
-                       error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
-                       printf("DISABLE\n");
-                       exit(1);
-               }
+        if(!collect_data_for_all_processes_from_proc()) {
+            error("Cannot collect /proc data for running processes. Disabling apps.plugin...");
+            printf("DISABLE\n");
+            exit(1);
+        }
 
-               calculate_netdata_statistics();
-               normalize_data(apps_groups_root_target);
+        calculate_netdata_statistics();
+        normalize_data(apps_groups_root_target);
 
-               unsigned long long dt = send_resource_usage_to_netdata();
+        unsigned long long dt = send_resource_usage_to_netdata();
 
-               // this is smart enough to show only newly added apps, when needed
-               send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps");
-               send_charts_updates_to_netdata(users_root_target, "users", "Users");
-               send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups");
+        // this is smart enough to show only newly added apps, when needed
+        send_charts_updates_to_netdata(apps_groups_root_target, "apps", "Apps");
+        send_charts_updates_to_netdata(users_root_target, "users", "Users");
+        send_charts_updates_to_netdata(groups_root_target, "groups", "User Groups");
 
-               send_collected_data_to_netdata(apps_groups_root_target, "apps", dt);
-               send_collected_data_to_netdata(users_root_target, "users", dt);
-               send_collected_data_to_netdata(groups_root_target, "groups", dt);
+        send_collected_data_to_netdata(apps_groups_root_target, "apps", dt);
+        send_collected_data_to_netdata(users_root_target, "users", dt);
+        send_collected_data_to_netdata(groups_root_target, "groups", dt);
 
-               show_guest_time_old = show_guest_time;
+        show_guest_time_old = show_guest_time;
 
-               //if(puts(buffer_tostring(output)) == EOF)
-               if(write(STDOUT_FILENO, buffer_tostring(output), buffer_strlen(output)) == -1)
-                       fatal("Cannot send chart values to netdata.");
+        //if(puts(buffer_tostring(output)) == EOF)
+        if(write(STDOUT_FILENO, buffer_tostring(output), buffer_strlen(output)) == -1)
+            fatal("Cannot send chart values to netdata.");
 
-               // fflush(stdout);
-               buffer_flush(output);
+        // fflush(stdout);
+        buffer_flush(output);
 
-               if(unlikely(debug))
-                       fprintf(stderr, "apps.plugin: done Loop No %llu\n", global_iterations_counter);
+        if(unlikely(debug))
+            fprintf(stderr, "apps.plugin: done Loop No %llu\n", global_iterations_counter);
 
-               current_t = time(NULL);
+        current_t = time(NULL);
 
 #ifndef PROFILING_MODE
-               // restart check (14400 seconds)
-               if(current_t - started_t > 14400) exit(0);
+        // restart check (14400 seconds)
+        if(current_t - started_t > 14400) exit(0);
 #else
-               if(current_t - started_t > 10) exit(0);
+        if(current_t - started_t > 10) exit(0);
 #endif /* PROFILING_MODE */
-       }
+    }
 }
index cf9705e257d01fbe8809e042caffa98d2ef968a2..324afeebbad76ad96a17a747a48bde9f5e5a19fa 100644 (file)
--- a/src/avl.c
+++ b/src/avl.c
@@ -1,10 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-// #include <assert.h>
-
-#include "avl.h"
-#include "log.h"
+#include "common.h"
 
 /* ------------------------------------------------------------------------- */
 /*
@@ -21,7 +15,7 @@
 
 
 /* Search |tree| for an item matching |item|, and return it if found.
-        Otherwise return |NULL|. */
+     Otherwise return |NULL|. */
 avl *avl_search(avl_tree *tree, avl *item) {
     avl *p;
 
@@ -42,8 +36,8 @@ avl *avl_search(avl_tree *tree, avl *item) {
 }
 
 /* Inserts |item| into |tree| and returns a pointer to |item|'s address.
-        If a duplicate item is found in the tree,
-        returns a pointer to the duplicate without inserting |item|.
+     If a duplicate item is found in the tree,
+     returns a pointer to the duplicate without inserting |item|.
  */
 avl *avl_insert(avl_tree *tree, avl *item) {
     avl *y, *z; /* Top node to update balance factor, and parent. */
@@ -140,7 +134,7 @@ avl *avl_insert(avl_tree *tree, avl *item) {
 }
 
 /* Deletes from |tree| and returns an item matching |item|.
-        Returns a null pointer if no matching item found. */
+     Returns a null pointer if no matching item found. */
 avl *avl_remove(avl_tree *tree, avl *item) {
     /* Stack of nodes. */
     avl *pa[AVL_MAX_HEIGHT]; /* Nodes. */
@@ -284,14 +278,34 @@ avl *avl_remove(avl_tree *tree, avl *item) {
 }
 
 /* ------------------------------------------------------------------------- */
-// these functions are (C) Costa Tsaousis
+// below are functions by (C) Costa Tsaousis
+
+// ---------------------------
+// traversing
+
+void avl_walker(avl *node, void (*callback)(void *)) {
+    if(node->avl_link[0])
+        avl_walker(node->avl_link[0], callback);
+
+    callback(node);
+
+    if(node->avl_link[1])
+        avl_walker(node->avl_link[1], callback);
+}
+
+void avl_traverse(avl_tree *t, void (*callback)(void *)) {
+    avl_walker(t->root, callback);
+}
+
+// ---------------------------
+// locks
 
 void avl_read_lock(avl_tree_lock *t) {
 #ifndef AVL_WITHOUT_PTHREADS
 #ifdef AVL_LOCK_WITH_MUTEX
-       pthread_mutex_lock(&t->mutex);
+    pthread_mutex_lock(&t->mutex);
 #else
-       pthread_rwlock_rdlock(&t->rwlock);
+    pthread_rwlock_rdlock(&t->rwlock);
 #endif
 #endif /* AVL_WITHOUT_PTHREADS */
 }
@@ -299,9 +313,9 @@ void avl_read_lock(avl_tree_lock *t) {
 void avl_write_lock(avl_tree_lock *t) {
 #ifndef AVL_WITHOUT_PTHREADS
 #ifdef AVL_LOCK_WITH_MUTEX
-       pthread_mutex_lock(&t->mutex);
+    pthread_mutex_lock(&t->mutex);
 #else
-       pthread_rwlock_wrlock(&t->rwlock);
+    pthread_rwlock_wrlock(&t->rwlock);
 #endif
 #endif /* AVL_WITHOUT_PTHREADS */
 }
@@ -309,52 +323,59 @@ void avl_write_lock(avl_tree_lock *t) {
 void avl_unlock(avl_tree_lock *t) {
 #ifndef AVL_WITHOUT_PTHREADS
 #ifdef AVL_LOCK_WITH_MUTEX
-       pthread_mutex_unlock(&t->mutex);
+    pthread_mutex_unlock(&t->mutex);
 #else
-       pthread_rwlock_unlock(&t->rwlock);
+    pthread_rwlock_unlock(&t->rwlock);
 #endif
 #endif /* AVL_WITHOUT_PTHREADS */
 }
 
-/* ------------------------------------------------------------------------- */
+// ---------------------------
+// operations with locking
 
 void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b)) {
-       avl_init(&t->avl_tree, compar);
+    avl_init(&t->avl_tree, compar);
 
 #ifndef AVL_WITHOUT_PTHREADS
-       int lock;
+    int lock;
 
 #ifdef AVL_LOCK_WITH_MUTEX
-       lock = pthread_mutex_init(&t->mutex, NULL);
+    lock = pthread_mutex_init(&t->mutex, NULL);
 #else
-       lock = pthread_rwlock_init(&t->rwlock, NULL);
+    lock = pthread_rwlock_init(&t->rwlock, NULL);
 #endif
 
-       if(lock != 0)
-               fatal("Failed to initialize AVL mutex/rwlock, error: %d", lock);
+    if(lock != 0)
+        fatal("Failed to initialize AVL mutex/rwlock, error: %d", lock);
 
 #endif /* AVL_WITHOUT_PTHREADS */
 }
 
 avl *avl_search_lock(avl_tree_lock *t, avl *a) {
-       avl_read_lock(t);
-       avl *ret = avl_search(&t->avl_tree, a);
-       avl_unlock(t);
-       return ret;
+    avl_read_lock(t);
+    avl *ret = avl_search(&t->avl_tree, a);
+    avl_unlock(t);
+    return ret;
 }
 
 avl * avl_remove_lock(avl_tree_lock *t, avl *a) {
-       avl_write_lock(t);
-       avl *ret = avl_remove(&t->avl_tree, a);
-       avl_unlock(t);
-       return ret;
+    avl_write_lock(t);
+    avl *ret = avl_remove(&t->avl_tree, a);
+    avl_unlock(t);
+    return ret;
 }
 
 avl *avl_insert_lock(avl_tree_lock *t, avl *a) {
-       avl_write_lock(t);
+    avl_write_lock(t);
     avl * ret = avl_insert(&t->avl_tree, a);
-       avl_unlock(t);
-       return ret;
+    avl_unlock(t);
+    return ret;
+}
+
+void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *)) {
+    avl_read_lock(t);
+    avl_traverse(&t->avl_tree, callback);
+    avl_unlock(t);
 }
 
 void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
@@ -362,3 +383,4 @@ void avl_init(avl_tree *t, int (*compar)(void *a, void *b)) {
     t->compar = compar;
 }
 
+// ------------------
\ No newline at end of file
index 98e60371899fdc77343b169a64dd125fba9525cd..973d68fb1151ec3e67ce53ccac5cd4de3ad0348c 100644 (file)
--- a/src/avl.h
+++ b/src/avl.h
@@ -32,18 +32,18 @@ typedef struct avl {
 
 /* An AVL tree */
 typedef struct avl_tree {
-       avl *root;
-       int (*compar)(void *a, void *b);
+    avl *root;
+    int (*compar)(void *a, void *b);
 } avl_tree;
 
 typedef struct avl_tree_lock {
-       avl_tree avl_tree;
+    avl_tree avl_tree;
 
 #ifndef AVL_WITHOUT_PTHREADS
 #ifdef AVL_LOCK_WITH_MUTEX
-       pthread_mutex_t mutex;
+    pthread_mutex_t mutex;
 #else /* AVL_LOCK_WITH_MUTEX */
-       pthread_rwlock_t rwlock;
+    pthread_rwlock_t rwlock;
 #endif /* AVL_LOCK_WITH_MUTEX */
 #endif /* AVL_WITHOUT_PTHREADS */
 } avl_tree_lock;
@@ -79,4 +79,8 @@ avl *avl_search(avl_tree *t, avl *a);
 void avl_init_lock(avl_tree_lock *t, int (*compar)(void *a, void *b));
 void avl_init(avl_tree *t, int (*compar)(void *a, void *b));
 
+
+void avl_traverse_lock(avl_tree_lock *t, void (*callback)(void *));
+void avl_traverse(avl_tree *t, void (*callback)(void *));
+
 #endif /* avl.h */
index 2566aa586ae67cde864e4ce5c0b38f867d9cc364..74f26e3b2a95d912315bee8e66f170633e82055a 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <sys/syscall.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <time.h>
-
-#include "log.h"
 #include "common.h"
-#include "appconfig.h"
-#include "../config.h"
 
 char *global_host_prefix = "";
 int enable_ksm = 1;
 
-// time(NULL) in milliseconds
-unsigned long long timems(void) {
-       struct timeval now;
-       gettimeofday(&now, NULL);
-       return now.tv_sec * 1000000ULL + now.tv_usec;
+volatile sig_atomic_t netdata_exit = 0;
+
+// ----------------------------------------------------------------------------
+// memory allocation functions that handle failures
+
+// although netdata does not use memory allocations too often (netdata tries to
+// maintain its memory footprint stable during runtime, i.e. all buffers are
+// allocated during initialization and are adapted to current use throughout
+// its lifetime), these can be used to override the default system allocation
+// routines.
+
+char *strdupz(const char *s) {
+    char *t = strdup(s);
+    if (unlikely(!t)) fatal("Cannot strdup() string '%s'", s);
+    return t;
+}
+
+void *mallocz(size_t size) {
+    void *p = malloc(size);
+    if (unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", size);
+    return p;
+}
+
+void *callocz(size_t nmemb, size_t size) {
+    void *p = calloc(nmemb, size);
+    if (unlikely(!p)) fatal("Cannot allocate %zu bytes of memory.", nmemb * size);
+    return p;
+}
+
+void *reallocz(void *ptr, size_t size) {
+    void *p = realloc(ptr, size);
+    if (unlikely(!p)) fatal("Cannot re-allocate memory to %zu bytes.", size);
+    return p;
+}
+
+void freez(void *ptr) {
+    free(ptr);
+}
+
+// ----------------------------------------------------------------------------
+// time functions
+
+inline unsigned long long timeval_usec(struct timeval *tv) {
+    return tv->tv_sec * 1000000ULL + tv->tv_usec;
 }
 
-int usecsleep(unsigned long long usec) {
+// time(NULL) in nanoseconds
+inline unsigned long long time_usec(void) {
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    return timeval_usec(&now);
+}
+
+inline unsigned long long usec_dt(struct timeval *now, struct timeval *old) {
+    unsigned long long tv1 = timeval_usec(now);
+    unsigned long long tv2 = timeval_usec(old);
+    return (tv1 > tv2) ? (tv1 - tv2) : (tv2 - tv1);
+}
+
+int sleep_usec(unsigned long long usec) {
 
 #ifndef NETDATA_WITH_USLEEP
-       // we expect microseconds (1.000.000 per second)
-       // but timespec is nanoseconds (1.000.000.000 per second)
-       struct timespec req = { .tv_sec = usec / 1000000, .tv_nsec = (usec % 1000000) * 1000 }, rem;
-
-       while(nanosleep(&req, &rem) == -1) {
-               if(likely(errno == EINTR)) {
-                       info("nanosleep() interrupted (while sleeping for %llu microseconds).", usec);
-                       req.tv_sec = rem.tv_sec;
-                       req.tv_nsec = rem.tv_nsec;
-               }
-               else {
-                       error("Cannot nanosleep() for %llu microseconds.", usec);
-                       break;
-               }
-       }
-
-       return 0;
+    // we expect microseconds (1.000.000 per second)
+    // but timespec is nanoseconds (1.000.000.000 per second)
+    struct timespec rem, req = {
+            .tv_sec = (time_t) (usec / 1000000),
+            .tv_nsec = (suseconds_t) ((usec % 1000000) * 1000)
+    };
+
+    while (nanosleep(&req, &rem) == -1) {
+        if (likely(errno == EINTR)) {
+            info("nanosleep() interrupted (while sleeping for %llu microseconds).", usec);
+            req.tv_sec = rem.tv_sec;
+            req.tv_nsec = rem.tv_nsec;
+        } else {
+            error("Cannot nanosleep() for %llu microseconds.", usec);
+            break;
+        }
+    }
+
+    return 0;
 #else
-       int ret = usleep(usec);
-       if(unlikely(ret == -1 && errno == EINVAL)) {
-               // on certain systems, usec has to be up to 999999
-               if(usec > 999999) {
-                       int counter = usec / 999999;
-                       while(counter--)
-                               usleep(999999);
-
-                       usleep(usec % 999999);
-               }
-               else {
-                       error("Cannot usleep() for %llu microseconds.", usec);
-                       return ret;
-               }
-       }
-
-       if(ret != 0)
-               error("usleep() failed for %llu microseconds.", usec);
-
-       return ret;
+    int ret = usleep(usec);
+    if(unlikely(ret == -1 && errno == EINVAL)) {
+        // on certain systems, usec has to be up to 999999
+        if(usec > 999999) {
+            int counter = usec / 999999;
+            while(counter--)
+                usleep(999999);
+
+            usleep(usec % 999999);
+        }
+        else {
+            error("Cannot usleep() for %llu microseconds.", usec);
+            return ret;
+        }
+    }
+
+    if(ret != 0)
+        error("usleep() failed for %llu microseconds.", usec);
+
+    return ret;
 #endif
 }
 
 unsigned char netdata_map_chart_names[256] = {
-               [0] = '\0', //
-               [1] = '_', //
-               [2] = '_', //
-               [3] = '_', //
-               [4] = '_', //
-               [5] = '_', //
-               [6] = '_', //
-               [7] = '_', //
-               [8] = '_', //
-               [9] = '_', //
-               [10] = '_', //
-               [11] = '_', //
-               [12] = '_', //
-               [13] = '_', //
-               [14] = '_', //
-               [15] = '_', //
-               [16] = '_', //
-               [17] = '_', //
-               [18] = '_', //
-               [19] = '_', //
-               [20] = '_', //
-               [21] = '_', //
-               [22] = '_', //
-               [23] = '_', //
-               [24] = '_', //
-               [25] = '_', //
-               [26] = '_', //
-               [27] = '_', //
-               [28] = '_', //
-               [29] = '_', //
-               [30] = '_', //
-               [31] = '_', //
-               [32] = '_', //
-               [33] = '_', // !
-               [34] = '_', // "
-               [35] = '_', // #
-               [36] = '_', // $
-               [37] = '_', // %
-               [38] = '_', // &
-               [39] = '_', // '
-               [40] = '_', // (
-               [41] = '_', // )
-               [42] = '_', // *
-               [43] = '_', // +
-               [44] = '.', // ,
-               [45] = '-', // -
-               [46] = '.', // .
-               [47] = '/', // /
-               [48] = '0', // 0
-               [49] = '1', // 1
-               [50] = '2', // 2
-               [51] = '3', // 3
-               [52] = '4', // 4
-               [53] = '5', // 5
-               [54] = '6', // 6
-               [55] = '7', // 7
-               [56] = '8', // 8
-               [57] = '9', // 9
-               [58] = '_', // :
-               [59] = '_', // ;
-               [60] = '_', // <
-               [61] = '_', // =
-               [62] = '_', // >
-               [63] = '_', // ?
-               [64] = '_', // @
-               [65] = 'a', // A
-               [66] = 'b', // B
-               [67] = 'c', // C
-               [68] = 'd', // D
-               [69] = 'e', // E
-               [70] = 'f', // F
-               [71] = 'g', // G
-               [72] = 'h', // H
-               [73] = 'i', // I
-               [74] = 'j', // J
-               [75] = 'k', // K
-               [76] = 'l', // L
-               [77] = 'm', // M
-               [78] = 'n', // N
-               [79] = 'o', // O
-               [80] = 'p', // P
-               [81] = 'q', // Q
-               [82] = 'r', // R
-               [83] = 's', // S
-               [84] = 't', // T
-               [85] = 'u', // U
-               [86] = 'v', // V
-               [87] = 'w', // W
-               [88] = 'x', // X
-               [89] = 'y', // Y
-               [90] = 'z', // Z
-               [91] = '_', // [
-               [92] = '/', // backslash
-               [93] = '_', // ]
-               [94] = '_', // ^
-               [95] = '_', // _
-               [96] = '_', // `
-               [97] = 'a', // a
-               [98] = 'b', // b
-               [99] = 'c', // c
-               [100] = 'd', // d
-               [101] = 'e', // e
-               [102] = 'f', // f
-               [103] = 'g', // g
-               [104] = 'h', // h
-               [105] = 'i', // i
-               [106] = 'j', // j
-               [107] = 'k', // k
-               [108] = 'l', // l
-               [109] = 'm', // m
-               [110] = 'n', // n
-               [111] = 'o', // o
-               [112] = 'p', // p
-               [113] = 'q', // q
-               [114] = 'r', // r
-               [115] = 's', // s
-               [116] = 't', // t
-               [117] = 'u', // u
-               [118] = 'v', // v
-               [119] = 'w', // w
-               [120] = 'x', // x
-               [121] = 'y', // y
-               [122] = 'z', // z
-               [123] = '_', // {
-               [124] = '_', // |
-               [125] = '_', // }
-               [126] = '_', // ~
-               [127] = '_', //
-               [128] = '_', //
-               [129] = '_', //
-               [130] = '_', //
-               [131] = '_', //
-               [132] = '_', //
-               [133] = '_', //
-               [134] = '_', //
-               [135] = '_', //
-               [136] = '_', //
-               [137] = '_', //
-               [138] = '_', //
-               [139] = '_', //
-               [140] = '_', //
-               [141] = '_', //
-               [142] = '_', //
-               [143] = '_', //
-               [144] = '_', //
-               [145] = '_', //
-               [146] = '_', //
-               [147] = '_', //
-               [148] = '_', //
-               [149] = '_', //
-               [150] = '_', //
-               [151] = '_', //
-               [152] = '_', //
-               [153] = '_', //
-               [154] = '_', //
-               [155] = '_', //
-               [156] = '_', //
-               [157] = '_', //
-               [158] = '_', //
-               [159] = '_', //
-               [160] = '_', //
-               [161] = '_', //
-               [162] = '_', //
-               [163] = '_', //
-               [164] = '_', //
-               [165] = '_', //
-               [166] = '_', //
-               [167] = '_', //
-               [168] = '_', //
-               [169] = '_', //
-               [170] = '_', //
-               [171] = '_', //
-               [172] = '_', //
-               [173] = '_', //
-               [174] = '_', //
-               [175] = '_', //
-               [176] = '_', //
-               [177] = '_', //
-               [178] = '_', //
-               [179] = '_', //
-               [180] = '_', //
-               [181] = '_', //
-               [182] = '_', //
-               [183] = '_', //
-               [184] = '_', //
-               [185] = '_', //
-               [186] = '_', //
-               [187] = '_', //
-               [188] = '_', //
-               [189] = '_', //
-               [190] = '_', //
-               [191] = '_', //
-               [192] = '_', //
-               [193] = '_', //
-               [194] = '_', //
-               [195] = '_', //
-               [196] = '_', //
-               [197] = '_', //
-               [198] = '_', //
-               [199] = '_', //
-               [200] = '_', //
-               [201] = '_', //
-               [202] = '_', //
-               [203] = '_', //
-               [204] = '_', //
-               [205] = '_', //
-               [206] = '_', //
-               [207] = '_', //
-               [208] = '_', //
-               [209] = '_', //
-               [210] = '_', //
-               [211] = '_', //
-               [212] = '_', //
-               [213] = '_', //
-               [214] = '_', //
-               [215] = '_', //
-               [216] = '_', //
-               [217] = '_', //
-               [218] = '_', //
-               [219] = '_', //
-               [220] = '_', //
-               [221] = '_', //
-               [222] = '_', //
-               [223] = '_', //
-               [224] = '_', //
-               [225] = '_', //
-               [226] = '_', //
-               [227] = '_', //
-               [228] = '_', //
-               [229] = '_', //
-               [230] = '_', //
-               [231] = '_', //
-               [232] = '_', //
-               [233] = '_', //
-               [234] = '_', //
-               [235] = '_', //
-               [236] = '_', //
-               [237] = '_', //
-               [238] = '_', //
-               [239] = '_', //
-               [240] = '_', //
-               [241] = '_', //
-               [242] = '_', //
-               [243] = '_', //
-               [244] = '_', //
-               [245] = '_', //
-               [246] = '_', //
-               [247] = '_', //
-               [248] = '_', //
-               [249] = '_', //
-               [250] = '_', //
-               [251] = '_', //
-               [252] = '_', //
-               [253] = '_', //
-               [254] = '_', //
-               [255] = '_'  //
+        [0] = '\0', //
+        [1] = '_', //
+        [2] = '_', //
+        [3] = '_', //
+        [4] = '_', //
+        [5] = '_', //
+        [6] = '_', //
+        [7] = '_', //
+        [8] = '_', //
+        [9] = '_', //
+        [10] = '_', //
+        [11] = '_', //
+        [12] = '_', //
+        [13] = '_', //
+        [14] = '_', //
+        [15] = '_', //
+        [16] = '_', //
+        [17] = '_', //
+        [18] = '_', //
+        [19] = '_', //
+        [20] = '_', //
+        [21] = '_', //
+        [22] = '_', //
+        [23] = '_', //
+        [24] = '_', //
+        [25] = '_', //
+        [26] = '_', //
+        [27] = '_', //
+        [28] = '_', //
+        [29] = '_', //
+        [30] = '_', //
+        [31] = '_', //
+        [32] = '_', //
+        [33] = '_', // !
+        [34] = '_', // "
+        [35] = '_', // #
+        [36] = '_', // $
+        [37] = '_', // %
+        [38] = '_', // &
+        [39] = '_', // '
+        [40] = '_', // (
+        [41] = '_', // )
+        [42] = '_', // *
+        [43] = '_', // +
+        [44] = '.', // ,
+        [45] = '-', // -
+        [46] = '.', // .
+        [47] = '/', // /
+        [48] = '0', // 0
+        [49] = '1', // 1
+        [50] = '2', // 2
+        [51] = '3', // 3
+        [52] = '4', // 4
+        [53] = '5', // 5
+        [54] = '6', // 6
+        [55] = '7', // 7
+        [56] = '8', // 8
+        [57] = '9', // 9
+        [58] = '_', // :
+        [59] = '_', // ;
+        [60] = '_', // <
+        [61] = '_', // =
+        [62] = '_', // >
+        [63] = '_', // ?
+        [64] = '_', // @
+        [65] = 'a', // A
+        [66] = 'b', // B
+        [67] = 'c', // C
+        [68] = 'd', // D
+        [69] = 'e', // E
+        [70] = 'f', // F
+        [71] = 'g', // G
+        [72] = 'h', // H
+        [73] = 'i', // I
+        [74] = 'j', // J
+        [75] = 'k', // K
+        [76] = 'l', // L
+        [77] = 'm', // M
+        [78] = 'n', // N
+        [79] = 'o', // O
+        [80] = 'p', // P
+        [81] = 'q', // Q
+        [82] = 'r', // R
+        [83] = 's', // S
+        [84] = 't', // T
+        [85] = 'u', // U
+        [86] = 'v', // V
+        [87] = 'w', // W
+        [88] = 'x', // X
+        [89] = 'y', // Y
+        [90] = 'z', // Z
+        [91] = '_', // [
+        [92] = '/', // backslash
+        [93] = '_', // ]
+        [94] = '_', // ^
+        [95] = '_', // _
+        [96] = '_', // `
+        [97] = 'a', // a
+        [98] = 'b', // b
+        [99] = 'c', // c
+        [100] = 'd', // d
+        [101] = 'e', // e
+        [102] = 'f', // f
+        [103] = 'g', // g
+        [104] = 'h', // h
+        [105] = 'i', // i
+        [106] = 'j', // j
+        [107] = 'k', // k
+        [108] = 'l', // l
+        [109] = 'm', // m
+        [110] = 'n', // n
+        [111] = 'o', // o
+        [112] = 'p', // p
+        [113] = 'q', // q
+        [114] = 'r', // r
+        [115] = 's', // s
+        [116] = 't', // t
+        [117] = 'u', // u
+        [118] = 'v', // v
+        [119] = 'w', // w
+        [120] = 'x', // x
+        [121] = 'y', // y
+        [122] = 'z', // z
+        [123] = '_', // {
+        [124] = '_', // |
+        [125] = '_', // }
+        [126] = '_', // ~
+        [127] = '_', //
+        [128] = '_', //
+        [129] = '_', //
+        [130] = '_', //
+        [131] = '_', //
+        [132] = '_', //
+        [133] = '_', //
+        [134] = '_', //
+        [135] = '_', //
+        [136] = '_', //
+        [137] = '_', //
+        [138] = '_', //
+        [139] = '_', //
+        [140] = '_', //
+        [141] = '_', //
+        [142] = '_', //
+        [143] = '_', //
+        [144] = '_', //
+        [145] = '_', //
+        [146] = '_', //
+        [147] = '_', //
+        [148] = '_', //
+        [149] = '_', //
+        [150] = '_', //
+        [151] = '_', //
+        [152] = '_', //
+        [153] = '_', //
+        [154] = '_', //
+        [155] = '_', //
+        [156] = '_', //
+        [157] = '_', //
+        [158] = '_', //
+        [159] = '_', //
+        [160] = '_', //
+        [161] = '_', //
+        [162] = '_', //
+        [163] = '_', //
+        [164] = '_', //
+        [165] = '_', //
+        [166] = '_', //
+        [167] = '_', //
+        [168] = '_', //
+        [169] = '_', //
+        [170] = '_', //
+        [171] = '_', //
+        [172] = '_', //
+        [173] = '_', //
+        [174] = '_', //
+        [175] = '_', //
+        [176] = '_', //
+        [177] = '_', //
+        [178] = '_', //
+        [179] = '_', //
+        [180] = '_', //
+        [181] = '_', //
+        [182] = '_', //
+        [183] = '_', //
+        [184] = '_', //
+        [185] = '_', //
+        [186] = '_', //
+        [187] = '_', //
+        [188] = '_', //
+        [189] = '_', //
+        [190] = '_', //
+        [191] = '_', //
+        [192] = '_', //
+        [193] = '_', //
+        [194] = '_', //
+        [195] = '_', //
+        [196] = '_', //
+        [197] = '_', //
+        [198] = '_', //
+        [199] = '_', //
+        [200] = '_', //
+        [201] = '_', //
+        [202] = '_', //
+        [203] = '_', //
+        [204] = '_', //
+        [205] = '_', //
+        [206] = '_', //
+        [207] = '_', //
+        [208] = '_', //
+        [209] = '_', //
+        [210] = '_', //
+        [211] = '_', //
+        [212] = '_', //
+        [213] = '_', //
+        [214] = '_', //
+        [215] = '_', //
+        [216] = '_', //
+        [217] = '_', //
+        [218] = '_', //
+        [219] = '_', //
+        [220] = '_', //
+        [221] = '_', //
+        [222] = '_', //
+        [223] = '_', //
+        [224] = '_', //
+        [225] = '_', //
+        [226] = '_', //
+        [227] = '_', //
+        [228] = '_', //
+        [229] = '_', //
+        [230] = '_', //
+        [231] = '_', //
+        [232] = '_', //
+        [233] = '_', //
+        [234] = '_', //
+        [235] = '_', //
+        [236] = '_', //
+        [237] = '_', //
+        [238] = '_', //
+        [239] = '_', //
+        [240] = '_', //
+        [241] = '_', //
+        [242] = '_', //
+        [243] = '_', //
+        [244] = '_', //
+        [245] = '_', //
+        [246] = '_', //
+        [247] = '_', //
+        [248] = '_', //
+        [249] = '_', //
+        [250] = '_', //
+        [251] = '_', //
+        [252] = '_', //
+        [253] = '_', //
+        [254] = '_', //
+        [255] = '_'  //
 };
 
 // make sure the supplied string
 // is good for a netdata chart/dimension ID/NAME
 void netdata_fix_chart_name(char *s) {
-       while((*s = netdata_map_chart_names[(unsigned char)*s])) s++;
+    while ((*s = netdata_map_chart_names[(unsigned char) *s])) s++;
 }
 
 unsigned char netdata_map_chart_ids[256] = {
-               [0] = '\0', //
-               [1] = '_', //
-               [2] = '_', //
-               [3] = '_', //
-               [4] = '_', //
-               [5] = '_', //
-               [6] = '_', //
-               [7] = '_', //
-               [8] = '_', //
-               [9] = '_', //
-               [10] = '_', //
-               [11] = '_', //
-               [12] = '_', //
-               [13] = '_', //
-               [14] = '_', //
-               [15] = '_', //
-               [16] = '_', //
-               [17] = '_', //
-               [18] = '_', //
-               [19] = '_', //
-               [20] = '_', //
-               [21] = '_', //
-               [22] = '_', //
-               [23] = '_', //
-               [24] = '_', //
-               [25] = '_', //
-               [26] = '_', //
-               [27] = '_', //
-               [28] = '_', //
-               [29] = '_', //
-               [30] = '_', //
-               [31] = '_', //
-               [32] = '_', //
-               [33] = '_', // !
-               [34] = '_', // "
-               [35] = '_', // #
-               [36] = '_', // $
-               [37] = '_', // %
-               [38] = '_', // &
-               [39] = '_', // '
-               [40] = '_', // (
-               [41] = '_', // )
-               [42] = '_', // *
-               [43] = '_', // +
-               [44] = '.', // ,
-               [45] = '-', // -
-               [46] = '.', // .
-               [47] = '_', // /
-               [48] = '0', // 0
-               [49] = '1', // 1
-               [50] = '2', // 2
-               [51] = '3', // 3
-               [52] = '4', // 4
-               [53] = '5', // 5
-               [54] = '6', // 6
-               [55] = '7', // 7
-               [56] = '8', // 8
-               [57] = '9', // 9
-               [58] = '_', // :
-               [59] = '_', // ;
-               [60] = '_', // <
-               [61] = '_', // =
-               [62] = '_', // >
-               [63] = '_', // ?
-               [64] = '_', // @
-               [65] = 'a', // A
-               [66] = 'b', // B
-               [67] = 'c', // C
-               [68] = 'd', // D
-               [69] = 'e', // E
-               [70] = 'f', // F
-               [71] = 'g', // G
-               [72] = 'h', // H
-               [73] = 'i', // I
-               [74] = 'j', // J
-               [75] = 'k', // K
-               [76] = 'l', // L
-               [77] = 'm', // M
-               [78] = 'n', // N
-               [79] = 'o', // O
-               [80] = 'p', // P
-               [81] = 'q', // Q
-               [82] = 'r', // R
-               [83] = 's', // S
-               [84] = 't', // T
-               [85] = 'u', // U
-               [86] = 'v', // V
-               [87] = 'w', // W
-               [88] = 'x', // X
-               [89] = 'y', // Y
-               [90] = 'z', // Z
-               [91] = '_', // [
-               [92] = '/', // backslash
-               [93] = '_', // ]
-               [94] = '_', // ^
-               [95] = '_', // _
-               [96] = '_', // `
-               [97] = 'a', // a
-               [98] = 'b', // b
-               [99] = 'c', // c
-               [100] = 'd', // d
-               [101] = 'e', // e
-               [102] = 'f', // f
-               [103] = 'g', // g
-               [104] = 'h', // h
-               [105] = 'i', // i
-               [106] = 'j', // j
-               [107] = 'k', // k
-               [108] = 'l', // l
-               [109] = 'm', // m
-               [110] = 'n', // n
-               [111] = 'o', // o
-               [112] = 'p', // p
-               [113] = 'q', // q
-               [114] = 'r', // r
-               [115] = 's', // s
-               [116] = 't', // t
-               [117] = 'u', // u
-               [118] = 'v', // v
-               [119] = 'w', // w
-               [120] = 'x', // x
-               [121] = 'y', // y
-               [122] = 'z', // z
-               [123] = '_', // {
-               [124] = '_', // |
-               [125] = '_', // }
-               [126] = '_', // ~
-               [127] = '_', //
-               [128] = '_', //
-               [129] = '_', //
-               [130] = '_', //
-               [131] = '_', //
-               [132] = '_', //
-               [133] = '_', //
-               [134] = '_', //
-               [135] = '_', //
-               [136] = '_', //
-               [137] = '_', //
-               [138] = '_', //
-               [139] = '_', //
-               [140] = '_', //
-               [141] = '_', //
-               [142] = '_', //
-               [143] = '_', //
-               [144] = '_', //
-               [145] = '_', //
-               [146] = '_', //
-               [147] = '_', //
-               [148] = '_', //
-               [149] = '_', //
-               [150] = '_', //
-               [151] = '_', //
-               [152] = '_', //
-               [153] = '_', //
-               [154] = '_', //
-               [155] = '_', //
-               [156] = '_', //
-               [157] = '_', //
-               [158] = '_', //
-               [159] = '_', //
-               [160] = '_', //
-               [161] = '_', //
-               [162] = '_', //
-               [163] = '_', //
-               [164] = '_', //
-               [165] = '_', //
-               [166] = '_', //
-               [167] = '_', //
-               [168] = '_', //
-               [169] = '_', //
-               [170] = '_', //
-               [171] = '_', //
-               [172] = '_', //
-               [173] = '_', //
-               [174] = '_', //
-               [175] = '_', //
-               [176] = '_', //
-               [177] = '_', //
-               [178] = '_', //
-               [179] = '_', //
-               [180] = '_', //
-               [181] = '_', //
-               [182] = '_', //
-               [183] = '_', //
-               [184] = '_', //
-               [185] = '_', //
-               [186] = '_', //
-               [187] = '_', //
-               [188] = '_', //
-               [189] = '_', //
-               [190] = '_', //
-               [191] = '_', //
-               [192] = '_', //
-               [193] = '_', //
-               [194] = '_', //
-               [195] = '_', //
-               [196] = '_', //
-               [197] = '_', //
-               [198] = '_', //
-               [199] = '_', //
-               [200] = '_', //
-               [201] = '_', //
-               [202] = '_', //
-               [203] = '_', //
-               [204] = '_', //
-               [205] = '_', //
-               [206] = '_', //
-               [207] = '_', //
-               [208] = '_', //
-               [209] = '_', //
-               [210] = '_', //
-               [211] = '_', //
-               [212] = '_', //
-               [213] = '_', //
-               [214] = '_', //
-               [215] = '_', //
-               [216] = '_', //
-               [217] = '_', //
-               [218] = '_', //
-               [219] = '_', //
-               [220] = '_', //
-               [221] = '_', //
-               [222] = '_', //
-               [223] = '_', //
-               [224] = '_', //
-               [225] = '_', //
-               [226] = '_', //
-               [227] = '_', //
-               [228] = '_', //
-               [229] = '_', //
-               [230] = '_', //
-               [231] = '_', //
-               [232] = '_', //
-               [233] = '_', //
-               [234] = '_', //
-               [235] = '_', //
-               [236] = '_', //
-               [237] = '_', //
-               [238] = '_', //
-               [239] = '_', //
-               [240] = '_', //
-               [241] = '_', //
-               [242] = '_', //
-               [243] = '_', //
-               [244] = '_', //
-               [245] = '_', //
-               [246] = '_', //
-               [247] = '_', //
-               [248] = '_', //
-               [249] = '_', //
-               [250] = '_', //
-               [251] = '_', //
-               [252] = '_', //
-               [253] = '_', //
-               [254] = '_', //
-               [255] = '_'  //
+        [0] = '\0', //
+        [1] = '_', //
+        [2] = '_', //
+        [3] = '_', //
+        [4] = '_', //
+        [5] = '_', //
+        [6] = '_', //
+        [7] = '_', //
+        [8] = '_', //
+        [9] = '_', //
+        [10] = '_', //
+        [11] = '_', //
+        [12] = '_', //
+        [13] = '_', //
+        [14] = '_', //
+        [15] = '_', //
+        [16] = '_', //
+        [17] = '_', //
+        [18] = '_', //
+        [19] = '_', //
+        [20] = '_', //
+        [21] = '_', //
+        [22] = '_', //
+        [23] = '_', //
+        [24] = '_', //
+        [25] = '_', //
+        [26] = '_', //
+        [27] = '_', //
+        [28] = '_', //
+        [29] = '_', //
+        [30] = '_', //
+        [31] = '_', //
+        [32] = '_', //
+        [33] = '_', // !
+        [34] = '_', // "
+        [35] = '_', // #
+        [36] = '_', // $
+        [37] = '_', // %
+        [38] = '_', // &
+        [39] = '_', // '
+        [40] = '_', // (
+        [41] = '_', // )
+        [42] = '_', // *
+        [43] = '_', // +
+        [44] = '.', // ,
+        [45] = '-', // -
+        [46] = '.', // .
+        [47] = '_', // /
+        [48] = '0', // 0
+        [49] = '1', // 1
+        [50] = '2', // 2
+        [51] = '3', // 3
+        [52] = '4', // 4
+        [53] = '5', // 5
+        [54] = '6', // 6
+        [55] = '7', // 7
+        [56] = '8', // 8
+        [57] = '9', // 9
+        [58] = '_', // :
+        [59] = '_', // ;
+        [60] = '_', // <
+        [61] = '_', // =
+        [62] = '_', // >
+        [63] = '_', // ?
+        [64] = '_', // @
+        [65] = 'a', // A
+        [66] = 'b', // B
+        [67] = 'c', // C
+        [68] = 'd', // D
+        [69] = 'e', // E
+        [70] = 'f', // F
+        [71] = 'g', // G
+        [72] = 'h', // H
+        [73] = 'i', // I
+        [74] = 'j', // J
+        [75] = 'k', // K
+        [76] = 'l', // L
+        [77] = 'm', // M
+        [78] = 'n', // N
+        [79] = 'o', // O
+        [80] = 'p', // P
+        [81] = 'q', // Q
+        [82] = 'r', // R
+        [83] = 's', // S
+        [84] = 't', // T
+        [85] = 'u', // U
+        [86] = 'v', // V
+        [87] = 'w', // W
+        [88] = 'x', // X
+        [89] = 'y', // Y
+        [90] = 'z', // Z
+        [91] = '_', // [
+        [92] = '/', // backslash
+        [93] = '_', // ]
+        [94] = '_', // ^
+        [95] = '_', // _
+        [96] = '_', // `
+        [97] = 'a', // a
+        [98] = 'b', // b
+        [99] = 'c', // c
+        [100] = 'd', // d
+        [101] = 'e', // e
+        [102] = 'f', // f
+        [103] = 'g', // g
+        [104] = 'h', // h
+        [105] = 'i', // i
+        [106] = 'j', // j
+        [107] = 'k', // k
+        [108] = 'l', // l
+        [109] = 'm', // m
+        [110] = 'n', // n
+        [111] = 'o', // o
+        [112] = 'p', // p
+        [113] = 'q', // q
+        [114] = 'r', // r
+        [115] = 's', // s
+        [116] = 't', // t
+        [117] = 'u', // u
+        [118] = 'v', // v
+        [119] = 'w', // w
+        [120] = 'x', // x
+        [121] = 'y', // y
+        [122] = 'z', // z
+        [123] = '_', // {
+        [124] = '_', // |
+        [125] = '_', // }
+        [126] = '_', // ~
+        [127] = '_', //
+        [128] = '_', //
+        [129] = '_', //
+        [130] = '_', //
+        [131] = '_', //
+        [132] = '_', //
+        [133] = '_', //
+        [134] = '_', //
+        [135] = '_', //
+        [136] = '_', //
+        [137] = '_', //
+        [138] = '_', //
+        [139] = '_', //
+        [140] = '_', //
+        [141] = '_', //
+        [142] = '_', //
+        [143] = '_', //
+        [144] = '_', //
+        [145] = '_', //
+        [146] = '_', //
+        [147] = '_', //
+        [148] = '_', //
+        [149] = '_', //
+        [150] = '_', //
+        [151] = '_', //
+        [152] = '_', //
+        [153] = '_', //
+        [154] = '_', //
+        [155] = '_', //
+        [156] = '_', //
+        [157] = '_', //
+        [158] = '_', //
+        [159] = '_', //
+        [160] = '_', //
+        [161] = '_', //
+        [162] = '_', //
+        [163] = '_', //
+        [164] = '_', //
+        [165] = '_', //
+        [166] = '_', //
+        [167] = '_', //
+        [168] = '_', //
+        [169] = '_', //
+        [170] = '_', //
+        [171] = '_', //
+        [172] = '_', //
+        [173] = '_', //
+        [174] = '_', //
+        [175] = '_', //
+        [176] = '_', //
+        [177] = '_', //
+        [178] = '_', //
+        [179] = '_', //
+        [180] = '_', //
+        [181] = '_', //
+        [182] = '_', //
+        [183] = '_', //
+        [184] = '_', //
+        [185] = '_', //
+        [186] = '_', //
+        [187] = '_', //
+        [188] = '_', //
+        [189] = '_', //
+        [190] = '_', //
+        [191] = '_', //
+        [192] = '_', //
+        [193] = '_', //
+        [194] = '_', //
+        [195] = '_', //
+        [196] = '_', //
+        [197] = '_', //
+        [198] = '_', //
+        [199] = '_', //
+        [200] = '_', //
+        [201] = '_', //
+        [202] = '_', //
+        [203] = '_', //
+        [204] = '_', //
+        [205] = '_', //
+        [206] = '_', //
+        [207] = '_', //
+        [208] = '_', //
+        [209] = '_', //
+        [210] = '_', //
+        [211] = '_', //
+        [212] = '_', //
+        [213] = '_', //
+        [214] = '_', //
+        [215] = '_', //
+        [216] = '_', //
+        [217] = '_', //
+        [218] = '_', //
+        [219] = '_', //
+        [220] = '_', //
+        [221] = '_', //
+        [222] = '_', //
+        [223] = '_', //
+        [224] = '_', //
+        [225] = '_', //
+        [226] = '_', //
+        [227] = '_', //
+        [228] = '_', //
+        [229] = '_', //
+        [230] = '_', //
+        [231] = '_', //
+        [232] = '_', //
+        [233] = '_', //
+        [234] = '_', //
+        [235] = '_', //
+        [236] = '_', //
+        [237] = '_', //
+        [238] = '_', //
+        [239] = '_', //
+        [240] = '_', //
+        [241] = '_', //
+        [242] = '_', //
+        [243] = '_', //
+        [244] = '_', //
+        [245] = '_', //
+        [246] = '_', //
+        [247] = '_', //
+        [248] = '_', //
+        [249] = '_', //
+        [250] = '_', //
+        [251] = '_', //
+        [252] = '_', //
+        [253] = '_', //
+        [254] = '_', //
+        [255] = '_'  //
 };
 
 // make sure the supplied string
 // is good for a netdata chart/dimension ID/NAME
 void netdata_fix_chart_id(char *s) {
-       while((*s = netdata_map_chart_ids[(unsigned char)*s])) s++;
+    while ((*s = netdata_map_chart_ids[(unsigned char) *s])) s++;
 }
 
 /*
 // http://stackoverflow.com/questions/7666509/hash-function-for-string
 uint32_t simple_hash(const char *name)
 {
-       const char *s = name;
-       uint32_t hash = 5381;
-       int i;
+    const char *s = name;
+    uint32_t hash = 5381;
+    int i;
 
-       while((i = *s++)) hash = ((hash << 5) + hash) + i;
+    while((i = *s++)) hash = ((hash << 5) + hash) + i;
 
-       // fprintf(stderr, "HASH: %lu %s\n", hash, name);
+    // fprintf(stderr, "HASH: %lu %s\n", hash, name);
 
-       return hash;
+    return hash;
 }
 */
 
 
 // http://isthe.com/chongo/tech/comp/fnv/#FNV-1a
 uint32_t simple_hash(const char *name) {
-       unsigned char *s = (unsigned char *)name;
-       uint32_t hval = 0x811c9dc5;
-
-       // FNV-1a algorithm
-       while (*s) {
-               // multiply by the 32 bit FNV magic prime mod 2^32
-               // NOTE: No need to optimize with left shifts.
-               //       GCC will use imul instruction anyway.
-               //       Tested with 'gcc -O3 -S'
-               //hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
-               hval *= 16777619;
-
-               // xor the bottom with the current octet
-               hval ^= (uint32_t)*s++;
-       }
-
-       // fprintf(stderr, "HASH: %u = %s\n", hval, name);
-       return hval;
+    unsigned char *s = (unsigned char *) name;
+    uint32_t hval = 0x811c9dc5;
+
+    // FNV-1a algorithm
+    while (*s) {
+        // multiply by the 32 bit FNV magic prime mod 2^32
+        // NOTE: No need to optimize with left shifts.
+        //       GCC will use imul instruction anyway.
+        //       Tested with 'gcc -O3 -S'
+        //hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
+        hval *= 16777619;
+
+        // xor the bottom with the current octet
+        hval ^= (uint32_t) *s++;
+    }
+
+    // fprintf(stderr, "HASH: %u = %s\n", hval, name);
+    return hval;
 }
 
 uint32_t simple_uhash(const char *name) {
-       unsigned char *s = (unsigned char *)name;
-       uint32_t hval = 0x811c9dc5, c;
-
-       // FNV-1a algorithm
-       while((c = *s++)) {
-               if(unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
-               hval *= 16777619;
-               hval ^= c;
-       }
-       return hval;
+    unsigned char *s = (unsigned char *) name;
+    uint32_t hval = 0x811c9dc5, c;
+
+    // FNV-1a algorithm
+    while ((c = *s++)) {
+        if (unlikely(c >= 'A' && c <= 'Z')) c += 'a' - 'A';
+        hval *= 16777619;
+        hval ^= c;
+    }
+    return hval;
 }
 
 /*
 // http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
 // one at a time hash
 uint32_t simple_hash(const char *name) {
-       unsigned char *s = (unsigned char *)name;
-       uint32_t h = 0;
+    unsigned char *s = (unsigned char *)name;
+    uint32_t h = 0;
 
-       while(*s) {
-               h += *s++;
-               h += (h << 10);
-               h ^= (h >> 6);
-       }
+    while(*s) {
+        h += *s++;
+        h += (h << 10);
+        h ^= (h >> 6);
+    }
 
-       h += (h << 3);
-       h ^= (h >> 11);
-       h += (h << 15);
+    h += (h << 3);
+    h ^= (h >> 11);
+    h += (h << 15);
 
-       // fprintf(stderr, "HASH: %u = %s\n", h, name);
+    // fprintf(stderr, "HASH: %u = %s\n", h, name);
 
-       return h;
+    return h;
 }
 */
 
-void strreverse(char* begin, char* end)
-{
-       char aux;
-
-       while (end > begin)
-       {
-               // clearer code.
-               aux = *end;
-               *end-- = *begin;
-               *begin++ = aux;
-       }
+void strreverse(char *begin, char *end) {
+    char aux;
+
+    while (end > begin) {
+        // clearer code.
+        aux = *end;
+        *end-- = *begin;
+        *begin++ = aux;
+    }
 }
 
-char *mystrsep(char **ptr, char *s)
-{
-       char *p = "";
-       while ( p && !p[0] && *ptr ) p = strsep(ptr, s);
-       return(p);
+char *mystrsep(char **ptr, char *s) {
+    char *p = "";
+    while (p && !p[0] && *ptr) p = strsep(ptr, s);
+    return (p);
 }
 
-char *trim(char *s)
-{
-       // skip leading spaces
-       // and 'comments' as well!?
-       while(*s && isspace(*s)) s++;
-       if(!*s || *s == '#') return NULL;
-
-       // skip tailing spaces
-       // this way is way faster. Writes only one NUL char.
-       ssize_t l = strlen(s);
-  if (--l >= 0)
-       {
-               char *p = s + l;
-               while (p > s && isspace(*p)) p--;
-               *++p = '\0';
-       }
-
-       if(!*s) return NULL;
-
-       return s;
+char *trim(char *s) {
+    // skip leading spaces
+    // and 'comments' as well!?
+    while (*s && isspace(*s)) s++;
+    if (!*s || *s == '#') return NULL;
+
+    // skip tailing spaces
+    // this way is way faster. Writes only one NUL char.
+    ssize_t l = strlen(s);
+    if (--l >= 0) {
+        char *p = s + l;
+        while (p > s && isspace(*p)) p--;
+        *++p = '\0';
+    }
+
+    if (!*s) return NULL;
+
+    return s;
 }
 
 void *mymmap(const char *filename, size_t size, int flags, int ksm) {
-       static int log_madvise_1 = 1;
+    static int log_madvise_1 = 1;
 #ifdef MADV_MERGEABLE
-       static int log_madvise_2 = 1, log_madvise_3 = 1;
+    static int log_madvise_2 = 1, log_madvise_3 = 1;
 #endif
-       int fd;
-       void *mem = NULL;
+    int fd;
+    void *mem = NULL;
 
-       errno = 0;
-       fd = open(filename, O_RDWR|O_CREAT|O_NOATIME, 0664);
-       if(fd != -1) {
-               if(lseek(fd, size, SEEK_SET) == (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);
+    errno = 0;
+    fd = open(filename, O_RDWR | O_CREAT | O_NOATIME, 0664);
+    if (fd != -1) {
+        if (lseek(fd, size, SEEK_SET) == (off_t) size) {
+            if (write(fd, "", 1) == 1) {
+                if (ftruncate(fd, size))
+                    error("Cannot truncate file '%s' to size %zu. Will use the larger file.", filename, size);
 
 #ifdef MADV_MERGEABLE
-                               if(flags & MAP_SHARED || !enable_ksm || !ksm) {
+                if (flags & MAP_SHARED || !enable_ksm || !ksm) {
 #endif
-                                       mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags, fd, 0);
-                                       if(mem != MAP_FAILED) {
-                                               int advise = MADV_SEQUENTIAL|MADV_DONTFORK;
-                                               if(flags & MAP_SHARED) advise |= MADV_WILLNEED;
-
-                                               if(madvise(mem, size, advise) != 0 && log_madvise_1) {
-                                                       error("Cannot advise the kernel about the memory usage of file '%s'.", filename);
-                                                       log_madvise_1--;
-                                               }
-                                       }
+                    mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
+                    if (mem != MAP_FAILED) {
+                        int advise = MADV_SEQUENTIAL | MADV_DONTFORK;
+                        if (flags & MAP_SHARED) advise |= MADV_WILLNEED;
+
+                        if (madvise(mem, size, advise) != 0 && log_madvise_1) {
+                            error("Cannot advise the kernel about the memory usage of file '%s'.", filename);
+                            log_madvise_1--;
+                        }
+                    }
 #ifdef MADV_MERGEABLE
-                               }
-                               else {
+                } 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);
-                                       }
+                    // test - load the file into memory
+                    mem = calloc(1, size);
+                    if(mem) {
+                        if(lseek(fd, 0, SEEK_SET) == 0) {
+                            if(read(fd, mem, size) != (ssize_t)size)
+                                error("Cannot read from file '%s'", filename);
+                        }
+                        else
+                            error("Cannot seek to beginning of file '%s'.", filename);
+                    }
 */
-                                       mem = mmap(NULL, size, PROT_READ|PROT_WRITE, flags|MAP_ANONYMOUS, -1, 0);
-                                       if(mem != MAP_FAILED) {
-                                               if(lseek(fd, 0, SEEK_SET) == 0) {
-                                                       if(read(fd, mem, size) != (ssize_t)size)
-                                                               error("Cannot read from file '%s'", filename);
-                                               }
-                                               else
-                                                       error("Cannot seek to beginning of file '%s'.", filename);
-
-                                               // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE
-                                               if(madvise(mem, size, MADV_SEQUENTIAL|MADV_DONTFORK) != 0 && log_madvise_2) {
-                                                       error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.", filename);
-                                                       log_madvise_2--;
-                                               }
-
-                                               if(madvise(mem, size, MADV_MERGEABLE) != 0 && log_madvise_3) {
-                                                       error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.", filename);
-                                                       log_madvise_3--;
-                                               }
-                                       }
-                                       else
-                                               error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename);
-                               }
+                    mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0);
+                    if (mem != MAP_FAILED) {
+                        if (lseek(fd, 0, SEEK_SET) == 0) {
+                            if (read(fd, mem, size) != (ssize_t) size)
+                                error("Cannot read from file '%s'", filename);
+                        } else
+                            error("Cannot seek to beginning of file '%s'.", filename);
+
+                        // don't use MADV_SEQUENTIAL|MADV_DONTFORK, they disable MADV_MERGEABLE
+                        if (madvise(mem, size, MADV_SEQUENTIAL | MADV_DONTFORK) != 0 && log_madvise_2) {
+                            error("Cannot advise the kernel about the memory usage (MADV_SEQUENTIAL|MADV_DONTFORK) of file '%s'.",
+                                  filename);
+                            log_madvise_2--;
+                        }
+
+                        if (madvise(mem, size, MADV_MERGEABLE) != 0 && log_madvise_3) {
+                            error("Cannot advise the kernel about the memory usage (MADV_MERGEABLE) of file '%s'.",
+                                  filename);
+                            log_madvise_3--;
+                        }
+                    } else
+                        error("Cannot allocate PRIVATE ANONYMOUS memory for KSM for file '%s'.", filename);
+                }
 #endif
-                       }
-                       else error("Cannot write to file '%s' at position %zu.", filename, size);
-               }
-               else error("Cannot seek file '%s' to size %zu.", filename, size);
+            } else
+                error("Cannot write to file '%s' at position %zu.", filename, size);
+        } else
+            error("Cannot seek file '%s' to size %zu.", filename, size);
 
-               close(fd);
-       }
-       else error("Cannot create/open file '%s'.", filename);
+        close(fd);
+    } else
+        error("Cannot create/open file '%s'.", filename);
 
-       return mem;
+    return mem;
 }
 
-int savememory(const char *filename, void *mem, size_t size)
-{
-       char tmpfilename[FILENAME_MAX + 1];
+int savememory(const char *filename, void *mem, size_t size) {
+    char tmpfilename[FILENAME_MAX + 1];
 
-       snprintfz(tmpfilename, FILENAME_MAX, "%s.%ld.tmp", filename, (long)getpid());
+    snprintfz(tmpfilename, FILENAME_MAX, "%s.%ld.tmp", filename, (long) getpid());
 
-       int fd = open(tmpfilename, O_RDWR|O_CREAT|O_NOATIME, 0664);
-       if(fd < 0) {
-               error("Cannot create/open file '%s'.", filename);
-               return -1;
-       }
+    int fd = open(tmpfilename, O_RDWR | O_CREAT | O_NOATIME, 0664);
+    if (fd < 0) {
+        error("Cannot create/open file '%s'.", filename);
+        return -1;
+    }
 
-       if(write(fd, mem, size) != (ssize_t)size) {
-               error("Cannot write to file '%s' %ld bytes.", filename, (long)size);
-               close(fd);
-               return -1;
-       }
+    if (write(fd, mem, size) != (ssize_t) size) {
+        error("Cannot write to file '%s' %ld bytes.", filename, (long) size);
+        close(fd);
+        return -1;
+    }
 
-       close(fd);
+    close(fd);
 
-       if(rename(tmpfilename, filename)) {
-               error("Cannot rename '%s' to '%s'", tmpfilename, filename);
-               return -1;
-       }
+    if (rename(tmpfilename, filename)) {
+        error("Cannot rename '%s' to '%s'", tmpfilename, filename);
+        return -1;
+    }
 
-       return 0;
+    return 0;
 }
 
 int fd_is_valid(int fd) {
@@ -837,75 +867,71 @@ int fd_is_valid(int fd) {
  */
 unsigned int hz;
 
-void get_HZ(void)
-{
-       long ticks;
+void get_HZ(void) {
+    long ticks;
 
-       if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
-               perror("sysconf");
-       }
+    if ((ticks = sysconf(_SC_CLK_TCK)) == -1) {
+        perror("sysconf");
+    }
 
-       hz = (unsigned int) ticks;
+    hz = (unsigned int) ticks;
 }
 
-pid_t gettid(void)
-{
-       return syscall(SYS_gettid);
+pid_t gettid(void) {
+    return (pid_t)syscall(SYS_gettid);
 }
 
 char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) {
-       char *s = fgets(buf, buf_size, fp);
-       if(!s) return NULL;
+    char *s = fgets(buf, (int)buf_size, fp);
+    if (!s) return NULL;
 
-       char *t = s;
-       if(*t != '\0') {
-               // find the string end
-               while (*++t != '\0');
+    char *t = s;
+    if (*t != '\0') {
+        // find the string end
+        while (*++t != '\0');
 
-               // trim trailing spaces/newlines/tabs
-               while (--t > s && *t == '\n')
-                       *t = '\0';
-       }
+        // trim trailing spaces/newlines/tabs
+        while (--t > s && *t == '\n')
+            *t = '\0';
+    }
 
-       if(len)
-               *len = t - s + 1;
+    if (len)
+        *len = t - s + 1;
 
-       return s;
+    return s;
 }
 
 char *strncpyz(char *dst, const char *src, size_t n) {
-       char *p = dst;
+    char *p = dst;
 
-       while(*src && n--)
-               *dst++ = *src++;
+    while (*src && n--)
+        *dst++ = *src++;
 
-       *dst = '\0';
+    *dst = '\0';
 
-       return p;
+    return p;
 }
 
 int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args) {
-       int size;
-
-       size = vsnprintf(dst, n, fmt, args);
+    int size = vsnprintf(dst, n, fmt, args);
 
-       if(unlikely((size_t)size > n)) {
-               // there is bug in vsnprintf() and it returns
-               // a number higher to len, but it does not
-               // overflow the buffer.
-               size = n;
-       }
+    if (unlikely((size_t) size > n)) {
+        // there is bug in vsnprintf() and it returns
+        // a number higher to len, but it does not
+        // overflow the buffer.
+        size = (int)n;
+    }
 
-       dst[size] = '\0';
-       return size;
+    dst[size] = '\0';
+    return size;
 }
 
 int snprintfz(char *dst, size_t n, const char *fmt, ...) {
-       va_list args;
+    va_list args;
 
-       va_start(args, fmt);
-       int ret = vsnprintfz(dst, n, fmt, args);
-       va_end(args);
+    va_start(args, fmt);
+    int ret = vsnprintfz(dst, n, fmt, args);
+    va_end(args);
 
-       return ret;
+    return ret;
 }
index f6736df68470c2cb178b17c6f653c11d7bb49b00..b9aa67721f22e012c703187cc8217a407bb062a9 100644 (file)
+#ifndef NETDATA_COMMON_H
+#define NETDATA_COMMON_H 1
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
 #include <stdarg.h>
-#include <sys/time.h>
+#include <stddef.h>
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <pwd.h>
+#include <locale.h>
+#include <malloc.h>
+#include <netdb.h>
+#include <poll.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
 #include <sys/resource.h>
-#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
 
-#ifndef NETDATA_COMMON_H
-#define NETDATA_COMMON_H 1
+#ifdef STORAGE_WITH_MATH
+#include <math.h>
+#endif
 
 #if defined(HAVE_INTTYPES_H)
 #include <inttypes.h>
 #elif defined(HAVE_STDINT_H)
 #include <stdint.h>
 #endif
-#include <sys/types.h>
-#include <unistd.h>
 
+#ifdef NETDATA_WITH_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 \
+                               + __GNUC_MINOR__ * 100 \
+                               + __GNUC_PATCHLEVEL__)
+
+// test for gcc version < 4.1.2
+// to disable gcc atomic operations
+#if GCC_VERSION < 40102
+#define NETDATA_NO_ATOMIC_INSTRUCTIONS 1
+#endif
+
+#if __x86_64__ || __ppc64__
+#define ENVIRONMENT64
+#else
+#define ENVIRONMENT32
+#endif
+
+#else // !__GNUC__
+#define NETDATA_NO_ATOMIC_INSTRUCTIONS 1
+#define ENVIRONMENT32
+#endif // __GNUC__
+
+#include "avl.h"
+#include "log.h"
+#include "global_statistics.h"
+#include "storage_number.h"
+#include "web_buffer.h"
+#include "web_buffer_svg.h"
+#include "url.h"
+#include "popen.h"
+
+#include "procfile.h"
+#include "appconfig.h"
+#include "dictionary.h"
+#include "proc_self_mountinfo.h"
+#include "plugin_checks.h"
+#include "plugin_idlejitter.h"
+#include "plugin_nfacct.h"
+#include "plugin_proc.h"
+#include "plugin_tc.h"
+#include "plugins_d.h"
+
+#include "eval.h"
+#include "health.h"
+
+#include "rrd.h"
+#include "rrd2json.h"
+
+#include "web_client.h"
+#include "web_server.h"
+
+#include "registry.h"
+#include "daemon.h"
+#include "main.h"
+#include "unit_test.h"
+
+#ifdef abs
+#undef abs
+#endif
 #define abs(x) ((x < 0)? -x : x)
-#define usecdiff(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
+
+extern unsigned long long usec_dt(struct timeval *now, struct timeval *old);
+extern unsigned long long timeval_usec(struct timeval *tv);
+
+// #define usec_dt(now, last) (((((now)->tv_sec * 1000000ULL) + (now)->tv_usec) - (((last)->tv_sec * 1000000ULL) + (last)->tv_usec)))
 
 extern void netdata_fix_chart_id(char *s);
 extern void netdata_fix_chart_name(char *s);
@@ -31,6 +140,13 @@ 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, ...) __attribute__ (( format (printf, 3, 4)));
 
+// memory allocation functions that handle failures
+extern char *strdupz(const char *s);
+extern void *callocz(size_t nmemb, size_t size);
+extern void *mallocz(size_t size);
+extern void freez(void *ptr);
+extern void *reallocz(void *ptr, size_t size);
+
 extern void *mymmap(const char *filename, size_t size, int flags, int ksm);
 extern int savememory(const char *filename, void *mem, size_t size);
 
@@ -45,8 +161,8 @@ extern void get_HZ(void);
 
 extern pid_t gettid(void);
 
-extern unsigned long long timems(void);
-extern int usecsleep(unsigned long long usec);
+extern unsigned long long time_usec(void);
+extern int sleep_usec(unsigned long long usec);
 
 extern char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len);
 
index b14b4d9a4a2d8adbded457fe9e686a855d6df1df..8653e3b9450d7ce3e23098bd79072d863b5a87f2 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <grp.h>
-#include <pthread.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "web_client.h"
-#include "plugins_d.h"
-#include "rrd.h"
-#include "popen.h"
-#include "main.h"
-#include "daemon.h"
+#include <sched.h>
 
 char pidfile[FILENAME_MAX + 1] = "";
 
 void sig_handler_exit(int signo)
 {
-       if(signo) {
-               error_log_limit_unlimited();
-               error("Received signal %d. Exiting...", signo);
-               netdata_exit = 1;
-       }
+    if(signo) {
+        error_log_limit_unlimited();
+        error("Received signal %d. Exiting...", signo);
+        netdata_exit = 1;
+    }
+}
+
+void sig_handler_logrotate(int signo)
+{
+    if(signo) {
+        error_log_limit_reset();
+        info("Received signal %d to re-open the log files", signo);
+        reopen_all_log_files();
+    }
 }
 
 void sig_handler_save(int signo)
 {
-       if(signo) {
-               info("Received signal %d to save the database...", signo);
-               rrdset_save_all();
-       }
+    if(signo) {
+        error_log_limit_reset();
+        info("Received signal %d to save the database...", signo);
+        rrdset_save_all();
+    }
 }
 
-static void properly_chown_netdata_generated_file(int fd, uid_t uid, gid_t gid) {
-       if(fd == -1) return;
+static void chown_open_file(int fd, uid_t uid, gid_t gid) {
+    if(fd == -1) return;
 
-       struct stat buf;
+    struct stat buf;
 
-       if(fstat(fd, &buf) == -1) {
-               error("Cannot fstat() fd %d", fd);
-               return;
-       }
+    if(fstat(fd, &buf) == -1) {
+        error("Cannot fstat() fd %d", fd);
+        return;
+    }
 
-       if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
-               if(fchown(fd, uid, gid) == -1)
-                       error("Cannot fchown() fd %d.", fd);
-       }
+    if((buf.st_uid != uid || buf.st_gid != gid) && S_ISREG(buf.st_mode)) {
+        if(fchown(fd, uid, gid) == -1)
+            error("Cannot fchown() fd %d.", fd);
+    }
 }
 
-int become_user(const char *username, int access_fd, int output_fd, int error_fd, int pid_fd)
+int become_user(const char *username, int pid_fd)
 {
-       struct passwd *pw = getpwnam(username);
-       if(!pw) {
-               error("User %s is not present.", username);
-               return -1;
-       }
-
-       uid_t uid = pw->pw_uid;
-       gid_t gid = pw->pw_gid;
-
-       int ngroups =  sysconf(_SC_NGROUPS_MAX);
-       gid_t *supplementary_groups = NULL;
-       if(ngroups) {
-               supplementary_groups = malloc(sizeof(gid_t) * ngroups);
-               if(supplementary_groups) {
-                       if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
-                               error("Cannot get supplementary groups of user '%s'.", username);
-                               free(supplementary_groups);
-                               supplementary_groups = NULL;
-                               ngroups = 0;
-                       }
-               }
-               else fatal("Cannot allocate memory for %d supplementary groups", ngroups);
-       }
-
-       properly_chown_netdata_generated_file(access_fd, uid, gid);
-       properly_chown_netdata_generated_file(output_fd, uid, gid);
-       properly_chown_netdata_generated_file(error_fd, uid, gid);
-       properly_chown_netdata_generated_file(pid_fd, uid, gid);
-
-       if(supplementary_groups && ngroups) {
-               if(setgroups(ngroups, supplementary_groups) == -1)
-                       error("Cannot set supplementary groups for user '%s'", username);
-
-               free(supplementary_groups);
-               supplementary_groups = NULL;
-               ngroups = 0;
-       }
-
-       if(setresgid(gid, gid, gid) != 0) {
-               error("Cannot switch to user's %s group (gid: %u).", username, gid);
-               return -1;
-       }
-
-       if(setresuid(uid, uid, uid) != 0) {
-               error("Cannot switch to user %s (uid: %u).", username, uid);
-               return -1;
-       }
-
-       if(setgid(gid) != 0) {
-               error("Cannot switch to user's %s group (gid: %u).", username, gid);
-               return -1;
-       }
-       if(setegid(gid) != 0) {
-               error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
-               return -1;
-       }
-       if(setuid(uid) != 0) {
-               error("Cannot switch to user %s (uid: %u).", username, uid);
-               return -1;
-       }
-       if(seteuid(uid) != 0) {
-               error("Cannot effectively switch to user %s (uid: %u).", username, uid);
-               return -1;
-       }
-
-       return(0);
+    struct passwd *pw = getpwnam(username);
+    if(!pw) {
+        error("User %s is not present.", username);
+        return -1;
+    }
+
+    uid_t uid = pw->pw_uid;
+    gid_t gid = pw->pw_gid;
+
+    int ngroups = (int)sysconf(_SC_NGROUPS_MAX);
+    gid_t *supplementary_groups = NULL;
+    if(ngroups) {
+        supplementary_groups = mallocz(sizeof(gid_t) * ngroups);
+        if(getgrouplist(username, gid, supplementary_groups, &ngroups) == -1) {
+            error("Cannot get supplementary groups of user '%s'.", username);
+            freez(supplementary_groups);
+            supplementary_groups = NULL;
+            ngroups = 0;
+        }
+    }
+
+    chown_open_file(STDOUT_FILENO, uid, gid);
+    chown_open_file(STDERR_FILENO, uid, gid);
+    chown_open_file(stdaccess_fd, uid, gid);
+    chown_open_file(pid_fd, uid, gid);
+
+    if(supplementary_groups && ngroups) {
+        if(setgroups(ngroups, supplementary_groups) == -1)
+            error("Cannot set supplementary groups for user '%s'", username);
+
+        freez(supplementary_groups);
+        supplementary_groups = NULL;
+        ngroups = 0;
+    }
+
+    if(setresgid(gid, gid, gid) != 0) {
+        error("Cannot switch to user's %s group (gid: %u).", username, gid);
+        return -1;
+    }
+
+    if(setresuid(uid, uid, uid) != 0) {
+        error("Cannot switch to user %s (uid: %u).", username, uid);
+        return -1;
+    }
+
+    if(setgid(gid) != 0) {
+        error("Cannot switch to user's %s group (gid: %u).", username, gid);
+        return -1;
+    }
+    if(setegid(gid) != 0) {
+        error("Cannot effectively switch to user's %s group (gid: %u).", username, gid);
+        return -1;
+    }
+    if(setuid(uid) != 0) {
+        error("Cannot switch to user %s (uid: %u).", username, uid);
+        return -1;
+    }
+    if(seteuid(uid) != 0) {
+        error("Cannot effectively switch to user %s (uid: %u).", username, uid);
+        return -1;
+    }
+
+    return(0);
+}
+
+void oom_score_adj(int score) {
+    int done = 0;
+    int fd = open("/proc/self/oom_score_adj", O_WRONLY);
+    if(fd != -1) {
+        char buf[10 + 1];
+        ssize_t len = snprintfz(buf, 10, "%d", score);
+        if(write(fd, buf, len) == len) done = 1;
+        close(fd);
+    }
+
+    if(!done)
+        error("Cannot adjust my Out-Of-Memory score to %d.", score);
+    else
+        info("Adjusted my Out-Of-Memory score to %d.", score);
+}
+
+int sched_setscheduler_idle(void) {
+    const struct sched_param param = {
+        .sched_priority = 0
+    };
+
+    int i = sched_setscheduler(0, SCHED_IDLE, &param);
+    if(i != 0)
+        error("Cannot adjust my scheduling priority to IDLE.");
+    else
+        info("Adjusted my scheduling priority to IDLE.");
+
+    return i;
 }
 
-int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp)
+int become_daemon(int dont_fork, const char *user)
 {
-       fflush(NULL);
-
-       // open the files before forking
-       int input_fd = -1, output_fd = -1, error_fd = -1, dev_null;
-
-       if(input && *input) {
-               if((input_fd = open(input, O_RDONLY, 0666)) == -1) {
-                       error("Cannot open input file '%s'.", input);
-                       return -1;
-               }
-       }
-
-       if(output && *output && strcmp(output, "/dev/null") != 0) {
-               if((output_fd = open(output, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
-                       error("Cannot open output log file '%s'", output);
-                       if(input_fd != -1) close(input_fd);
-                       return -1;
-               }
-       }
-
-       if(error && *error && strcmp(error, "/dev/null") != 0) {
-               if((error_fd = open(error, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
-                       error("Cannot open error log file '%s'.", error);
-                       if(input_fd != -1) close(input_fd);
-                       if(output_fd != -1) close(output_fd);
-                       return -1;
-               }
-       }
-
-       if(access && *access && access_fd && strcmp(access, "/dev/null") != 0) {
-               if((*access_fd = open(access, O_RDWR | O_APPEND | O_CREAT, 0666)) == -1) {
-                       error("Cannot open access log file '%s'", access);
-                       if(input_fd != -1) close(input_fd);
-                       if(output_fd != -1) close(output_fd);
-                       if(error_fd != -1) close(error_fd);
-                       return -1;
-               }
-
-               if(access_fp) {
-                       *access_fp = fdopen(*access_fd, "w");
-                       if(!*access_fp) {
-                               error("Cannot migrate file's '%s' fd %d.", access, *access_fd);
-                               if(input_fd != -1) close(input_fd);
-                               if(output_fd != -1) close(output_fd);
-                               if(error_fd != -1) close(error_fd);
-                               close(*access_fd);
-                               *access_fd = -1;
-                               return -1;
-                       }
-                       if(setvbuf(*access_fp, NULL, _IOLBF, 0) != 0)
-                               error("Cannot set line buffering on access.log");
-               }
-       }
-
-       if((dev_null = open("/dev/null", O_RDWR, 0666)) == -1) {
-               perror("Cannot open /dev/null");
-               if(input_fd != -1) close(input_fd);
-               if(output_fd != -1) close(output_fd);
-               if(error_fd != -1) close(error_fd);
-               if(access && access_fd && *access_fd != -1) {
-                       close(*access_fd);
-                       *access_fd = -1;
-                       if(access_fp) {
-                               fclose(*access_fp);
-                               *access_fp = NULL;
-                       }
-               }
-               return -1;
-       }
-
-       // all files opened
-       // lets do it
-
-       if(!dont_fork) {
-               int i = fork();
-               if(i == -1) {
-                       perror("cannot fork");
-                       exit(1);
-               }
-               if(i != 0) {
-                       exit(0); // the parent
-               }
-
-               // become session leader
-               if (setsid() < 0) {
-                       perror("Cannot become session leader.");
-                       exit(2);
-               }
-       }
-
-       // fork() again
-       if(!dont_fork) {
-               int i = fork();
-               if(i == -1) {
-                       perror("cannot fork");
-                       exit(1);
-               }
-               if(i != 0) {
-                       exit(0); // the parent
-               }
-       }
-
-       // Set new file permissions
-       umask(0);
-
-       // close all files
-       if(close_all_files) {
-               int i;
-               for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
-                       if(
-                               ((access_fd && i != *access_fd) || !access_fd)
-                               && i != dev_null
-                               && i != input_fd
-                               && i != output_fd
-                               && i != error_fd
-                               && fd_is_valid(i)
-                               ) close(i);
-       }
-       else {
-               close(STDIN_FILENO);
-               close(STDOUT_FILENO);
-               close(STDERR_FILENO);
-       }
-
-       // put the opened files
-       // to our standard file descriptors
-       if(input_fd != -1) {
-               if(input_fd != STDIN_FILENO) {
-                       dup2(input_fd, STDIN_FILENO);
-                       close(input_fd);
-               }
-               input_fd = -1;
-       }
-       else dup2(dev_null, STDIN_FILENO);
-
-       if(output_fd != -1) {
-               if(output_fd != STDOUT_FILENO) {
-                       dup2(output_fd, STDOUT_FILENO);
-                       close(output_fd);
-               }
-
-               if(setvbuf(stdout, NULL, _IOLBF, 0) != 0)
-                       error("Cannot set line buffering on debug.log");
-
-               output_fd = STDOUT_FILENO;
-       }
-       else dup2(dev_null, STDOUT_FILENO);
-
-       if(error_fd != -1) {
-               if(error_fd != STDERR_FILENO) {
-                       dup2(error_fd, STDERR_FILENO);
-                       close(error_fd);
-               }
-
-               if(setvbuf(stderr, NULL, _IOLBF, 0) != 0)
-                       error("Cannot set line buffering on error.log");
-
-               error_fd = STDERR_FILENO;
-       }
-       else dup2(dev_null, STDERR_FILENO);
-
-       // close /dev/null
-       if(dev_null != STDIN_FILENO && dev_null != STDOUT_FILENO && dev_null != STDERR_FILENO)
-               close(dev_null);
-
-       // generate our pid file
-       int pidfd = -1;
-       if(pidfile[0]) {
-               pidfd = open(pidfile, O_RDWR | O_CREAT, 0644);
-               if(pidfd >= 0) {
-                       if(ftruncate(pidfd, 0) != 0)
-                               error("Cannot truncate pidfile '%s'.", pidfile);
-
-                       char b[100];
-                       sprintf(b, "%d\n", getpid());
-                       ssize_t i = write(pidfd, b, strlen(b));
-                       if(i <= 0)
-                               error("Cannot write pidfile '%s'.", pidfile);
-               }
-               else error("Failed to open pidfile '%s'.", pidfile);
-       }
-
-       if(user && *user) {
-               if(become_user(user, (access_fd)?*access_fd:-1, output_fd, error_fd, pidfd) != 0) {
-                       error("Cannot become user '%s'. Continuing as we are.", user);
-               }
-               else info("Successfully became user '%s'.", user);
-       }
-
-       if(pidfd != -1) {
-               close(pidfd);
-               pidfd = -1;
-       }
-
-       return(0);
+    if(!dont_fork) {
+        int i = fork();
+        if(i == -1) {
+            perror("cannot fork");
+            exit(1);
+        }
+        if(i != 0) {
+            exit(0); // the parent
+        }
+
+        // become session leader
+        if (setsid() < 0) {
+            perror("Cannot become session leader.");
+            exit(2);
+        }
+
+        // fork() again
+        i = fork();
+        if(i == -1) {
+            perror("cannot fork");
+            exit(1);
+        }
+        if(i != 0) {
+            exit(0); // the parent
+        }
+    }
+
+    // generate our pid file
+    int pidfd = -1;
+    if(pidfile[0]) {
+        pidfd = open(pidfile, O_WRONLY | O_CREAT, 0644);
+        if(pidfd >= 0) {
+            if(ftruncate(pidfd, 0) != 0)
+                error("Cannot truncate pidfile '%s'.", pidfile);
+
+            char b[100];
+            sprintf(b, "%d\n", getpid());
+            ssize_t i = write(pidfd, b, strlen(b));
+            if(i <= 0)
+                error("Cannot write pidfile '%s'.", pidfile);
+        }
+        else error("Failed to open pidfile '%s'.", pidfile);
+    }
+
+    // Set new file permissions
+    umask(0002);
+
+    // adjust my Out-Of-Memory score
+    oom_score_adj(1000);
+
+    // never become a problem
+    if(sched_setscheduler_idle() != 0) {
+        if(nice(19) == -1) error("Cannot lower my CPU priority.");
+        else info("Set my nice value to 19.");
+    }
+
+    if(user && *user) {
+        if(become_user(user, pidfd) != 0) {
+            error("Cannot become user '%s'. Continuing as we are.", user);
+        }
+        else info("Successfully became user '%s'.", user);
+    }
+
+    if(pidfd != -1) {
+        close(pidfd);
+        pidfd = -1;
+    }
+
+    return(0);
 }
index 8d62469b65a1b611db8da530407f56a466ecf3cb..6cf1b871972d3db201f2523906e1070ec023ff06 100644 (file)
@@ -3,10 +3,11 @@
 
 extern void sig_handler_exit(int signo);
 extern void sig_handler_save(int signo);
+extern void sig_handler_logrotate(int signo);
 
-extern int become_user(const char *username, int access_fd, int output_fd, int error_fd, int pid_fd);
+extern int become_user(const char *username, int pid_fd);
 
-extern int become_daemon(int dont_fork, int close_all_files, const char *user, const char *input, const char *output, const char *error, const char *access, int *access_fd, FILE **access_fp);
+extern int become_daemon(int dont_fork, const char *user);
 
 extern void netdata_cleanup_and_exit(int i);
 
index 8bc048276b45e7667cacf4a4af27af863e5c7657..91d3b45f1f8be424be9e1e68133babac17423b0d 100644 (file)
@@ -1,39 +1,27 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "avl.h"
 #include "common.h"
-#include "log.h"
-
-#include "dictionary.h"
 
 // ----------------------------------------------------------------------------
 // dictionary statistics
 
 static inline void NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(DICTIONARY *dict) {
-       if(likely(dict->stats))
-               dict->stats->inserts++;
+    if(likely(dict->stats))
+        dict->stats->inserts++;
 }
 static inline void NETDATA_DICTIONARY_STATS_DELETES_PLUS1(DICTIONARY *dict) {
-       if(likely(dict->stats))
-               dict->stats->deletes++;
+    if(likely(dict->stats))
+        dict->stats->deletes++;
 }
 static inline void NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
-       if(likely(dict->stats))
-               dict->stats->searches++;
+    if(likely(dict->stats))
+        dict->stats->searches++;
 }
 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict) {
-       if(likely(dict->stats))
-               dict->stats->entries++;
+    if(likely(dict->stats))
+        dict->stats->entries++;
 }
 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
-       if(likely(dict->stats))
-               dict->stats->entries--;
+    if(likely(dict->stats))
+        dict->stats->entries--;
 }
 
 
@@ -41,24 +29,24 @@ static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
 // dictionary locks
 
 static inline void dictionary_read_lock(DICTIONARY *dict) {
-       if(likely(dict->rwlock)) {
-               // debug(D_DICTIONARY, "Dictionary READ lock");
-               pthread_rwlock_rdlock(dict->rwlock);
-       }
+    if(likely(dict->rwlock)) {
+        // debug(D_DICTIONARY, "Dictionary READ lock");
+        pthread_rwlock_rdlock(dict->rwlock);
+    }
 }
 
 static inline void dictionary_write_lock(DICTIONARY *dict) {
-       if(likely(dict->rwlock)) {
-               // debug(D_DICTIONARY, "Dictionary WRITE lock");
-               pthread_rwlock_wrlock(dict->rwlock);
-       }
+    if(likely(dict->rwlock)) {
+        // debug(D_DICTIONARY, "Dictionary WRITE lock");
+        pthread_rwlock_wrlock(dict->rwlock);
+    }
 }
 
 static inline void dictionary_unlock(DICTIONARY *dict) {
-       if(likely(dict->rwlock)) {
-               // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
-               pthread_rwlock_unlock(dict->rwlock);
-       }
+    if(likely(dict->rwlock)) {
+        // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
+        pthread_rwlock_unlock(dict->rwlock);
+    }
 }
 
 
@@ -66,209 +54,196 @@ static inline void dictionary_unlock(DICTIONARY *dict) {
 // avl index
 
 static int name_value_compare(void* a, void* b) {
-       if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
-       else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
-       else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
+    if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
+    else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
+    else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
 }
 
 #define dictionary_name_value_index_add_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
 #define dictionary_name_value_index_del_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
 
 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
-       NAME_VALUE tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = (char *)name;
+    NAME_VALUE tmp;
+    tmp.hash = (hash)?hash:simple_hash(name);
+    tmp.name = (char *)name;
 
-       NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
-       return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
+    NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
+    return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
 }
 
 // ----------------------------------------------------------------------------
 // internal methods
 
 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
-       debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
+    debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
 
-       NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
-       if(unlikely(!nv)) fatal("Cannot allocate name_value of size %zu", sizeof(NAME_VALUE));
+    NAME_VALUE *nv = callocz(1, sizeof(NAME_VALUE));
 
-       if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
-               nv->name = (char *)name;
-       else {
-               nv->name = strdup(name);
-               if (unlikely(!nv->name))
-                       fatal("Cannot allocate name_value.name of size %zu", strlen(name));
-       }
+    if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
+        nv->name = (char *)name;
+    else {
+        nv->name = strdupz(name);
+    }
 
-       nv->hash = (hash)?hash:simple_hash(nv->name);
+    nv->hash = (hash)?hash:simple_hash(nv->name);
 
-       if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
-               nv->value = value;
-       else {
-               nv->value = malloc(value_len);
-               if (unlikely(!nv->value))
-                       fatal("Cannot allocate name_value.value of size %zu", value_len);
+    if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
+        nv->value = value;
+    else {
+        nv->value = mallocz(value_len);
+        memcpy(nv->value, value, value_len);
+    }
 
-               memcpy(nv->value, value, value_len);
-       }
+    // index it
+    dictionary_name_value_index_add_nolock(dict, nv);
+    NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
 
-       // index it
-       dictionary_name_value_index_add_nolock(dict, nv);
-       NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
-
-       return nv;
+    return nv;
 }
 
 static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
-       debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
+    debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
 
-       dictionary_name_value_index_del_nolock(dict, nv);
+    dictionary_name_value_index_del_nolock(dict, nv);
 
-       NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
+    NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
 
-       if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
-               debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
-               free(nv->value);
-       }
+    if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
+        debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
+        freez(nv->value);
+    }
 
-       if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
-               debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
-               free(nv->name);
-       }
+    if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
+        debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
+        freez(nv->name);
+    }
 
-       free(nv);
+    freez(nv);
 }
 
 // ----------------------------------------------------------------------------
 // API - basic methods
 
-DICTIONARY *dictionary_create(uint32_t flags) {
-       debug(D_DICTIONARY, "Creating dictionary.");
+DICTIONARY *dictionary_create(uint8_t flags) {
+    debug(D_DICTIONARY, "Creating dictionary.");
 
-       DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
-       if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
+    DICTIONARY *dict = callocz(1, sizeof(DICTIONARY));
 
-       if(flags & DICTIONARY_FLAG_WITH_STATISTICS) {
-               dict->stats = calloc(1, sizeof(struct dictionary_stats));
-               if(!dict->stats) fatal("Cannot allocate statistics for DICTIONARY");
-       }
+    if(flags & DICTIONARY_FLAG_WITH_STATISTICS)
+        dict->stats = callocz(1, sizeof(struct dictionary_stats));
 
-       if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
-               dict->rwlock = calloc(1, sizeof(pthread_rwlock_t));
-               if(!dict->rwlock) fatal("Cannot allocate pthread_rwlock_t for DICTIONARY");
-               pthread_rwlock_init(dict->rwlock, NULL);
-       }
+    if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
+        dict->rwlock = callocz(1, sizeof(pthread_rwlock_t));
+        pthread_rwlock_init(dict->rwlock, NULL);
+    }
 
-       avl_init(&dict->values_index, name_value_compare);
-       dict->flags = flags;
+    avl_init(&dict->values_index, name_value_compare);
+    dict->flags = flags;
 
-       return dict;
+    return dict;
 }
 
 void dictionary_destroy(DICTIONARY *dict) {
-       debug(D_DICTIONARY, "Destroying dictionary.");
+    debug(D_DICTIONARY, "Destroying dictionary.");
 
-       dictionary_write_lock(dict);
+    dictionary_write_lock(dict);
 
-       while(dict->values_index.root)
-               dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
+    while(dict->values_index.root)
+        dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
 
-       dictionary_unlock(dict);
+    dictionary_unlock(dict);
 
-       if(dict->stats)
-               free(dict->stats);
+    if(dict->stats)
+        freez(dict->stats);
 
-       if(dict->rwlock)
-               free(dict->rwlock);
+    if(dict->rwlock)
+        freez(dict->rwlock);
 
-       free(dict);
+    freez(dict);
 }
 
 // ----------------------------------------------------------------------------
 
 void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
-       debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
-
-       uint32_t hash = simple_hash(name);
+    debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
 
-       dictionary_write_lock(dict);
+    uint32_t hash = simple_hash(name);
 
-       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
-       if(unlikely(!nv)) {
-               debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
+    dictionary_write_lock(dict);
 
-               nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
-               if(unlikely(!nv))
-                       fatal("Cannot create name_value.");
-       }
-       else {
-               debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
+    NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
+    if(unlikely(!nv)) {
+        debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
 
-               if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
-                       debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
-                       nv->value = value;
-               }
-               else {
-                       debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
+        nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
+        if(unlikely(!nv))
+            fatal("Cannot create name_value.");
+    }
+    else {
+        debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
 
-                       // copy the new value without breaking
-                       // any other thread accessing the same entry
-                       void *new = malloc(value_len),
-                                       *old = nv->value;
+        if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
+            debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
+            nv->value = value;
+        }
+        else {
+            debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
 
-                       if(unlikely(!new))
-                               fatal("Cannot allocate value of size %zu", value_len);
+            // copy the new value without breaking
+            // any other thread accessing the same entry
+            void *new = mallocz(value_len),
+                    *old = nv->value;
 
-                       memcpy(new, value, value_len);
-                       nv->value = new;
+            memcpy(new, value, value_len);
+            nv->value = new;
 
-                       debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
-                       free(old);
-               }
-       }
+            debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
+            freez(old);
+        }
+    }
 
-       dictionary_unlock(dict);
+    dictionary_unlock(dict);
 
-       return nv->value;
+    return nv->value;
 }
 
 void *dictionary_get(DICTIONARY *dict, const char *name) {
-       debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
+    debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
 
-       dictionary_read_lock(dict);
-       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
-       dictionary_unlock(dict);
+    dictionary_read_lock(dict);
+    NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
+    dictionary_unlock(dict);
 
-       if(unlikely(!nv)) {
-               debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
-               return NULL;
-       }
+    if(unlikely(!nv)) {
+        debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
+        return NULL;
+    }
 
-       debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
-       return nv->value;
+    debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
+    return nv->value;
 }
 
 int dictionary_del(DICTIONARY *dict, const char *name) {
-       int ret;
+    int ret;
 
-       debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
+    debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
 
-       dictionary_write_lock(dict);
+    dictionary_write_lock(dict);
 
-       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
-       if(unlikely(!nv)) {
-               debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
-               ret = -1;
-       }
-       else {
-               debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
-               dictionary_name_value_destroy_nolock(dict, nv);
-               ret = 0;
-       }
+    NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
+    if(unlikely(!nv)) {
+        debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
+        ret = -1;
+    }
+    else {
+        debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
+        dictionary_name_value_destroy_nolock(dict, nv);
+        ret = 0;
+    }
 
-       dictionary_unlock(dict);
+    dictionary_unlock(dict);
 
-       return ret;
+    return ret;
 }
 
 
@@ -278,36 +253,36 @@ int dictionary_del(DICTIONARY *dict, const char *name) {
 // do not user other dictionary calls while walking the dictionary - deadlock!
 
 static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
-       int total = 0, ret = 0;
+    int total = 0, ret = 0;
 
-       if(a->avl_link[0]) {
-               ret = dictionary_walker(a->avl_link[0], callback, data);
-               if(ret < 0) return ret;
-               total += ret;
-       }
+    if(a->avl_link[0]) {
+        ret = dictionary_walker(a->avl_link[0], callback, data);
+        if(ret < 0) return ret;
+        total += ret;
+    }
 
-       ret = callback(((NAME_VALUE *)a)->value, data);
-       if(ret < 0) return ret;
-       total += ret;
+    ret = callback(((NAME_VALUE *)a)->value, data);
+    if(ret < 0) return ret;
+    total += ret;
 
-       if(a->avl_link[1]) {
-               ret = dictionary_walker(a->avl_link[1], callback, data);
-               if (ret < 0) return ret;
-               total += ret;
-       }
+    if(a->avl_link[1]) {
+        ret = dictionary_walker(a->avl_link[1], callback, data);
+        if (ret < 0) return ret;
+        total += ret;
+    }
 
-       return total;
+    return total;
 }
 
 int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
-       int ret = 0;
+    int ret = 0;
 
-       dictionary_read_lock(dict);
+    dictionary_read_lock(dict);
 
-       if(likely(dict->values_index.root))
-               ret = dictionary_walker(dict->values_index.root, callback, data);
+    if(likely(dict->values_index.root))
+        ret = dictionary_walker(dict->values_index.root, callback, data);
 
-       dictionary_unlock(dict);
+    dictionary_unlock(dict);
 
-       return ret;
+    return ret;
 }
index c32d8bd92d82514ca6694e88ebaf0f6e5c3d8147..6bebbfa857083d08722493bdc0150822bf336a22 100644 (file)
@@ -1,49 +1,44 @@
-#include <pthread.h>
-
-#include "web_buffer.h"
-#include "avl.h"
-
 #ifndef NETDATA_DICTIONARY_H
 #define NETDATA_DICTIONARY_H 1
 
 struct dictionary_stats {
-       unsigned long long inserts;
-       unsigned long long deletes;
-       unsigned long long searches;
-       unsigned long long entries;
+    unsigned long long inserts;
+    unsigned long long deletes;
+    unsigned long long searches;
+    unsigned long long entries;
 };
 
 typedef struct name_value {
-       avl avl;                                // the index - this has to be first!
+    avl avl;                // the index - this has to be first!
 
-       uint32_t hash;                  // a simple hash to speed up searching
-                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
+    uint32_t hash;          // a simple hash to speed up searching
+                            // we first compare hashes, and only if the hashes are equal we do string comparisons
 
-       char *name;
-       void *value;
+    char *name;
+    void *value;
 } NAME_VALUE;
 
 typedef struct dictionary {
-       avl_tree values_index;
+    avl_tree values_index;
 
-       uint8_t flags;
+    uint8_t flags;
 
-       struct dictionary_stats *stats;
-       pthread_rwlock_t *rwlock;
+    struct dictionary_stats *stats;
+    pthread_rwlock_t *rwlock;
 } DICTIONARY;
 
-#define DICTIONARY_FLAG_DEFAULT                                        0x00000000
-#define DICTIONARY_FLAG_SINGLE_THREADED                        0x00000001
-#define DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE  0x00000002
-#define DICTIONARY_FLAG_NAME_LINK_DONT_CLONE   0x00000004
-#define DICTIONARY_FLAG_WITH_STATISTICS                        0x00000008
+#define DICTIONARY_FLAG_DEFAULT                 0x00000000
+#define DICTIONARY_FLAG_SINGLE_THREADED         0x00000001
+#define DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE   0x00000002
+#define DICTIONARY_FLAG_NAME_LINK_DONT_CLONE    0x00000004
+#define DICTIONARY_FLAG_WITH_STATISTICS         0x00000008
 
-extern DICTIONARY *dictionary_create(uint32_t flags);
+extern DICTIONARY *dictionary_create(uint8_t flags);
 extern void dictionary_destroy(DICTIONARY *dict);
 extern void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len);
 extern void *dictionary_get(DICTIONARY *dict, const char *name);
 extern int dictionary_del(DICTIONARY *dict, const char *name);
 
-extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data);
+extern int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *d), void *data);
 
 #endif /* NETDATA_DICTIONARY_H */
diff --git a/src/eval.c b/src/eval.c
new file mode 100644 (file)
index 0000000..f84e4e0
--- /dev/null
@@ -0,0 +1,958 @@
+#include "common.h"
+
+// ----------------------------------------------------------------------------
+// data structures for storing the parsed expression in memory
+
+typedef struct eval_value {
+    int type;
+
+    union {
+        calculated_number number;
+        EVAL_VARIABLE *variable;
+        struct eval_node *expression;
+    };
+} EVAL_VALUE;
+
+typedef struct eval_node {
+    int id;
+    unsigned char operator;
+    int precedence;
+
+    int count;
+    EVAL_VALUE ops[];
+} EVAL_NODE;
+
+// these are used for EVAL_NODE.operator
+// they are used as internal IDs to identify an operator
+// THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
+#define EVAL_OPERATOR_NOP                   '\0'
+#define EVAL_OPERATOR_EXPRESSION_OPEN       '('
+#define EVAL_OPERATOR_EXPRESSION_CLOSE      ')'
+#define EVAL_OPERATOR_NOT                   '!'
+#define EVAL_OPERATOR_PLUS                  '+'
+#define EVAL_OPERATOR_MINUS                 '-'
+#define EVAL_OPERATOR_AND                   '&'
+#define EVAL_OPERATOR_OR                    '|'
+#define EVAL_OPERATOR_GREATER_THAN_OR_EQUAL 'G'
+#define EVAL_OPERATOR_LESS_THAN_OR_EQUAL    'L'
+#define EVAL_OPERATOR_NOT_EQUAL             '~'
+#define EVAL_OPERATOR_EQUAL                 '='
+#define EVAL_OPERATOR_LESS                  '<'
+#define EVAL_OPERATOR_GREATER               '>'
+#define EVAL_OPERATOR_MULTIPLY              '*'
+#define EVAL_OPERATOR_DIVIDE                '/'
+#define EVAL_OPERATOR_SIGN_PLUS             'P'
+#define EVAL_OPERATOR_SIGN_MINUS            'M'
+
+// ----------------------------------------------------------------------------
+// forward function definitions
+
+static inline void eval_node_free(EVAL_NODE *op);
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error);
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error);
+static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
+static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error);
+static inline void print_parsed_as_constant(BUFFER *out, calculated_number n);
+
+// ----------------------------------------------------------------------------
+// evaluation of expressions
+
+static inline calculated_number eval_check_number(calculated_number n, int *error) {
+    if(unlikely(isnan(n))) {
+        *error = EVAL_ERROR_VALUE_IS_NAN;
+        return 0;
+    }
+
+    if(unlikely(isinf(n))) {
+        *error = EVAL_ERROR_VALUE_IS_INFINITE;
+        return 0;
+    }
+
+    return n;
+}
+
+static inline calculated_number eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
+    static uint32_t this_hash = 0;
+
+    if(unlikely(this_hash == 0))
+        this_hash = simple_hash("this");
+
+    if(exp->this && v->hash == this_hash && !strcmp(v->name, "this")) {
+        buffer_strcat(exp->error_msg, "[ $this = ");
+        print_parsed_as_constant(exp->error_msg, *exp->this);
+        buffer_strcat(exp->error_msg, " ] ");
+        return *exp->this;
+    }
+
+    calculated_number n;
+    if(exp->rrdcalc && health_variable_lookup(v->name, v->hash, exp->rrdcalc, &n)) {
+        buffer_sprintf(exp->error_msg, "[ $%s = ", v->name);
+        print_parsed_as_constant(exp->error_msg, n);
+        buffer_strcat(exp->error_msg, " ] ");
+        return n;
+    }
+
+    *error = EVAL_ERROR_UNKNOWN_VARIABLE;
+    buffer_sprintf(exp->error_msg, "unknown variable '%s'", v->name);
+    return 0;
+}
+
+static inline calculated_number eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
+    calculated_number n;
+
+    switch(v->type) {
+        case EVAL_VALUE_EXPRESSION:
+            n = eval_node(exp, v->expression, error);
+            break;
+
+        case EVAL_VALUE_NUMBER:
+            n = v->number;
+            break;
+
+        case EVAL_VALUE_VARIABLE:
+            n = eval_variable(exp, v->variable, error);
+            break;
+
+        default:
+            *error = EVAL_ERROR_INVALID_VALUE;
+            n = 0;
+            break;
+    }
+
+    // return eval_check_number(n, error);
+    return n;
+}
+
+calculated_number eval_and(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) && eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_or(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) || eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_greater_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) >= eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_less_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) <= eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_not_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) != eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) == eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_less(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) < eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_greater(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) > eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) + eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) - eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_multiply(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) * eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_divide(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error) / eval_value(exp, &op->ops[1], error);
+}
+calculated_number eval_nop(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_not(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return !eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_sign_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return eval_value(exp, &op->ops[0], error);
+}
+calculated_number eval_sign_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    return -eval_value(exp, &op->ops[0], error);
+}
+
+static struct operator {
+    const char *print_as;
+    char precedence;
+    char parameters;
+    calculated_number (*eval)(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
+} operators[256] = {
+        // this is a random access array
+        // we always access it with a known EVAL_OPERATOR_X
+
+        [EVAL_OPERATOR_AND]                   = { "&&", 2, 2, eval_and },
+        [EVAL_OPERATOR_OR]                    = { "||", 2, 2, eval_or },
+        [EVAL_OPERATOR_GREATER_THAN_OR_EQUAL] = { ">=", 3, 2, eval_greater_than_or_equal },
+        [EVAL_OPERATOR_LESS_THAN_OR_EQUAL]    = { "<=", 3, 2, eval_less_than_or_equal },
+        [EVAL_OPERATOR_NOT_EQUAL]             = { "!=", 3, 2, eval_not_equal },
+        [EVAL_OPERATOR_EQUAL]                 = { "==", 3, 2, eval_equal },
+        [EVAL_OPERATOR_LESS]                  = { "<",  3, 2, eval_less },
+        [EVAL_OPERATOR_GREATER]               = { ">",  3, 2, eval_greater },
+        [EVAL_OPERATOR_PLUS]                  = { "+",  4, 2, eval_plus },
+        [EVAL_OPERATOR_MINUS]                 = { "-",  4, 2, eval_minus },
+        [EVAL_OPERATOR_MULTIPLY]              = { "*",  5, 2, eval_multiply },
+        [EVAL_OPERATOR_DIVIDE]                = { "/",  5, 2, eval_divide },
+        [EVAL_OPERATOR_NOT]                   = { "!",  6, 1, eval_not },
+        [EVAL_OPERATOR_SIGN_PLUS]             = { "+",  6, 1, eval_sign_plus },
+        [EVAL_OPERATOR_SIGN_MINUS]            = { "-",  6, 1, eval_sign_minus },
+        [EVAL_OPERATOR_NOP]                   = { NULL, 7, 1, eval_nop },
+        [EVAL_OPERATOR_EXPRESSION_OPEN]       = { NULL, 7, 1, eval_nop },
+
+        // this should exist in our evaluation list
+        [EVAL_OPERATOR_EXPRESSION_CLOSE]      = { NULL, 7, 1, eval_nop }
+};
+
+#define eval_precedence(operator) (operators[(unsigned char)(operator)].precedence)
+
+static inline calculated_number eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
+    if(unlikely(op->count != operators[op->operator].parameters)) {
+        *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
+        return 0;
+    }
+
+    calculated_number n = operators[op->operator].eval(exp, op, error);
+
+    // return eval_check_number(n, error);
+    return n;
+}
+
+// ----------------------------------------------------------------------------
+// parsed-as generation
+
+static inline void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error) {
+    (void)error;
+    buffer_sprintf(out, "$%s", v->name);
+}
+
+static inline void print_parsed_as_constant(BUFFER *out, calculated_number n) {
+    if(unlikely(isnan(n))) {
+        buffer_strcat(out, "nan");
+        return;
+    }
+
+    if(unlikely(isinf(n))) {
+        buffer_strcat(out, "inf");
+        return;
+    }
+
+    char b[100+1], *s;
+    snprintfz(b, 100, CALCULATED_NUMBER_FORMAT, n);
+
+    s = &b[strlen(b) - 1];
+    while(s > b && *s == '0') {
+        *s ='\0';
+        s--;
+    }
+
+    if(s > b && *s == '.')
+        *s = '\0';
+
+    buffer_strcat(out, b);
+}
+
+static inline void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error) {
+    switch(v->type) {
+        case EVAL_VALUE_EXPRESSION:
+            print_parsed_as_node(out, v->expression, error);
+            break;
+
+        case EVAL_VALUE_NUMBER:
+            print_parsed_as_constant(out, v->number);
+            break;
+
+        case EVAL_VALUE_VARIABLE:
+            print_parsed_as_variable(out, v->variable, error);
+            break;
+
+        default:
+            *error = EVAL_ERROR_INVALID_VALUE;
+            break;
+    }
+}
+
+static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error) {
+    if(unlikely(op->count != operators[op->operator].parameters)) {
+        *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
+        return;
+    }
+
+    if(operators[op->operator].parameters == 1) {
+
+        if(operators[op->operator].print_as)
+            buffer_sprintf(out, "%s", operators[op->operator].print_as);
+
+        //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
+        //    buffer_strcat(out, "(");
+
+        print_parsed_as_value(out, &op->ops[0], error);
+
+        //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
+        //    buffer_strcat(out, ")");
+    }
+
+    else if(operators[op->operator].parameters == 2) {
+        buffer_strcat(out, "(");
+        print_parsed_as_value(out, &op->ops[0], error);
+
+        if(operators[op->operator].print_as)
+            buffer_sprintf(out, " %s ", operators[op->operator].print_as);
+
+        print_parsed_as_value(out, &op->ops[1], error);
+        buffer_strcat(out, ")");
+    }
+}
+
+// ----------------------------------------------------------------------------
+// parsing expressions
+
+// skip spaces
+static inline void skip_spaces(const char **string) {
+    const char *s = *string;
+    while(isspace(*s)) s++;
+    *string = s;
+}
+
+// what character can appear just after an operator keyword
+// like NOT AND OR ?
+static inline int isoperatorterm_word(const char s) {
+    if(isspace(s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit(s) || !s)
+        return 1;
+
+    return 0;
+}
+
+// what character can appear just after an operator symbol?
+static inline int isoperatorterm_symbol(const char s) {
+    if(isoperatorterm_word(s) || isalpha(s))
+        return 1;
+
+    return 0;
+}
+
+// return 1 if the character should never appear in a variable
+static inline int isvariableterm(const char s) {
+    if(isalnum(s) || s == '.' || s == '_')
+        return 0;
+
+    return 1;
+}
+
+// ----------------------------------------------------------------------------
+// parse operators
+
+static inline int parse_and(const char **string) {
+    const char *s = *string;
+
+    // AND
+    if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') && isoperatorterm_word(s[3])) {
+        *string = &s[4];
+        return 1;
+    }
+
+    // &&
+    if(s[0] == '&' && s[1] == '&' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_or(const char **string) {
+    const char *s = *string;
+
+    // OR
+    if((s[0] == 'O' || s[0] == 'o') && (s[1] == 'R' || s[1] == 'r') && isoperatorterm_word(s[2])) {
+        *string = &s[3];
+        return 1;
+    }
+
+    // ||
+    if(s[0] == '|' && s[1] == '|' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_greater_than_or_equal(const char **string) {
+    const char *s = *string;
+
+    // >=
+    if(s[0] == '>' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_less_than_or_equal(const char **string) {
+    const char *s = *string;
+
+    // <=
+    if (s[0] == '<' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_greater(const char **string) {
+    const char *s = *string;
+
+    // >
+    if(s[0] == '>' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_less(const char **string) {
+    const char *s = *string;
+
+    // <
+    if(s[0] == '<' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_equal(const char **string) {
+    const char *s = *string;
+
+    // ==
+    if(s[0] == '=' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+        return 1;
+    }
+
+    // =
+    if(s[0] == '=' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_not_equal(const char **string) {
+    const char *s = *string;
+
+    // !=
+    if(s[0] == '!' && s[1] == '=' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+        return 1;
+    }
+
+    // <>
+    if(s[0] == '<' && s[1] == '>' && isoperatorterm_symbol(s[2])) {
+        *string = &s[2];
+    }
+
+    return 0;
+}
+
+static inline int parse_not(const char **string) {
+    const char *s = *string;
+
+    // NOT
+    if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') && isoperatorterm_word(s[3])) {
+        *string = &s[3];
+        return 1;
+    }
+
+    if(s[0] == '!') {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_multiply(const char **string) {
+    const char *s = *string;
+
+    // *
+    if(s[0] == '*' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_divide(const char **string) {
+    const char *s = *string;
+
+    // /
+    if(s[0] == '/' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_minus(const char **string) {
+    const char *s = *string;
+
+    // -
+    if(s[0] == '-' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_plus(const char **string) {
+    const char *s = *string;
+
+    // +
+    if(s[0] == '+' && isoperatorterm_symbol(s[1])) {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_open_subexpression(const char **string) {
+    const char *s = *string;
+
+    // (
+    if(s[0] == '(') {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_close_subexpression(const char **string) {
+    const char *s = *string;
+
+    // (
+    if(s[0] == ')') {
+        *string = &s[1];
+        return 1;
+    }
+
+    return 0;
+}
+
+static inline int parse_variable(const char **string, char *buffer, size_t len) {
+    const char *s = *string;
+
+    // $
+    if(s[0] == '$') {
+        size_t i = 0;
+        s++;
+
+        while(*s && !isvariableterm(*s) && i < len)
+            buffer[i++] = *s++;
+
+        buffer[i] = '\0';
+
+        if(buffer[0]) {
+            *string = &s[0];
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static inline int parse_constant(const char **string, calculated_number *number) {
+    char *end = NULL;
+    calculated_number n = strtold(*string, &end);
+    if(unlikely(!end || *string == end)) {
+        *number = 0;
+        return 0;
+    }
+    *number = n;
+    *string = end;
+    return 1;
+}
+
+static struct operator_parser {
+    unsigned char id;
+    int (*parse)(const char **);
+} operator_parsers[] = {
+        // the order in this list is important!
+        // the first matching will be used
+        // so place the longer of overlapping ones
+        // at the top
+
+        { EVAL_OPERATOR_AND,                   parse_and },
+        { EVAL_OPERATOR_OR,                    parse_or },
+        { EVAL_OPERATOR_GREATER_THAN_OR_EQUAL, parse_greater_than_or_equal },
+        { EVAL_OPERATOR_LESS_THAN_OR_EQUAL,    parse_less_than_or_equal },
+        { EVAL_OPERATOR_NOT_EQUAL,             parse_not_equal },
+        { EVAL_OPERATOR_EQUAL,                 parse_equal },
+        { EVAL_OPERATOR_LESS,                  parse_less },
+        { EVAL_OPERATOR_GREATER,               parse_greater },
+        { EVAL_OPERATOR_PLUS,                  parse_plus },
+        { EVAL_OPERATOR_MINUS,                 parse_minus },
+        { EVAL_OPERATOR_MULTIPLY,              parse_multiply },
+        { EVAL_OPERATOR_DIVIDE,                parse_divide },
+
+        /* we should not put in this list the following:
+         *
+         *  - NOT
+         *  - (
+         *  - )
+         *
+         * these are handled in code
+         */
+
+        // termination
+        { EVAL_OPERATOR_NOP, NULL }
+};
+
+static inline unsigned char parse_operator(const char **string, int *precedence) {
+    skip_spaces(string);
+
+    int i;
+    for(i = 0 ; operator_parsers[i].parse != NULL ; i++)
+        if(operator_parsers[i].parse(string)) {
+            if(precedence) *precedence = eval_precedence(operator_parsers[i].id);
+            return operator_parsers[i].id;
+        }
+
+    return EVAL_OPERATOR_NOP;
+}
+
+// ----------------------------------------------------------------------------
+// memory management
+
+static inline EVAL_NODE *eval_node_alloc(int count) {
+    static int id = 1;
+
+    EVAL_NODE *op = callocz(1, sizeof(EVAL_NODE) + (sizeof(EVAL_VALUE) * count));
+
+    op->id = id++;
+    op->operator = EVAL_OPERATOR_NOP;
+    op->precedence = eval_precedence(EVAL_OPERATOR_NOP);
+    op->count = count;
+    return op;
+}
+
+static inline void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value) {
+    if(pos >= op->count)
+        fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+    op->ops[pos].type = EVAL_VALUE_EXPRESSION;
+    op->ops[pos].expression = value;
+}
+
+static inline void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, calculated_number value) {
+    if(pos >= op->count)
+        fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+    op->ops[pos].type = EVAL_VALUE_NUMBER;
+    op->ops[pos].number = value;
+}
+
+static inline void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const char *variable) {
+    if(pos >= op->count)
+        fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
+
+    op->ops[pos].type = EVAL_VALUE_VARIABLE;
+    op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE));
+    op->ops[pos].variable->name = strdupz(variable);
+    op->ops[pos].variable->hash = simple_hash(op->ops[pos].variable->name);
+}
+
+static inline void eval_variable_free(EVAL_VARIABLE *v) {
+    freez(v->name);
+    freez(v);
+}
+
+static inline void eval_value_free(EVAL_VALUE *v) {
+    switch(v->type) {
+        case EVAL_VALUE_EXPRESSION:
+            eval_node_free(v->expression);
+            break;
+
+        case EVAL_VALUE_VARIABLE:
+            eval_variable_free(v->variable);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static inline void eval_node_free(EVAL_NODE *op) {
+    if(op->count) {
+        int i;
+        for(i = op->count - 1; i >= 0 ;i--)
+            eval_value_free(&op->ops[i]);
+    }
+
+    freez(op);
+}
+
+// ----------------------------------------------------------------------------
+// the parsing logic
+
+// helper function to avoid allocations all over the place
+static inline EVAL_NODE *parse_next_operand_given_its_operator(const char **string, unsigned char operator_type, int *error) {
+    EVAL_NODE *sub = parse_one_full_operand(string, error);
+    if(!sub) return NULL;
+
+    EVAL_NODE *op = eval_node_alloc(1);
+    op->operator = operator_type;
+    eval_node_set_value_to_node(op, 0, sub);
+    return op;
+}
+
+// parse a full operand, including its sign or other associative operator (e.g. NOT)
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error) {
+    char variable_buffer[EVAL_MAX_VARIABLE_NAME_LENGTH + 1];
+    EVAL_NODE *op1 = NULL;
+    calculated_number number;
+
+    *error = EVAL_ERROR_OK;
+
+    skip_spaces(string);
+    if(!(**string)) {
+        *error = EVAL_ERROR_MISSING_OPERAND;
+        return NULL;
+    }
+
+    if(parse_not(string)) {
+        op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_NOT, error);
+        op1->precedence = eval_precedence(EVAL_OPERATOR_NOT);
+    }
+    else if(parse_plus(string)) {
+        op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_PLUS, error);
+        op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
+    }
+    else if(parse_minus(string)) {
+        op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_MINUS, error);
+        op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
+    }
+    else if(parse_open_subexpression(string)) {
+        EVAL_NODE *sub = parse_full_expression(string, error);
+        if(sub) {
+            op1 = eval_node_alloc(1);
+            op1->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
+            op1->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
+            eval_node_set_value_to_node(op1, 0, sub);
+            if(!parse_close_subexpression(string)) {
+                *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
+                eval_node_free(op1);
+                return NULL;
+            }
+        }
+    }
+    else if(parse_variable(string, variable_buffer, EVAL_MAX_VARIABLE_NAME_LENGTH)) {
+        op1 = eval_node_alloc(1);
+        op1->operator = EVAL_OPERATOR_NOP;
+        eval_node_set_value_to_variable(op1, 0, variable_buffer);
+    }
+    else if(parse_constant(string, &number)) {
+        op1 = eval_node_alloc(1);
+        op1->operator = EVAL_OPERATOR_NOP;
+        eval_node_set_value_to_constant(op1, 0, number);
+    }
+    else if(**string)
+        *error = EVAL_ERROR_UNKNOWN_OPERAND;
+    else
+        *error = EVAL_ERROR_MISSING_OPERAND;
+
+    return op1;
+}
+
+// parse an operator and the rest of the expression
+// precedence processing is handled here
+static inline EVAL_NODE *parse_rest_of_expression(const char **string, int *error, EVAL_NODE *op1) {
+    EVAL_NODE *op2 = NULL;
+    unsigned char operator;
+    int precedence;
+
+    operator = parse_operator(string, &precedence);
+    skip_spaces(string);
+
+    if(operator != EVAL_OPERATOR_NOP) {
+        op2 = parse_one_full_operand(string, error);
+        if(!op2) {
+            // error is already reported
+            eval_node_free(op1);
+            return NULL;
+        }
+
+        EVAL_NODE *op = eval_node_alloc(2);
+        op->operator = operator;
+        op->precedence = precedence;
+
+        eval_node_set_value_to_node(op, 1, op2);
+
+        // precedence processing
+        // if this operator has a higher precedence compared to its next
+        // put the next operator on top of us (top = evaluated later)
+        // function recursion does the rest...
+        if(op->precedence > op1->precedence && op1->count == 2 && op1->operator != '(' && op1->ops[1].type == EVAL_VALUE_EXPRESSION) {
+            eval_node_set_value_to_node(op, 0, op1->ops[1].expression);
+            op1->ops[1].expression = op;
+            op = op1;
+        }
+        else
+            eval_node_set_value_to_node(op, 0, op1);
+
+        return parse_rest_of_expression(string, error, op);
+    }
+    else if(**string == ')') {
+        ;
+    }
+    else if(**string) {
+        if(op1) eval_node_free(op1);
+        op1 = NULL;
+        *error = EVAL_ERROR_MISSING_OPERATOR;
+    }
+
+    return op1;
+}
+
+// high level function to parse an expression or a sub-expression
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error) {
+    EVAL_NODE *op1 = NULL;
+
+    op1 = parse_one_full_operand(string, error);
+    if(!op1) {
+        *error = EVAL_ERROR_MISSING_OPERAND;
+        return NULL;
+    }
+
+    return parse_rest_of_expression(string, error, op1);
+}
+
+// ----------------------------------------------------------------------------
+// public API
+
+int expression_evaluate(EVAL_EXPRESSION *exp) {
+    exp->error = EVAL_ERROR_OK;
+
+    buffer_reset(exp->error_msg);
+    exp->result = eval_node(exp, (EVAL_NODE *)exp->nodes, &exp->error);
+
+    if(exp->error == EVAL_ERROR_OK)
+        exp->result = eval_check_number(exp->result, &exp->error);
+
+    if(exp->error != EVAL_ERROR_OK) {
+        exp->result = NAN;
+
+        if(buffer_strlen(exp->error_msg))
+            buffer_strcat(exp->error_msg, "; ");
+
+        buffer_sprintf(exp->error_msg, "failed to evaluate expression with error %d (%s)", exp->error, expression_strerror(exp->error));
+        return 0;
+    }
+
+    return 1;
+}
+
+EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
+    const char *s = string;
+    int err = EVAL_ERROR_OK;
+    unsigned long pos = 0;
+
+    EVAL_NODE *op = parse_full_expression(&s, &err);
+
+    if(*s) {
+        if(op) {
+            eval_node_free(op);
+            op = NULL;
+        }
+        err = EVAL_ERROR_REMAINING_GARBAGE;
+    }
+
+    if (failed_at) *failed_at = s;
+    if (error) *error = err;
+
+    if(!op) {
+        pos = s - string + 1;
+        error("failed to parse expression '%s': %s at character %lu (i.e.: '%s').", string, expression_strerror(err), pos, s);
+        return NULL;
+    }
+
+    BUFFER *out = buffer_create(1024);
+    print_parsed_as_node(out, op, &err);
+    if(err != EVAL_ERROR_OK) {
+        error("failed to re-generate expression '%s' with reason: %s", string, expression_strerror(err));
+        eval_node_free(op);
+        buffer_free(out);
+        return NULL;
+    }
+
+    EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
+
+    exp->source = strdupz(string);
+    exp->parsed_as = strdupz(buffer_tostring(out));
+    buffer_free(out);
+
+    exp->error_msg = buffer_create(100);
+    exp->nodes = (void *)op;
+
+    return exp;
+}
+
+void expression_free(EVAL_EXPRESSION *exp) {
+    if(!exp) return;
+
+    if(exp->nodes) eval_node_free((EVAL_NODE *)exp->nodes);
+    freez((void *)exp->source);
+    freez((void *)exp->parsed_as);
+    buffer_free(exp->error_msg);
+    freez(exp);
+}
+
+const char *expression_strerror(int error) {
+    switch(error) {
+        case EVAL_ERROR_OK:
+            return "success";
+
+        case EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION:
+            return "missing closing parenthesis";
+
+        case EVAL_ERROR_UNKNOWN_OPERAND:
+            return "unknown operand";
+
+        case EVAL_ERROR_MISSING_OPERAND:
+            return "expected operand";
+
+        case EVAL_ERROR_MISSING_OPERATOR:
+            return "expected operator";
+
+        case EVAL_ERROR_REMAINING_GARBAGE:
+            return "remaining characters after expression";
+
+        case EVAL_ERROR_INVALID_VALUE:
+            return "invalid value structure - internal error";
+
+        case EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS:
+            return "wrong number of operands for operation - internal error";
+
+        case EVAL_ERROR_VALUE_IS_NAN:
+            return "value is unset";
+
+        case EVAL_ERROR_VALUE_IS_INFINITE:
+            return "computed value is infinite";
+
+        case EVAL_ERROR_UNKNOWN_VARIABLE:
+            return "undefined variable";
+
+        default:
+            return "unknown error";
+    }
+}
diff --git a/src/eval.h b/src/eval.h
new file mode 100644 (file)
index 0000000..0b174a2
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef NETDATA_EVAL_H
+#define NETDATA_EVAL_H
+
+#define EVAL_MAX_VARIABLE_NAME_LENGTH 300
+
+typedef struct eval_variable {
+    char *name;
+    uint32_t hash;
+    struct rrdvar *rrdvar;
+    struct eval_variable *next;
+} EVAL_VARIABLE;
+
+typedef struct eval_expression {
+    const char *source;
+    const char *parsed_as;
+
+    calculated_number *this;
+    calculated_number result;
+
+    int error;
+    BUFFER *error_msg;
+
+    // hidden EVAL_NODE *
+    void *nodes;
+
+    // custom data to be used for looking up variables
+    struct rrdcalc *rrdcalc;
+} EVAL_EXPRESSION;
+
+#define EVAL_VALUE_INVALID 0
+#define EVAL_VALUE_NUMBER 1
+#define EVAL_VALUE_VARIABLE 2
+#define EVAL_VALUE_EXPRESSION 3
+
+#define EVAL_ERROR_OK 0
+
+// parsing errors
+#define EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION 1
+#define EVAL_ERROR_UNKNOWN_OPERAND 2
+#define EVAL_ERROR_MISSING_OPERAND 3
+#define EVAL_ERROR_MISSING_OPERATOR 4
+#define EVAL_ERROR_REMAINING_GARBAGE 5
+
+// evaluation errors
+#define EVAL_ERROR_INVALID_VALUE 11
+#define EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS 12
+#define EVAL_ERROR_VALUE_IS_NAN 13
+#define EVAL_ERROR_VALUE_IS_INFINITE 14
+#define EVAL_ERROR_UNKNOWN_VARIABLE 15
+
+// parse the given string as an expression and return:
+//   a pointer to an expression if it parsed OK
+//   NULL in which case the pointer to error has the error code
+extern EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error);
+
+// free all resources allocated for an expression
+extern void expression_free(EVAL_EXPRESSION *op);
+
+// convert an error code to a message
+extern const char *expression_strerror(int error);
+
+// evaluate an expression and return
+// 1 = OK, the result is in: expression->result
+// 2 = FAILED, the error message is in: buffer_tostring(expression->error_msg)
+extern int expression_evaluate(EVAL_EXPRESSION *expression);
+
+#endif //NETDATA_EVAL_H
index 40d3c3e3da83a6b38e9ea66a3da31a0fc8ae7425..ad89ab5446048396cd6c173a606b461f6594dcdc 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
+#include "common.h"
+
+volatile struct global_statistics global_statistics = {
+        .connected_clients = 0,
+        .web_requests = 0,
+        .web_usec = 0,
+        .bytes_received = 0,
+        .bytes_sent = 0,
+        .content_size = 0,
+        .compressed_content_size = 0
+};
+
+pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+inline void global_statistics_lock(void) {
+    pthread_mutex_lock(&global_statistics_mutex);
+}
+
+inline void global_statistics_unlock(void) {
+    pthread_mutex_unlock(&global_statistics_mutex);
+}
+
+void finished_web_request_statistics(uint64_t dt,
+                                     uint64_t bytes_received,
+                                     uint64_t bytes_sent,
+                                     uint64_t content_size,
+                                     uint64_t compressed_content_size) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+    uint64_t old_web_usec_max = global_statistics.web_usec_max;
+    while(dt > old_web_usec_max) {
+        if(__sync_bool_compare_and_swap(&global_statistics.web_usec_max, old_web_usec_max, dt))
+            old_web_usec_max = dt;
+        else
+            old_web_usec_max = global_statistics.web_usec_max;
+    }
+
+    __sync_fetch_and_add(&global_statistics.web_requests, 1);
+    __sync_fetch_and_add(&global_statistics.web_usec, dt);
+    __sync_fetch_and_add(&global_statistics.bytes_received, bytes_received);
+    __sync_fetch_and_add(&global_statistics.bytes_sent, bytes_sent);
+    __sync_fetch_and_add(&global_statistics.content_size, content_size);
+    __sync_fetch_and_add(&global_statistics.compressed_content_size, compressed_content_size);
+#else
+    if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+        global_statistics_lock();
+
+    if (dt > global_statistics.web_usec_max)
+        global_statistics.web_usec_max = dt;
+
+    global_statistics.web_requests++;
+    global_statistics.web_usec += dt;
+    global_statistics.bytes_received += bytes_received;
+    global_statistics.bytes_sent += bytes_sent;
+    global_statistics.content_size += content_size;
+    global_statistics.compressed_content_size += compressed_content_size;
+
+    if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+        global_statistics_unlock();
 #endif
-#include <pthread.h>
+}
 
-#include "global_statistics.h"
+void web_client_connected(void) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+    __sync_fetch_and_add(&global_statistics.connected_clients, 1);
+#else
+    if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+        global_statistics_lock();
 
-struct global_statistics global_statistics = { 0, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL};
+    global_statistics.connected_clients++;
 
-pthread_mutex_t global_statistics_mutex = PTHREAD_MUTEX_INITIALIZER;
+    if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+        global_statistics_unlock();
+#endif
+}
+
+void web_client_disconnected(void) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+    __sync_fetch_and_sub(&global_statistics.connected_clients, 1);
+#else
+    if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+        global_statistics_lock();
 
-void global_statistics_lock(void) {
-       pthread_mutex_lock(&global_statistics_mutex);
+    global_statistics.connected_clients--;
+
+    if (web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
+        global_statistics_unlock();
+#endif
 }
 
-void global_statistics_unlock(void) {
-       pthread_mutex_unlock(&global_statistics_mutex);
+
+inline void global_statistics_copy(struct global_statistics *gs, uint8_t options) {
+#ifndef NETDATA_NO_ATOMIC_INSTRUCTIONS
+    gs->connected_clients       = __sync_fetch_and_add(&global_statistics.connected_clients, 0);
+    gs->web_requests            = __sync_fetch_and_add(&global_statistics.web_requests, 0);
+    gs->web_usec                = __sync_fetch_and_add(&global_statistics.web_usec, 0);
+    gs->web_usec_max            = __sync_fetch_and_add(&global_statistics.web_usec_max, 0);
+    gs->bytes_received          = __sync_fetch_and_add(&global_statistics.bytes_received, 0);
+    gs->bytes_sent              = __sync_fetch_and_add(&global_statistics.bytes_sent, 0);
+    gs->content_size            = __sync_fetch_and_add(&global_statistics.content_size, 0);
+    gs->compressed_content_size = __sync_fetch_and_add(&global_statistics.compressed_content_size, 0);
+
+    if(options & GLOBAL_STATS_RESET_WEB_USEC_MAX)
+        __sync_bool_compare_and_swap(&global_statistics.web_usec_max, gs->web_usec_max, 0);
+#else
+    global_statistics_lock();
+
+    memcpy(gs, (const void *)&global_statistics, sizeof(struct global_statistics));
+
+    if (options & GLOBAL_STATS_RESET_WEB_USEC_MAX)
+        global_statistics.web_usec_max = 0;
+
+    global_statistics_unlock();
+#endif
 }
+
+void global_statistics_charts(void) {
+    static unsigned long long old_web_requests = 0, old_web_usec = 0,
+            old_content_size = 0, old_compressed_content_size = 0;
+
+    static collected_number compression_ratio = -1, average_response_time = -1;
+
+    static RRDSET *stcpu = NULL, *stcpu_thread = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL, *stduration = NULL,
+            *stcompression = NULL;
+
+    struct global_statistics gs;
+    struct rusage me, thread;
+
+    global_statistics_copy(&gs, GLOBAL_STATS_RESET_WEB_USEC_MAX);
+    getrusage(RUSAGE_THREAD, &thread);
+    getrusage(RUSAGE_SELF, &me);
+
+    if (!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu");
+    if (!stcpu_thread) {
+        stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc.internal", NULL,
+                                     "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every,
+                                     RRDSET_TYPE_STACKED);
+
+        rrddim_add(stcpu_thread, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+        rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+    } else rrdset_next(stcpu_thread);
+
+    rrddim_set(stcpu_thread, "user", thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
+    rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
+    rrdset_done(stcpu_thread);
+
+    // ----------------------------------------------------------------
+
+    if (!stcpu) stcpu = rrdset_find("netdata.server_cpu");
+    if (!stcpu) {
+        stcpu = rrdset_create("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage", "milliseconds/s",
+                              130000, rrd_update_every, RRDSET_TYPE_STACKED);
+
+        rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+        rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+    } else rrdset_next(stcpu);
+
+    rrddim_set(stcpu, "user", me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec);
+    rrddim_set(stcpu, "system", me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec);
+    rrdset_done(stcpu);
+
+    // ----------------------------------------------------------------
+
+    if (!stclients) stclients = rrdset_find("netdata.clients");
+    if (!stclients) {
+        stclients = rrdset_create("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients",
+                                  "connected clients", 130200, rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(stclients, "clients", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    } else rrdset_next(stclients);
+
+    rrddim_set(stclients, "clients", gs.connected_clients);
+    rrdset_done(stclients);
+
+    // ----------------------------------------------------------------
+
+    if (!streqs) streqs = rrdset_find("netdata.requests");
+    if (!streqs) {
+        streqs = rrdset_create("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests", "requests/s",
+                               130300, rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(streqs, "requests", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    } else rrdset_next(streqs);
+
+    rrddim_set(streqs, "requests", (collected_number) gs.web_requests);
+    rrdset_done(streqs);
+
+    // ----------------------------------------------------------------
+
+    if (!stbytes) stbytes = rrdset_find("netdata.net");
+    if (!stbytes) {
+        stbytes = rrdset_create("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic", "kilobits/s",
+                                130000, rrd_update_every, RRDSET_TYPE_AREA);
+
+        rrddim_add(stbytes, "in", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+        rrddim_add(stbytes, "out", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+    } else rrdset_next(stbytes);
+
+    rrddim_set(stbytes, "in", (collected_number) gs.bytes_received);
+    rrddim_set(stbytes, "out", (collected_number) gs.bytes_sent);
+    rrdset_done(stbytes);
+
+    // ----------------------------------------------------------------
+
+    if (!stduration) stduration = rrdset_find("netdata.response_time");
+    if (!stduration) {
+        stduration = rrdset_create("netdata", "response_time", NULL, "netdata", NULL, "NetData API Response Time",
+                                   "ms/request", 130400, rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(stduration, "average", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+        rrddim_add(stduration, "max", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+    } else rrdset_next(stduration);
+
+    uint64_t gweb_usec = gs.web_usec;
+    uint64_t gweb_requests = gs.web_requests;
+
+    uint64_t web_usec = (gweb_usec >= old_web_usec) ? gweb_usec - old_web_usec : 0;
+    uint64_t web_requests = (gweb_requests >= old_web_requests) ? gweb_requests - old_web_requests : 0;
+
+    old_web_usec = gweb_usec;
+    old_web_requests = gweb_requests;
+
+    if (web_requests)
+        average_response_time = (collected_number) (web_usec / web_requests);
+
+    if (unlikely(average_response_time != -1))
+        rrddim_set(stduration, "average", average_response_time);
+    else
+        rrddim_set(stduration, "average", 0);
+
+    rrddim_set(stduration, "max", ((gs.web_usec_max)?(collected_number)gs.web_usec_max:average_response_time));
+    rrdset_done(stduration);
+
+    // ----------------------------------------------------------------
+
+    if (!stcompression) stcompression = rrdset_find("netdata.compression_ratio");
+    if (!stcompression) {
+        stcompression = rrdset_create("netdata", "compression_ratio", NULL, "netdata", NULL,
+                                      "NetData API Responses Compression Savings Ratio", "percentage", 130500,
+                                      rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(stcompression, "savings", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+    } else rrdset_next(stcompression);
+
+    // since we don't lock here to read the global statistics
+    // read the smaller value first
+    unsigned long long gcompressed_content_size = gs.compressed_content_size;
+    unsigned long long gcontent_size = gs.content_size;
+
+    unsigned long long compressed_content_size = gcompressed_content_size - old_compressed_content_size;
+    unsigned long long content_size = gcontent_size - old_content_size;
+
+    old_compressed_content_size = gcompressed_content_size;
+    old_content_size = gcontent_size;
+
+    if (content_size && content_size >= compressed_content_size)
+        compression_ratio = ((content_size - compressed_content_size) * 100 * 1000) / content_size;
+
+    if (compression_ratio != -1)
+        rrddim_set(stcompression, "savings", compression_ratio);
+
+    rrdset_done(stcompression);
+}
\ No newline at end of file
index 56a8118c727fb27f3aea58f7a0ee7f2d6547baa5..d28aa4401ce464d4e97b2724a8b48be8314d8ebc 100644 (file)
@@ -5,18 +5,32 @@
 // global statistics
 
 struct global_statistics {
-       volatile unsigned long volatile connected_clients;
-       volatile unsigned long long volatile web_requests;
-       volatile unsigned long long volatile web_usec;
-       volatile unsigned long long volatile bytes_received;
-       volatile unsigned long long volatile bytes_sent;
-       volatile unsigned long long volatile content_size;
-       volatile unsigned long long volatile compressed_content_size;
+    volatile uint16_t connected_clients;
+
+    volatile uint64_t web_requests;
+    volatile uint64_t web_usec;
+    volatile uint64_t web_usec_max;
+    volatile uint64_t bytes_received;
+    volatile uint64_t bytes_sent;
+    volatile uint64_t content_size;
+    volatile uint64_t compressed_content_size;
 };
 
-extern struct global_statistics global_statistics;
+extern volatile struct global_statistics global_statistics;
 
 extern void global_statistics_lock(void);
 extern void global_statistics_unlock(void);
+extern void finished_web_request_statistics(uint64_t dt,
+                                     uint64_t bytes_received,
+                                     uint64_t bytes_sent,
+                                     uint64_t content_size,
+                                     uint64_t compressed_content_size);
+
+extern void web_client_connected(void);
+extern void web_client_disconnected(void);
+
+#define GLOBAL_STATS_RESET_WEB_USEC_MAX 0x01
+extern void global_statistics_copy(struct global_statistics *gs, uint8_t options);
+extern void global_statistics_charts(void);
 
 #endif /* NETDATA_GLOBAL_STATISTICS_H */
diff --git a/src/health.c b/src/health.c
new file mode 100644 (file)
index 0000000..7f444a8
--- /dev/null
@@ -0,0 +1,1603 @@
+#include "common.h"
+
+#define RRDVAR_MAX_LENGTH 1024
+
+static const char *health_default_exec = PLUGINS_DIR "/alarm.sh";
+int health_enabled = 1;
+
+// ----------------------------------------------------------------------------
+// RRDVAR management
+
+static inline int rrdvar_fix_name(char *variable) {
+    int fixed = 0;
+    while(*variable) {
+        if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
+            *variable++ = '_';
+            fixed++;
+        }
+        else
+            variable++;
+    }
+
+    return fixed;
+}
+
+int rrdvar_compare(void* a, void* b) {
+    if(((RRDVAR *)a)->hash < ((RRDVAR *)b)->hash) return -1;
+    else if(((RRDVAR *)a)->hash > ((RRDVAR *)b)->hash) return 1;
+    else return strcmp(((RRDVAR *)a)->name, ((RRDVAR *)b)->name);
+}
+
+static inline RRDVAR *rrdvar_index_add(avl_tree_lock *tree, RRDVAR *rv) {
+    RRDVAR *ret = (RRDVAR *)avl_insert_lock(tree, (avl *)(rv));
+    if(ret != rv)
+        debug(D_VARIABLES, "Request to insert RRDVAR '%s' into index failed. Already exists.", rv->name);
+
+    return ret;
+}
+
+static inline RRDVAR *rrdvar_index_del(avl_tree_lock *tree, RRDVAR *rv) {
+    RRDVAR *ret = (RRDVAR *)avl_remove_lock(tree, (avl *)(rv));
+    if(!ret)
+        error("Request to remove RRDVAR '%s' from index failed. Not Found.", rv->name);
+
+    return ret;
+}
+
+static inline RRDVAR *rrdvar_index_find(avl_tree_lock *tree, const char *name, uint32_t hash) {
+    RRDVAR tmp;
+    tmp.name = (char *)name;
+    tmp.hash = (hash)?hash:simple_hash(tmp.name);
+
+    return (RRDVAR *)avl_search_lock(tree, (avl *)&tmp);
+}
+
+static inline void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv) {
+    (void)host;
+
+    if(!rv) return;
+
+    if(tree)
+        rrdvar_index_del(tree, rv);
+
+    freez(rv->name);
+    freez(rv);
+}
+
+static inline RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, int type, calculated_number *value) {
+    char *variable = strdupz(name);
+    rrdvar_fix_name(variable);
+    uint32_t hash = simple_hash(variable);
+
+    RRDVAR *rv = rrdvar_index_find(tree, variable, hash);
+    if(unlikely(!rv)) {
+        debug(D_VARIABLES, "Variable '%s' not found in scope '%s'. Creating a new one.", variable, scope);
+
+        rv = callocz(1, sizeof(RRDVAR));
+        rv->name = variable;
+        rv->hash = hash;
+        rv->type = type;
+        rv->value = value;
+
+        RRDVAR *ret = rrdvar_index_add(tree, rv);
+        if(unlikely(ret != rv)) {
+            debug(D_VARIABLES, "Variable '%s' in scope '%s' already exists", variable, scope);
+            rrdvar_free(NULL, NULL, rv);
+            rv = NULL;
+        }
+        else
+            debug(D_VARIABLES, "Variable '%s' created in scope '%s'", variable, scope);
+    }
+    else {
+        // already exists
+        freez(variable);
+        rv = NULL;
+    }
+
+    return rv;
+}
+
+// ----------------------------------------------------------------------------
+// RRDVAR lookup
+
+calculated_number rrdvar2number(RRDVAR *rv) {
+    switch(rv->type) {
+        case RRDVAR_TYPE_CALCULATED: {
+            calculated_number *n = (calculated_number *)rv->value;
+            return *n;
+        }
+            break;
+
+        case RRDVAR_TYPE_TIME_T: {
+            time_t *n = (time_t *)rv->value;
+            return *n;
+        }
+            break;
+
+        case RRDVAR_TYPE_COLLECTED: {
+            collected_number *n = (collected_number *)rv->value;
+            return *n;
+        }
+            break;
+
+        case RRDVAR_TYPE_TOTAL: {
+            total_number *n = (total_number *)rv->value;
+            return *n;
+        }
+
+        default:
+            error("I don't know how to convert RRDVAR type %d to calculated_number", rv->type);
+            return NAN;
+            break;
+    }
+}
+
+void dump_variable(void *data) {
+    RRDVAR *rv = (RRDVAR *)data;
+    debug(D_HEALTH, "%50s : %20.5Lf", rv->name, rrdvar2number(rv));
+}
+
+int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result) {
+    RRDSET *st = rc->rrdset;
+    RRDVAR *rv;
+
+    if(!st) return 0;
+
+    rv = rrdvar_index_find(&st->variables_root_index, variable, hash);
+    if(rv) {
+        *result = rrdvar2number(rv);
+        return 1;
+    }
+
+    rv = rrdvar_index_find(&st->rrdcontext->variables_root_index, variable, hash);
+    if(rv) {
+        *result = rrdvar2number(rv);
+        return 1;
+    }
+
+    rv = rrdvar_index_find(&st->rrdhost->variables_root_index, variable, hash);
+    if(rv) {
+        *result = rrdvar2number(rv);
+        return 1;
+    }
+
+    debug(D_HEALTH, "Available local chart '%s' variables:", st->id);
+    avl_traverse_lock(&st->variables_root_index, dump_variable);
+
+    debug(D_HEALTH, "Available context '%s' variables:", st->rrdcontext->id);
+    avl_traverse_lock(&st->rrdcontext->variables_root_index, dump_variable);
+
+    debug(D_HEALTH, "Available host '%s' variables:", st->rrdhost->hostname);
+    avl_traverse_lock(&st->rrdhost->variables_root_index, dump_variable);
+
+    return 0;
+}
+
+// ----------------------------------------------------------------------------
+// RRDSETVAR management
+
+RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options) {
+    debug(D_VARIABLES, "RRDVARSET create for chart id '%s' name '%s' with variable name '%s'", st->id, st->name, variable);
+    RRDSETVAR *rs = (RRDSETVAR *)callocz(1, sizeof(RRDSETVAR));
+
+    char buffer[RRDVAR_MAX_LENGTH + 1];
+    snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->id, variable);
+    rs->fullid = strdupz(buffer);
+
+    snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, variable);
+    rs->fullname = strdupz(buffer);
+
+    rs->variable = strdupz(variable);
+
+    rs->type = type;
+    rs->value = value;
+    rs->options = options;
+    rs->rrdset = st;
+
+    rs->local        = rrdvar_create_and_index("local",   &st->variables_root_index, rs->variable, rs->type, rs->value);
+    rs->context      = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullid, rs->type, rs->value);
+    rs->host         = rrdvar_create_and_index("host",    &st->rrdhost->variables_root_index, rs->fullid, rs->type, rs->value);
+    rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->type, rs->value);
+    rs->host_name    = rrdvar_create_and_index("host",    &st->rrdhost->variables_root_index, rs->fullname, rs->type, rs->value);
+
+    rs->next = st->variables;
+    st->variables = rs;
+
+    return rs;
+}
+
+void rrdsetvar_rename_all(RRDSET *st) {
+    debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", st->id, st->name);
+
+    // only these 2 can change name
+    // rs->context_name
+    // rs->host_name
+
+    char buffer[RRDVAR_MAX_LENGTH + 1];
+    RRDSETVAR *rs, *next = st->variables;
+    while((rs = next)) {
+        next = rs->next;
+
+        snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rs->variable);
+
+        if (strcmp(buffer, rs->fullname)) {
+            // name changed
+            rrdvar_free(st->rrdhost, &st->rrdcontext->variables_root_index, rs->context_name);
+            rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_name);
+
+            freez(rs->fullname);
+            rs->fullname = strdupz(st->name);
+            rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->fullname, rs->type, rs->value);
+            rs->host_name    = rrdvar_create_and_index("host",    &st->rrdhost->variables_root_index, rs->fullname, rs->type, rs->value);
+        }
+    }
+
+    rrdsetcalc_link_matching(st);
+}
+
+void rrdsetvar_free(RRDSETVAR *rs) {
+    RRDSET *st = rs->rrdset;
+    debug(D_VARIABLES, "RRDSETVAR free for chart id '%s' name '%s', variable '%s'", st->id, st->name, rs->variable);
+
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local);
+    rrdvar_free(st->rrdhost, &st->rrdcontext->variables_root_index, rs->context);
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host);
+    rrdvar_free(st->rrdhost, &st->rrdcontext->variables_root_index, rs->context_name);
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_name);
+
+    if(st->variables == rs) {
+        st->variables = rs->next;
+    }
+    else {
+        RRDSETVAR *t;
+        for (t = st->variables; t && t->next != rs; t = t->next);
+        if(!t) error("RRDSETVAR '%s' not found in chart '%s' variables linked list", rs->fullname, st->id);
+        else t->next = rs->next;
+    }
+
+    freez(rs->fullid);
+    freez(rs->fullname);
+    freez(rs->variable);
+    freez(rs);
+}
+
+// ----------------------------------------------------------------------------
+// RRDDIMVAR management
+
+#define RRDDIMVAR_ID_MAX 1024
+
+RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options) {
+    RRDSET *st = rd->rrdset;
+
+    debug(D_VARIABLES, "RRDDIMSET create for chart id '%s' name '%s', dimension id '%s', name '%s%s%s'", st->id, st->name, rd->id, (prefix)?prefix:"", rd->name, (suffix)?suffix:"");
+
+    if(!prefix) prefix = "";
+    if(!suffix) suffix = "";
+
+    char buffer[RRDDIMVAR_ID_MAX + 1];
+    RRDDIMVAR *rs = (RRDDIMVAR *)callocz(1, sizeof(RRDDIMVAR));
+
+    rs->prefix = strdupz(prefix);
+    rs->suffix = strdupz(suffix);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->id, rs->suffix);
+    rs->id = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+    rs->name = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->id);
+    rs->fullidid = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->id, rs->name);
+    rs->fullidname = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->id);
+    rs->fullnameid = strdupz(buffer);
+
+    snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rd->rrdset->name, rs->name);
+    rs->fullnamename = strdupz(buffer);
+
+    rs->type = type;
+    rs->value = value;
+    rs->options = options;
+    rs->rrddim = rd;
+
+    rs->local_id     = rrdvar_create_and_index("local",   &st->variables_root_index, rs->id, rs->type, rs->value);
+    rs->local_name   = rrdvar_create_and_index("local",   &st->variables_root_index, rs->name, rs->type, rs->value);
+
+    rs->context_id   = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->id, rs->type, rs->value);
+    rs->context_name = rrdvar_create_and_index("context", &st->rrdcontext->variables_root_index, rs->name, rs->type, rs->value);
+
+    rs->host_fullidid     = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidid, rs->type, rs->value);
+    rs->host_fullidname   = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullidname, rs->type, rs->value);
+    rs->host_fullnameid   = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnameid, rs->type, rs->value);
+    rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index, rs->fullnamename, rs->type, rs->value);
+
+    rs->next = rd->variables;
+    rd->variables = rs;
+
+    return rs;
+}
+
+void rrddimvar_rename_all(RRDDIM *rd) {
+    RRDSET *st = rd->rrdset;
+    debug(D_VARIABLES, "RRDDIMSET rename for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+
+    RRDDIMVAR *rs, *next = rd->variables;
+    while((rs = next)) {
+        next = rs->next;
+
+        if (strcmp(rd->name, rs->name)) {
+            char buffer[RRDDIMVAR_ID_MAX + 1];
+            // name changed
+
+            // name
+            rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_name);
+            freez(rs->name);
+            snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", rs->prefix, rd->name, rs->suffix);
+            rs->name = strdupz(buffer);
+            rs->local_name = rrdvar_create_and_index("local", &st->variables_root_index, rs->name, rs->type, rs->value);
+
+            rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidname);
+            freez(rs->fullidname);
+            snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->id, rs->name);
+            rs->fullidname = strdupz(buffer);
+            rs->host_fullidname = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+                                                             rs->fullidname, rs->type, rs->value);
+
+            // fullnameid
+            rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnameid);
+            freez(rs->fullnameid);
+            snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->id);
+            rs->fullnameid = strdupz(buffer);
+            rs->host_fullnameid = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+                                                          rs->fullnameid, rs->type, rs->value);
+
+            // fullnamename
+            rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnamename);
+            freez(rs->fullnamename);
+            snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", st->name, rs->name);
+            rs->fullnamename = strdupz(buffer);
+            rs->host_fullnamename = rrdvar_create_and_index("host", &st->rrdhost->variables_root_index,
+                                                          rs->fullnamename, rs->type, rs->value);
+        }
+    }
+}
+
+void rrddimvar_free(RRDDIMVAR *rs) {
+    RRDDIM *rd = rs->rrddim;
+    RRDSET *st = rd->rrdset;
+    debug(D_VARIABLES, "RRDDIMSET free for chart id '%s' name '%s', dimension id '%s', name '%s', prefix='%s', suffix='%s'", st->id, st->name, rd->id, rd->name, rs->prefix, rs->suffix);
+
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_id);
+    rrdvar_free(st->rrdhost, &st->variables_root_index, rs->local_name);
+
+    rrdvar_free(st->rrdhost, &st->rrdcontext->variables_root_index, rs->context_id);
+    rrdvar_free(st->rrdhost, &st->rrdcontext->variables_root_index, rs->context_name);
+
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidid);
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullidname);
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnameid);
+    rrdvar_free(st->rrdhost, &st->rrdhost->variables_root_index, rs->host_fullnamename);
+
+    if(rd->variables == rs) {
+        debug(D_VARIABLES, "RRDDIMSET removing first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+        rd->variables = rs->next;
+    }
+    else {
+        debug(D_VARIABLES, "RRDDIMSET removing non-first entry for chart id '%s' name '%s', dimension id '%s', name '%s'", st->id, st->name, rd->id, rd->name);
+        RRDDIMVAR *t;
+        for (t = rd->variables; t && t->next != rs; t = t->next) ;
+        if(!t) error("RRDDIMVAR '%s' not found in dimension '%s/%s' variables linked list", rs->name, st->id, rd->id);
+        else t->next = rs->next;
+    }
+
+    freez(rs->prefix);
+    freez(rs->suffix);
+    freez(rs->id);
+    freez(rs->name);
+    freez(rs->fullidid);
+    freez(rs->fullidname);
+    freez(rs->fullnameid);
+    freez(rs->fullnamename);
+    freez(rs);
+}
+
+// ----------------------------------------------------------------------------
+// RRDCALC management
+
+static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
+    debug(D_HEALTH, "Health linking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, st->rrdhost->hostname);
+
+    rc->rrdset = st;
+
+    if(rc->green && !st->green)
+        st->green = rc->green;
+
+    if(rc->red && !st->red)
+        st->red = rc->red;
+
+    rc->local    = rrdvar_create_and_index("local", &st->variables_root_index, rc->name, RRDVAR_TYPE_CALCULATED, &rc->value);
+    rc->context  = rrdvar_create_and_index("context", &st->rrdcontext->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);
+}
+
+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->calculations; rc ; rc = rc->next) {
+        if(rc->rrdset) continue;
+
+        if(rrdcalc_is_matching_this_rrdset(rc, st))
+            rrdsetcalc_link(st, rc);
+    }
+}
+
+// this has to be called while the RRDHOST is locked
+inline void rrdsetcalc_unlink(RRDCALC *rc) {
+    RRDSET *st = rc->rrdset;
+
+    if(!st) {
+        error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
+        return;
+    }
+
+    RRDHOST *host = st->rrdhost;
+
+    debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
+
+    // unlink it
+    if(rc->rrdset_prev)
+        rc->rrdset_prev->rrdset_next = rc->rrdset_next;
+
+    if(rc->rrdset_next)
+        rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
+
+    if(st->calculations == rc)
+        st->calculations = 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->rrdcontext->variables_root_index, rc->context);
+    rc->context = 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
+}
+
+static inline int rrdcalc_exists(RRDHOST *host, const char *name, uint32_t hash) {
+    RRDCALC *rc;
+
+    // make sure it does not already exist
+    for(rc = host->calculations; rc ; rc = rc->next) {
+        if (rc->hash == hash && !strcmp(name, rc->name)) {
+            error("Health alarm '%s' already exists in host '%s'.", name, host->hostname);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static inline void rrdcalc_create_part2(RRDHOST *host, RRDCALC *rc) {
+    rrdhost_check_rdlock(host);
+
+    if(rc->calculation) {
+        rc->calculation->this = &rc->value;
+        rc->calculation->rrdcalc = rc;
+    }
+
+    if(rc->warning) {
+        rc->warning->this = &rc->value;
+        rc->warning->rrdcalc = rc;
+    }
+
+    if(rc->critical) {
+        rc->critical->this = &rc->value;
+        rc->critical->rrdcalc = rc;
+    }
+
+    // link it to the host
+    rc->next = host->calculations;
+    host->calculations = rc;
+
+    // link it to its chart
+    RRDSET *st;
+    for(st = host->rrdset_root; st ; st = st->next) {
+        if(rrdcalc_is_matching_this_rrdset(rc, st)) {
+            rrdsetcalc_link(st, rc);
+            break;
+        }
+    }
+}
+
+static inline uint32_t rrdcalc_fullname(char *fullname, size_t len, const char *chart, const char *name) {
+    snprintfz(fullname, len - 1, "%s%s%s", chart?chart:"", chart?".":"", name);
+    rrdvar_fix_name(fullname);
+    return simple_hash(fullname);
+}
+
+static inline RRDCALC *rrdcalc_create(RRDHOST *host, const char *name, const char *chart, const char *dimensions, int group_method,
+                        int after, int before, int update_every, uint32_t options,
+                        calculated_number green, calculated_number red,
+                        const char *exec, const char *source,
+                        const char *calc, const char *warn, const char *crit) {
+
+    char fullname[RRDVAR_MAX_LENGTH + 1];
+    uint32_t hash = rrdcalc_fullname(fullname, RRDVAR_MAX_LENGTH + 1, chart, name);
+
+    if(rrdcalc_exists(host, fullname, hash))
+        return NULL;
+
+    RRDCALC *rc = callocz(1, sizeof(RRDCALC));
+
+    rc->name = strdupz(name);
+    rc->hash = simple_hash(rc->name);
+
+    rc->chart = strdupz(chart);
+    rc->hash_chart = simple_hash(rc->chart);
+
+    if(dimensions) rc->dimensions = strdupz(dimensions);
+
+    rc->group = group_method;
+    rc->after = after;
+    rc->before = before;
+    rc->update_every = update_every;
+    rc->options = options;
+
+    rc->green = green;
+    rc->red = red;
+
+    if(exec) rc->exec = strdupz(exec);
+    if(source) rc->source = strdupz(source);
+
+    if(calc) {
+        rc->calculation = expression_parse(calc, NULL, NULL);
+        if(!rc->calculation)
+            error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, name, calc);
+    }
+    if(warn) {
+        rc->warning = expression_parse(warn, NULL, NULL);
+        if(!rc->warning)
+            error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, name, warn);
+    }
+    if(crit) {
+        rc->critical = expression_parse(crit, NULL, NULL);
+        if(!rc->critical)
+            error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, name, crit);
+    }
+
+    debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s",
+          (rc->chart)?rc->chart:"NOCHART",
+          rc->name,
+          (rc->exec)?rc->exec:"DEFAULT",
+          rc->green,
+          rc->red,
+          rc->group,
+          rc->after,
+          rc->before,
+          rc->options,
+          (rc->dimensions)?rc->dimensions:"NONE",
+          rc->update_every,
+          (rc->calculation)?rc->calculation->parsed_as:"NONE",
+          (rc->warning)?rc->warning->parsed_as:"NONE",
+          (rc->critical)?rc->critical->parsed_as:"NONE",
+          rc->source
+    );
+
+    rrdcalc_create_part2(host, rc);
+    return rc;
+}
+
+void rrdcalc_free(RRDHOST *host, RRDCALC *rc) {
+    if(!rc) return;
+
+    debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+
+    // unlink it from RRDSET
+    if(rc->rrdset) rrdsetcalc_unlink(rc);
+
+    // unlink it from RRDHOST
+    if(rc == host->calculations)
+        host->calculations = rc->next;
+
+    else if(host->calculations) {
+        RRDCALC *t, *last = host->calculations;
+
+        for(t = last->next; t && t != rc; last = t, t = t->next) ;
+        if(last && last->next == rc)
+            last->next = rc->next;
+        else
+            error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+    }
+    else
+        error("Cannot unlink unlink '%s.%s' from host '%s': This host does not have any calculations", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
+
+    if(rc->warning) expression_free(rc->warning);
+    if(rc->critical) expression_free(rc->critical);
+
+    freez(rc->source);
+    freez(rc->name);
+    freez(rc->chart);
+    freez(rc->dimensions);
+    freez(rc->exec);
+
+    freez(rc);
+}
+
+// ----------------------------------------------------------------------------
+// RRDCALCTEMPLATE management
+
+void rrdcalctemplate_link_matching(RRDSET *st) {
+    RRDCALCTEMPLATE *rt;
+
+    for(rt = st->rrdhost->templates; rt ; rt = rt->next) {
+        if(rt->hash_context == st->hash_context && !strcmp(rt->context, st->context)) {
+
+            RRDCALC *rc = rrdcalc_create(st->rrdhost, rt->name, st->id,
+                           rt->dimensions, rt->group, rt->after, rt->before, rt->update_every, rt->options,
+                           rt->green, rt->red, rt->exec, rt->source,
+                           (rt->calculation)?rt->calculation->source:NULL,
+                           (rt->warning)?rt->warning->source:NULL,
+                           (rt->critical)?rt->critical->source:NULL);
+
+            if(!rc)
+                error("Health tried to create alarm from template '%s', but it failed", rt->name);
+
+#ifdef NETDATA_INTERNAL_CHECKS
+            else if(rc->rrdset != st)
+                error("Health alarm '%s.%s' should be linked to chart '%s', but it is not", rc->chart?rc->chart:"NOCHART", rc->name, st->id);
+#else
+            (void)rc;
+#endif
+        }
+    }
+}
+
+static inline void rrdcalctemplate_free(RRDHOST *host, RRDCALCTEMPLATE *rt) {
+    debug(D_HEALTH, "Health removing template '%s' of host '%s'", rt->name, host->hostname);
+
+    if(host->templates) {
+        if(host->templates == rt) {
+            host->templates = rt->next;
+        }
+        else {
+            RRDCALCTEMPLATE *t, *last = host->templates;
+            for (t = last->next; t && t != rt; last = t, t = t->next ) ;
+            if(last && last->next == rt) {
+                last->next = rt->next;
+                rt->next = NULL;
+            }
+            else
+                error("Cannot find RRDCALCTEMPLATE '%s' linked in host '%s'", rt->name, host->hostname);
+        }
+    }
+
+    if(rt->warning) expression_free(rt->warning);
+    if(rt->critical) expression_free(rt->critical);
+
+    freez(rt->dimensions);
+    freez(rt->context);
+    freez(rt->name);
+    freez(rt->exec);
+    freez(rt->source);
+    freez(rt);
+}
+
+// ----------------------------------------------------------------------------
+// load health configuration
+
+#define HEALTH_CONF_MAX_LINE 4096
+
+#define HEALTH_ALARM_KEY "alarm"
+#define HEALTH_TEMPLATE_KEY "template"
+#define HEALTH_ON_KEY "on"
+#define HEALTH_LOOKUP_KEY "lookup"
+#define HEALTH_CALC_KEY "calc"
+#define HEALTH_EVERY_KEY "every"
+#define HEALTH_GREEN_KEY "green"
+#define HEALTH_RED_KEY "red"
+#define HEALTH_WARN_KEY "warn"
+#define HEALTH_CRIT_KEY "crit"
+#define HEALTH_EXEC_KEY "exec"
+
+static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
+    {
+        char fullname[RRDVAR_MAX_LENGTH + 1];
+        uint32_t hash = rrdcalc_fullname(fullname, RRDVAR_MAX_LENGTH + 1, rc->chart, rc->name);
+
+        if (rrdcalc_exists(host, fullname, hash))
+            return 0;
+    }
+
+    if(!rc->chart) {
+        error("Health configuration for alarm '%s' does not have a chart", rc->name);
+        return 0;
+    }
+
+    if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->warning && !rc->critical) {
+        error("Health configuration for alarm '%s.%s' is useless (no calculation, no warning and no critical evaluation)", rc->chart?rc->chart:"NOCHART", rc->name);
+        return 0;
+    }
+
+    debug(D_HEALTH, "Health configuration adding alarm '%s.%s': exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s",
+          rc->chart?rc->chart:"NOCHART",
+          rc->name,
+          (rc->exec)?rc->exec:"DEFAULT",
+          rc->green,
+          rc->red,
+          rc->group,
+          rc->after,
+          rc->before,
+          rc->options,
+          (rc->dimensions)?rc->dimensions:"NONE",
+          rc->update_every,
+          (rc->calculation)?rc->calculation->parsed_as:"NONE",
+          (rc->warning)?rc->warning->parsed_as:"NONE",
+          (rc->critical)?rc->critical->parsed_as:"NONE",
+          rc->source
+    );
+
+    rrdcalc_create_part2(host, rc);
+    return 1;
+}
+
+static inline int rrdcalctemplate_add(RRDHOST *host, RRDCALCTEMPLATE *rt) {
+    if(!rt->context) {
+        error("Health configuration for template '%s' does not have a context", rt->name);
+        return 0;
+    }
+
+    if(!RRDCALCTEMPLATE_HAS_CALCULATION(rt) && !rt->warning && !rt->critical) {
+        error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rt->name);
+        return 0;
+    }
+
+    RRDCALCTEMPLATE *t;
+    for (t = host->templates; t ; t = t->next) {
+        if(t->hash_name == rt->hash_name && !strcmp(t->name, rt->name)) {
+            error("Health configuration template '%s' already exists for host '%s'.", rt->name, host->hostname);
+            return 0;
+        }
+    }
+
+    debug(D_HEALTH, "Health configuration adding template '%s': context '%s', exec '%s', green %Lf, red %Lf, lookup: group %d, after %d, before %d, options %u, dimensions '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s'",
+          rt->name,
+          (rt->context)?rt->context:"NONE",
+          (rt->exec)?rt->exec:"DEFAULT",
+          rt->green,
+          rt->red,
+          rt->group,
+          rt->after,
+          rt->before,
+          rt->options,
+          (rt->dimensions)?rt->dimensions:"NONE",
+          rt->update_every,
+          (rt->calculation)?rt->calculation->parsed_as:"NONE",
+          (rt->warning)?rt->warning->parsed_as:"NONE",
+          (rt->critical)?rt->critical->parsed_as:"NONE",
+          rt->source
+    );
+
+    rt->next = host->templates;
+    host->templates = rt;
+    return 1;
+}
+
+static inline int health_parse_time(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_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_time(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_time(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_time(value, every)) {
+                error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
+                      line, path, file, value, key);
+            }
+        }
+        else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
+            *options |= RRDR_OPTION_ABSOLUTE;
+        }
+        else if(!strcasecmp(key, "min2max")) {
+            *options |= RRDR_OPTION_MIN2MAX;
+        }
+        else if(!strcasecmp(key, "null2zero")) {
+            *options |= RRDR_OPTION_NULL2ZERO;
+        }
+        else if(!strcasecmp(key, "percentage")) {
+            *options |= RRDR_OPTION_PERCENTAGE;
+        }
+        else if(!strcasecmp(key, "unaligned")) {
+            *options |= RRDR_OPTION_NOT_ALIGNED;
+        }
+        else if(!strcasecmp(key, "of")) {
+            if(*s && strcasecmp(s, "all"))
+               *dimensions = strdupz(s);
+            break;
+        }
+        else {
+            error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
+                  line, path, file, key);
+        }
+    }
+
+    return 1;
+}
+
+static inline char *health_source_file(int line, const char *path, const char *filename) {
+    char buffer[FILENAME_MAX + 1];
+    snprintfz(buffer, FILENAME_MAX, "%d@%s/%s", line, path, filename);
+    return strdupz(buffer);
+}
+
+int health_readfile(const char *path, const char *filename) {
+    debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename);
+
+    static uint32_t hash_alarm = 0, hash_template = 0, hash_on = 0, hash_calc = 0, hash_green = 0, hash_red = 0, hash_warn = 0, hash_crit = 0, hash_exec = 0, hash_every = 0, hash_lookup = 0;
+    char buffer[HEALTH_CONF_MAX_LINE + 1];
+
+    if(unlikely(!hash_alarm)) {
+        hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
+        hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
+        hash_on = simple_uhash(HEALTH_ON_KEY);
+        hash_calc = simple_uhash(HEALTH_CALC_KEY);
+        hash_lookup = simple_uhash(HEALTH_LOOKUP_KEY);
+        hash_green = simple_uhash(HEALTH_GREEN_KEY);
+        hash_red = simple_uhash(HEALTH_RED_KEY);
+        hash_warn = simple_uhash(HEALTH_WARN_KEY);
+        hash_crit = simple_uhash(HEALTH_CRIT_KEY);
+        hash_exec = simple_uhash(HEALTH_EXEC_KEY);
+        hash_every = simple_uhash(HEALTH_EVERY_KEY);
+    }
+
+    snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
+    FILE *fp = fopen(buffer, "r");
+    if(!fp) {
+        error("Health configuration cannot read file '%s'.", buffer);
+        return 0;
+    }
+
+    RRDCALC *rc = NULL;
+    RRDCALCTEMPLATE *rt = NULL;
+
+    size_t line = 0, append = 0;
+    char *s;
+    while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
+        int stop_appending = !s;
+        line++;
+        // info("Line %zu of file '%s/%s': '%s'", line, path, filename, s);
+        s = trim(buffer);
+        if(!s) continue;
+        // info("Trimmed line %zu of file '%s/%s': '%s'", line, path, filename, s);
+
+        append = strlen(s);
+        if(!stop_appending && s[append - 1] == '\\') {
+            s[append - 1] = ' ';
+            append = &s[append] - buffer;
+            if(append < HEALTH_CONF_MAX_LINE)
+                continue;
+            continue;
+        }
+        append = 0;
+
+        char *key = s;
+        while(*s && *s != ':') s++;
+        if(!*s) {
+            error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
+            continue;
+        }
+        *s = '\0';
+        s++;
+
+        char *value = s;
+        key = trim(key);
+        value = trim(value);
+
+        if(!key) {
+            error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
+            continue;
+        }
+
+        if(!value) {
+            error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
+            continue;
+        }
+
+        // info("Health file '%s/%s', key '%s', value '%s'", path, filename, key, value);
+        uint32_t hash = simple_uhash(key);
+
+        if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
+            if(rc && !rrdcalc_add_alarm_from_config(&localhost, rc))
+                rrdcalc_free(&localhost, rc);
+
+            if(rt) {
+                if (!rrdcalctemplate_add(&localhost, rt))
+                    rrdcalctemplate_free(&localhost, rt);
+                rt = NULL;
+            }
+
+            rc = callocz(1, sizeof(RRDCALC));
+            rc->name = strdupz(value);
+            rc->hash = simple_hash(rc->name);
+            rc->source = health_source_file(line, path, filename);
+
+            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(&localhost, rt))
+                rrdcalctemplate_free(&localhost, rt);
+
+            rt = callocz(1, sizeof(RRDCALCTEMPLATE));
+            rt->name = strdupz(value);
+            rt->hash_name = simple_hash(rt->name);
+            rt->source = health_source_file(line, path, filename);
+
+            if(rrdvar_fix_name(rt->name))
+                error("Health configuration renamed template '%s' to '%s'", value, rt->name);
+        }
+        else if(rc) {
+            if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
+                if(rc->chart) {
+                    if(strcmp(rc->chart, value))
+                        info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+                             line, path, filename, rc->name, key, rc->chart, value, value);
+
+                    freez(rc->chart);
+                }
+                rc->chart = strdupz(value);
+                rc->hash_chart = simple_hash(rc->chart);
+            }
+            else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
+                health_parse_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_time(value, &rc->update_every))
+                    info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
+                         line, path, filename, rc->name, key, value);
+            }
+            else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
+                char *e;
+                rc->green = strtold(value, &e);
+                if(e && *e) {
+                    info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+                         line, path, filename, rc->name, key, e);
+                }
+            }
+            else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
+                char *e;
+                rc->red = strtold(value, &e);
+                if(e && *e) {
+                    info("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
+                         line, path, filename, rc->name, key, e);
+                }
+            }
+            else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rc->calculation = expression_parse(value, &failed_at, &error);
+                if(!rc->calculation) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                          line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rc->warning = expression_parse(value, &failed_at, &error);
+                if(!rc->warning) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                          line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rc->critical = expression_parse(value, &failed_at, &error);
+                if(!rc->critical) {
+                    error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                          line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
+                if(rc->exec) {
+                    if(strcmp(rc->exec, value))
+                        info("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+                             line, path, filename, rc->name, key, rc->exec, value, value);
+
+                    freez(rc->exec);
+                }
+                rc->exec = strdupz(value);
+            }
+            else {
+                error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
+                     line, path, filename, rc->name, key);
+            }
+        }
+        else if(rt) {
+            if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
+                if(rt->context) {
+                    if(strcmp(rt->context, value))
+                        info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+                             line, path, filename, rt->name, key, rt->context, value, value);
+
+                    freez(rt->context);
+                }
+                rt->context = strdupz(value);
+                rt->hash_context = simple_hash(rt->context);
+            }
+            else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
+                health_parse_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_time(value, &rt->update_every))
+                    info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
+                         line, path, filename, rt->name, key, value);
+            }
+            else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
+                char *e;
+                rt->green = strtold(value, &e);
+                if(e && *e) {
+                    info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+                         line, path, filename, rt->name, key, e);
+                }
+            }
+            else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
+                char *e;
+                rt->red = strtold(value, &e);
+                if(e && *e) {
+                    info("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
+                         line, path, filename, rt->name, key, e);
+                }
+            }
+            else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rt->calculation = expression_parse(value, &failed_at, &error);
+                if(!rt->calculation) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                          line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rt->warning = expression_parse(value, &failed_at, &error);
+                if(!rt->warning) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                          line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
+                const char *failed_at = NULL;
+                int error = 0;
+                rt->critical = expression_parse(value, &failed_at, &error);
+                if(!rt->critical) {
+                    error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
+                          line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
+                }
+            }
+            else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
+                if(rt->exec) {
+                    if(strcmp(rt->exec, value))
+                        info("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
+                             line, path, filename, rt->name, key, rt->exec, value, value);
+
+                    freez(rt->exec);
+                }
+                rt->exec = strdupz(value);
+            }
+            else {
+                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(&localhost, rt))
+        rrdcalctemplate_free(&localhost, rt);
+
+    fclose(fp);
+    return 1;
+}
+
+void health_readdir(const char *path) {
+    size_t pathlen = strlen(path);
+
+    debug(D_HEALTH, "Health configuration reading directory '%s'", path);
+
+    DIR *dir = opendir(path);
+    if (!dir) {
+        error("Health configuration cannot open directory '%s'.", path);
+        return;
+    }
+
+    struct dirent *de = NULL;
+    while ((de = readdir(dir))) {
+        size_t len = strlen(de->d_name);
+
+        if(de->d_type == DT_DIR
+           && (
+                   (de->d_name[0] == '.' && de->d_name[1] == '\0')
+                   || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+           ))
+            continue;
+
+        else if(de->d_type == DT_DIR) {
+            char *s = mallocz(pathlen + strlen(de->d_name) + 2);
+            strcpy(s, path);
+            strcat(s, "/");
+            strcat(s, de->d_name);
+            health_readdir(s);
+            freez(s);
+            continue;
+        }
+
+        else if((de->d_type == DT_LNK || de->d_type == DT_REG) &&
+                len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
+            health_readfile(path, de->d_name);
+        }
+    }
+
+    closedir(dir);
+}
+
+void health_init(void) {
+    debug(D_HEALTH, "Health configuration initializing");
+
+    char *path;
+
+    if(!(health_enabled = config_get_boolean("health", "enabled", 1))) {
+        debug(D_HEALTH, "Health is disabled.");
+        return;
+    }
+
+    {
+        char buffer[FILENAME_MAX + 1];
+        snprintfz(buffer, FILENAME_MAX, "%s/health.d", config_get("global", "config directory", CONFIG_DIR));
+        path = config_get("health", "health configuration directory", buffer);
+
+        snprintfz(buffer, FILENAME_MAX, "%s/alarm.sh", config_get("global", "plugins directory", PLUGINS_DIR));
+        health_default_exec = config_get("health", "script to execute on alarm", buffer);
+    }
+
+    rrdhost_rwlock(&localhost);
+    health_readdir(path);
+    rrdhost_unlock(&localhost);
+}
+
+static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
+    if (unlikely(!rc->rrdset)) {
+        debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rc->chart?rc->chart:"NOCHART", rc->name);
+        return 0;
+    }
+
+    if (unlikely(!rc->update_every)) {
+        debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rc->chart?rc->chart:"NOCHART", rc->name);
+        return 0;
+    }
+
+    if (unlikely(rc->next_update > now)) {
+        if (*next_run > rc->next_update)
+            *next_run = rc->next_update;
+
+        debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rc->chart?rc->chart:"NOCHART", rc->name, (int) (rc->next_update - now));
+        return 0;
+    }
+
+    return 1;
+}
+
+static inline int rrdcalc_value2status(calculated_number n) {
+    if(isnan(n)) return RRDCALC_STATUS_UNDEFINED;
+    if(n) return RRDCALC_STATUS_RAISED;
+    return RRDCALC_STATUS_OFF;
+}
+
+static inline const char *rrdcalc_status2string(int status) {
+    switch(status) {
+        case RRDCALC_STATUS_UNINITIALIZED:
+            return "UNINITIALIZED";
+
+        case RRDCALC_STATUS_UNDEFINED:
+            return "UNDEFINED";
+
+        case RRDCALC_STATUS_RAISED:
+            return "RAISED";
+
+        case RRDCALC_STATUS_OFF:
+            return "OFF";
+
+        default:
+            return "UNKNOWN";
+    }
+}
+
+void rrdcalc_check_critical_event(RRDCALC *rc) {
+    calculated_number n = rc->critical->result;
+
+    int old_status = rc->critical_status;
+    int new_status = rrdcalc_value2status(n);
+
+    if(new_status != old_status) {
+        info("Health alarm '%s.%s' - CRITICAL condition changed status from %s to %s",
+             rc->chart?rc->chart:"NOCHART", rc->name,
+             rrdcalc_status2string(old_status),
+             rrdcalc_status2string(new_status)
+        );
+
+        rc->critical_status = new_status;
+    }
+}
+
+void rrdcalc_check_warning_event(RRDCALC *rc) {
+    calculated_number n = rc->warning->result;
+
+    int old_status = rc->warning_status;
+    int new_status = rrdcalc_value2status(n);
+
+    if(new_status != old_status) {
+        info("Health alarm '%s.%s' - WARNING condition changed status from %s to %s",
+             rc->chart?rc->chart:"NOCHART", rc->name,
+             rrdcalc_status2string(old_status),
+             rrdcalc_status2string(new_status)
+        );
+
+        rc->warning_status = new_status;
+    }
+}
+
+void *health_main(void *ptr) {
+    (void)ptr;
+
+    info("HEALTH thread created with task id %d", gettid());
+
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
+
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
+
+    int min_run_every = (int)config_get_number("health", "run at least every seconds", 10);
+    if(min_run_every < 1) min_run_every = 1;
+
+    BUFFER *wb = buffer_create(100);
+
+    unsigned int loop = 0;
+    while(health_enabled) {
+        loop++;
+        debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
+
+        int oldstate, runnable = 0;
+        time_t now = time(NULL);
+        time_t next_run = now + min_run_every;
+        RRDCALC *rc;
+
+        if (unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
+            error("Cannot set pthread cancel state to DISABLE.");
+
+        rrdhost_rdlock(&localhost);
+
+        // the first loop is to lookup values from the db
+        for (rc = localhost.calculations; rc; rc = rc->next) {
+            if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run)))
+                continue;
+
+            runnable++;
+
+            // 1. if there is database lookup, do it
+            // 2. if there is calculation expression, run it
+
+            if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) {
+                time_t old_db_timestamp = rc->db_timestamp;
+                int value_is_null = 0;
+
+                int ret = rrd2value(rc->rrdset, wb, &rc->value,
+                                    rc->dimensions, 1, rc->after, rc->before, rc->group,
+                                    rc->options, &rc->db_timestamp, &value_is_null);
+
+                if (unlikely(ret != 200)) {
+                    // database lookup failed
+                    rc->value = NAN;
+
+                    if (unlikely(!(rc->rrdcalc_options & RRDCALC_OPTION_DB_ERROR))) {
+                        rc->rrdcalc_options |= RRDCALC_OPTION_DB_ERROR;
+                        error("Health alarm '%s.%s': database lookup returned error %d", rc->chart?rc->chart:"NOCHART", rc->name, ret);
+                    }
+                }
+                else if (unlikely(rc->rrdcalc_options & RRDCALC_OPTION_DB_ERROR))
+                    rc->rrdcalc_options &= ~RRDCALC_OPTION_DB_ERROR;
+
+                if (unlikely(old_db_timestamp == rc->db_timestamp)) {
+                    // database is stale
+
+                    if (unlikely(!(rc->rrdcalc_options & RRDCALC_OPTION_DB_STALE))) {
+                        rc->rrdcalc_options |= RRDCALC_OPTION_DB_STALE;
+                        error("Health alarm '%s.%s': database is stale", rc->chart?rc->chart:"NOCHART", rc->name);
+                    }
+                }
+                else if (unlikely(rc->rrdcalc_options & RRDCALC_OPTION_DB_STALE))
+                    rc->rrdcalc_options &= ~RRDCALC_OPTION_DB_STALE;
+
+                if (unlikely(value_is_null)) {
+                    // collected value is null
+
+                    rc->value = NAN;
+
+                    if (unlikely(!(rc->rrdcalc_options & RRDCALC_OPTION_DB_NAN))) {
+                        rc->rrdcalc_options |= RRDCALC_OPTION_DB_NAN;
+                        error("Health alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
+                              rc->chart?rc->chart:"NOCHART", rc->name);
+                    }
+                }
+                else if (unlikely(rc->rrdcalc_options & RRDCALC_OPTION_DB_NAN))
+                    rc->rrdcalc_options &= ~RRDCALC_OPTION_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;
+
+                    if (unlikely(!(rc->rrdcalc_options & RRDCALC_OPTION_CALC_ERROR))) {
+                        rc->rrdcalc_options |= RRDCALC_OPTION_CALC_ERROR;
+                        error("Health alarm '%s.%s': failed to evaluate calculation with error: %s",
+                              rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->calculation->error_msg));
+                    }
+                }
+                else {
+                    if (unlikely(rc->rrdcalc_options & RRDCALC_OPTION_CALC_ERROR))
+                        rc->rrdcalc_options &= ~RRDCALC_OPTION_CALC_ERROR;
+
+                    debug(D_HEALTH, "Health alarm '%s.%s': calculation expression gave value "
+                            CALCULATED_NUMBER_FORMAT
+                            ": %s (source: %s)",
+                          rc->chart?rc->chart:"NOCHART", rc->name,
+                          rc->calculation->result,
+                          buffer_tostring(rc->calculation->error_msg),
+                          rc->source
+                    );
+
+                    rc->value = rc->calculation->result;
+                }
+            }
+        }
+        rrdhost_unlock(&localhost);
+
+        if (runnable) {
+            rrdhost_rdlock(&localhost);
+
+            for (rc = localhost.calculations; rc; rc = rc->next) {
+                if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run)))
+                    continue;
+
+                if(unlikely(rc->warning)) {
+                    if(unlikely(!expression_evaluate(rc->warning))) {
+                        // calculation failed
+                        if (unlikely(!(rc->rrdcalc_options & RRDCALC_OPTION_WARN_ERROR))) {
+                            rc->rrdcalc_options |= RRDCALC_OPTION_WARN_ERROR;
+                            error("Health alarm '%s.%s': warning expression failed with error: %s",
+                                  rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->warning->error_msg));
+                        }
+                    }
+                    else {
+                        if(unlikely(rc->rrdcalc_options & RRDCALC_OPTION_WARN_ERROR))
+                            rc->rrdcalc_options &= ~RRDCALC_OPTION_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
+                        );
+                    }
+
+                    rrdcalc_check_warning_event(rc);
+                }
+
+                if(unlikely(rc->critical)) {
+                    if(unlikely(!expression_evaluate(rc->critical))) {
+                        // calculation failed
+                        if (unlikely(!(rc->rrdcalc_options & RRDCALC_OPTION_CRIT_ERROR))) {
+                            rc->rrdcalc_options |= RRDCALC_OPTION_CRIT_ERROR;
+                            error("Health alarm '%s.%s': critical expression failed with error: %s",
+                                  rc->chart?rc->chart:"NOCHART", rc->name, buffer_tostring(rc->critical->error_msg));
+                        }
+                    }
+                    else {
+                        if(unlikely(rc->rrdcalc_options & RRDCALC_OPTION_CRIT_ERROR))
+                            rc->rrdcalc_options &= ~RRDCALC_OPTION_CRIT_ERROR;
+
+                        debug(D_HEALTH, "Health alarm '%s.%s': critical expression gave value "
+                                CALCULATED_NUMBER_FORMAT
+                                ": %s (source: %s)",
+                              rc->chart?rc->chart:"NOCHART", rc->name,
+                              rc->critical->result,
+                              buffer_tostring(rc->critical->error_msg),
+                              rc->source
+                        );
+                    }
+
+                    rrdcalc_check_critical_event(rc);
+                }
+
+                rc->last_updated = now;
+                rc->next_update = now + rc->update_every;
+
+                if (next_run > rc->next_update)
+                    next_run = rc->next_update;
+            }
+
+            rrdhost_unlock(&localhost);
+        }
+
+
+        if (unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
+            error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+
+        debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs",
+              loop, (int) (next_run - now));
+
+        sleep_usec(1000000 * (unsigned long long) (next_run - now));
+    }
+
+    buffer_free(wb);
+
+    info("HEALTH thread exiting");
+    pthread_exit(NULL);
+    return NULL;
+}
diff --git a/src/health.h b/src/health.h
new file mode 100644 (file)
index 0000000..50cd8b1
--- /dev/null
@@ -0,0 +1,224 @@
+#ifndef NETDATA_HEALTH_H
+#define NETDATA_HEALTH_H
+
+extern int health_enabled;
+
+extern int rrdvar_compare(void *a, void *b);
+
+#define RRDVAR_TYPE_CALCULATED 1
+#define RRDVAR_TYPE_TIME_T     2
+#define RRDVAR_TYPE_COLLECTED  3
+#define RRDVAR_TYPE_TOTAL      4
+
+// the variables as stored in the variables indexes
+// there are 3 indexes:
+// 1. at each chart   (RRDSET.variables_root_index)
+// 2. at each context (RRDCONTEXT.variables_root_index)
+// 3. at each host    (RRDHOST.variables_root_index)
+typedef struct rrdvar {
+    avl avl;
+
+    char *name;
+    uint32_t hash;
+
+    int type;
+    void *value;
+
+    time_t last_updated;
+} RRDVAR;
+
+// variables linked to charts
+// We link variables to point to the values that are already
+// calculated / processed by the normal data collection process
+// This means, there will be no speed penalty for using
+// these variables
+typedef struct rrdsetvar {
+    char *fullid;               // chart type.chart id.variable
+    char *fullname;             // chart type.chart name.variable
+    char *variable;             // variable
+
+    int type;
+    void *value;
+
+    uint32_t options;
+
+    RRDVAR *local;
+    RRDVAR *context;
+    RRDVAR *host;
+    RRDVAR *context_name;
+    RRDVAR *host_name;
+
+    struct rrdset *rrdset;
+
+    struct rrdsetvar *next;
+} RRDSETVAR;
+
+
+// variables linked to individual dimensions
+// We link variables to point the values that are already
+// calculated / processed by the normal data collection process
+// This means, there will be no speed penalty for using
+// these variables
+typedef struct rrddimvar {
+    char *prefix;
+    char *suffix;
+
+    char *id;                   // dimension id
+    char *name;                 // dimension name
+    char *fullidid;             // chart type.chart id.dimension id
+    char *fullidname;           // chart type.chart id.dimension name
+    char *fullnameid;           // chart type.chart name.dimension id
+    char *fullnamename;         // chart type.chart name.dimension name
+
+    int type;
+    void *value;
+
+    uint32_t options;
+
+    RRDVAR *local_id;
+    RRDVAR *local_name;
+
+    RRDVAR *context_id;
+    RRDVAR *context_name;
+
+    RRDVAR *host_fullidid;
+    RRDVAR *host_fullidname;
+    RRDVAR *host_fullnameid;
+    RRDVAR *host_fullnamename;
+
+    struct rrddim *rrddim;
+
+    struct rrddimvar *next;
+} RRDDIMVAR;
+
+// calculated variables (defined in health configuration)
+// These aggregate time-series data at fixed intervals
+// (defined in their update_every member below)
+// These increase the overhead of netdata.
+//
+// These calculations are allocated and linked (->next)
+// under RRDHOST.
+// Then are also linked to RRDSET (of course only when the
+// chart is found, via ->rrdset_next and ->rrdset_prev).
+// This double-linked list is maintained sorted at all times
+// having as RRDSET.calculations the RRDCALC to be processed
+// next.
+
+#define RRDCALC_STATUS_UNINITIALIZED  0
+#define RRDCALC_STATUS_UNDEFINED     -1
+#define RRDCALC_STATUS_OFF            1
+#define RRDCALC_STATUS_RAISED         2
+
+#define RRDCALC_OPTION_DB_ERROR      0x00000001
+#define RRDCALC_OPTION_DB_NAN        0x00000002
+#define RRDCALC_OPTION_DB_STALE      0x00000004
+#define RRDCALC_OPTION_CALC_ERROR    0x00000008
+#define RRDCALC_OPTION_WARN_ERROR    0x00000010
+#define RRDCALC_OPTION_CRIT_ERROR    0x00000020
+
+typedef struct rrdcalc {
+    char *name;
+    uint32_t hash;
+
+    char *exec;
+
+    char *chart;        // the chart id this should be linked to
+    uint32_t hash_chart;
+
+    char *source;       // the source of this calculation
+
+    char *dimensions;   // the chart dimensions
+
+    int group;          // grouping method: average, max, etc.
+    int before;         // ending point in time-series
+    int after;          // starting point in time-series
+    uint32_t options;   // calculation options
+    int update_every;   // update frequency for the calculation
+
+    time_t last_updated;
+    time_t next_update;
+
+    EVAL_EXPRESSION *calculation;
+    EVAL_EXPRESSION *warning;
+    EVAL_EXPRESSION *critical;
+
+    uint32_t rrdcalc_options;
+    int warning_status;
+    int critical_status;
+
+    time_t db_timestamp;
+
+    calculated_number value;
+
+    calculated_number green;
+    calculated_number red;
+
+    RRDVAR *local;
+    RRDVAR *context;
+    RRDVAR *hostid;
+    RRDVAR *hostname;
+
+    struct rrdset *rrdset;
+    struct rrdcalc *rrdset_next;
+    struct rrdcalc *rrdset_prev;
+
+    struct rrdcalc *next;
+} RRDCALC;
+
+#define RRDCALC_HAS_DB_LOOKUP(rc) ((rc)->after)
+
+// RRDCALCTEMPLATE
+// these are to be applied to charts found dynamically
+// based on their context.
+typedef struct rrdcalctemplate {
+    char *name;
+    uint32_t hash_name;
+
+    char *exec;
+
+    char *context;
+    uint32_t hash_context;
+
+    char *source;       // the source of this template
+
+    char *dimensions;
+
+    int group;          // grouping method: average, max, etc.
+    int before;         // ending point in time-series
+    int after;          // starting point in time-series
+    uint32_t options;   // calculation options
+    int update_every;   // update frequency for the calculation
+
+    EVAL_EXPRESSION *calculation;
+    EVAL_EXPRESSION *warning;
+    EVAL_EXPRESSION *critical;
+
+    calculated_number green;
+    calculated_number red;
+
+    struct rrdcalctemplate *next;
+} RRDCALCTEMPLATE;
+
+#define RRDCALCTEMPLATE_HAS_CALCULATION(rt) ((rt)->after)
+
+
+#include "rrd.h"
+
+extern void rrdsetvar_rename_all(RRDSET *st);
+extern RRDSETVAR *rrdsetvar_create(RRDSET *st, const char *variable, int type, void *value, uint32_t options);
+extern void rrdsetvar_free(RRDSETVAR *rs);
+
+extern void rrddimvar_rename_all(RRDDIM *rd);
+extern RRDDIMVAR *rrddimvar_create(RRDDIM *rd, int type, const char *prefix, const char *suffix, void *value, uint32_t options);
+extern void rrddimvar_free(RRDDIMVAR *rs);
+
+extern void rrdsetcalc_link_matching(RRDSET *st);
+extern void rrdsetcalc_unlink(RRDCALC *rc);
+extern void rrdcalctemplate_link_matching(RRDSET *st);
+
+extern void health_init(void);
+extern void *health_main(void *ptr);
+
+extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
+
+#endif //NETDATA_HEALTH_H
index 62f07adde5017a29e8c940a9ff74074851fe69d9..e952c57243c3f278a4fa25321a85c111f9f9768b 100644 (file)
--- a/src/log.c
+++ b/src/log.c
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <time.h>
-#include <syslog.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "log.h"
 #include "common.h"
 
-
-// ----------------------------------------------------------------------------
-// LOG
-
 const char *program_name = "";
 unsigned long long debug_flags = DEBUG;
 
-int silent = 0;
+int access_log_syslog = 1;
+int error_log_syslog = 1;
+int output_log_syslog = 1;  // debug log
 
-int access_fd = -1;
+int stdaccess_fd = -1;
 FILE *stdaccess = NULL;
 
-int access_log_syslog = 1;
-int error_log_syslog = 1;
-int output_log_syslog = 1;     // debug log
+const char *stdaccess_filename = NULL;
+const char *stderr_filename = NULL;
+const char *stdout_filename = NULL;
+
+void syslog_init(void) {
+    static int i = 0;
+
+    if(!i) {
+        openlog(program_name, LOG_PID, LOG_DAEMON);
+        i = 1;
+    }
+}
+
+int open_log_file(int fd, FILE **fp, const char *filename, int *enabled_syslog) {
+    int f, t;
+
+    if(!filename || !*filename || !strcmp(filename, "none"))
+        filename = "/dev/null";
+
+    if(!strcmp(filename, "syslog")) {
+        filename = "/dev/null";
+        syslog_init();
+        if(enabled_syslog) *enabled_syslog = 1;
+    }
+    else if(enabled_syslog) *enabled_syslog = 0;
+
+    // don't do anything if the user is willing
+    // to have the standard one
+    if(!strcmp(filename, "system")) {
+        if(fd != -1) return fd;
+        filename = "stdout";
+    }
+
+    if(!strcmp(filename, "stdout"))
+        f = STDOUT_FILENO;
+
+    else if(!strcmp(filename, "stderr"))
+        f = STDERR_FILENO;
+
+    else {
+        f = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0664);
+        if(f == -1) {
+            error("Cannot open file '%s'. Leaving %d to its default.", filename, fd);
+            return fd;
+        }
+    }
+
+    // if there is a level-2 file pointer
+    // flush it before switching the level-1 fds
+    if(fp && *fp)
+        fflush(*fp);
+
+    if(fd != f && fd != -1) {
+        // it automatically closes
+        t = dup2(f, fd);
+        if (t == -1) {
+            error("Cannot dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
+            close(f);
+            return fd;
+        }
+        // info("dup2() new fd %d to old fd %d for '%s'", f, fd, filename);
+        close(f);
+    }
+    else fd = f;
+
+    if(fp && !*fp) {
+        *fp = fdopen(fd, "a");
+        if (!*fp)
+            error("Cannot fdopen() fd %d ('%s')", fd, filename);
+        else {
+            if (setvbuf(*fp, NULL, _IOLBF, 0) != 0)
+                error("Cannot set line buffering on fd %d ('%s')", fd, filename);
+        }
+    }
+
+    return fd;
+}
+
+void reopen_all_log_files() {
+    if(stdout_filename)
+        open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+
+    if(stderr_filename)
+        open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+
+    if(stdaccess_filename)
+        stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+}
+
+void open_all_log_files() {
+    // disable stdin
+    open_log_file(STDIN_FILENO, &stdin, "/dev/null", NULL);
+
+    open_log_file(STDOUT_FILENO, &stdout, stdout_filename, &output_log_syslog);
+    open_log_file(STDERR_FILENO, &stderr, stderr_filename, &error_log_syslog);
+    stdaccess_fd = open_log_file(stdaccess_fd, &stdaccess, stdaccess_filename, &access_log_syslog);
+}
+
+// ----------------------------------------------------------------------------
+// error log throttling
 
 time_t error_log_throttle_period = 1200;
 unsigned long error_log_errors_per_period = 200;
 
 int error_log_limit(int reset) {
-       static time_t start = 0;
-       static unsigned long counter = 0, prevented = 0;
-
-       // do not throttle if the period is 0
-       if(error_log_throttle_period == 0)
-               return 0;
-
-       // prevent all logs if the errors per period is 0
-       if(error_log_errors_per_period == 0)
-               return 1;
-
-       time_t now = time(NULL);
-       if(!start) start = now;
-
-       if(reset) {
-               if(prevented) {
-                       log_date(stderr);
-                       fprintf(stderr, "%s: Resetting logging for process '%s' (prevented %lu logs in the last %ld seconds).\n"
-                                       , program_name
-                               , program_name
-                                       , prevented
-                                       , now - start
-                       );
-               }
-
-               start = now;
-               counter = 0;
-               prevented = 0;
-       }
-
-       // detect if we log too much
-       counter++;
-
-       if(now - start > error_log_throttle_period) {
-               if(prevented) {
-                       log_date(stderr);
-                       fprintf(stderr, "%s: Resuming logging from process '%s' (prevented %lu logs in the last %ld seconds).\n"
-                                       , program_name
-                               , program_name
-                                       , prevented
-                                       , error_log_throttle_period
-                       );
-               }
-
-               // restart the period accounting
-               start = now;
-               counter = 1;
-               prevented = 0;
-
-               // log this error
-               return 0;
-       }
-
-       if(counter > error_log_errors_per_period) {
-               if(!prevented) {
-                       log_date(stderr);
-                       fprintf(stderr, "%s: Too many logs (%lu logs in %ld seconds, threshold is set to %lu logs in %ld seconds). Preventing more logs from process '%s' for %ld seconds.\n"
-                                       , program_name
-                               , counter
-                               , now - start
-                               , error_log_errors_per_period
-                               , error_log_throttle_period
-                               , program_name
-                                       , start + error_log_throttle_period - now);
-               }
-
-               prevented++;
-
-               // prevent logging this error
-               return 1;
-       }
-
-       return 0;
+    static time_t start = 0;
+    static unsigned long counter = 0, prevented = 0;
+
+    // do not throttle if the period is 0
+    if(error_log_throttle_period == 0)
+        return 0;
+
+    // prevent all logs if the errors per period is 0
+    if(error_log_errors_per_period == 0)
+        return 1;
+
+    time_t now = time(NULL);
+    if(!start) start = now;
+
+    if(reset) {
+        if(prevented) {
+            log_date(stderr);
+            fprintf(stderr, "%s: Resetting logging for process '%s' (prevented %lu logs in the last %ld seconds).\n"
+                    , program_name
+                    , program_name
+                    , prevented
+                    , now - start
+            );
+        }
+
+        start = now;
+        counter = 0;
+        prevented = 0;
+    }
+
+    // detect if we log too much
+    counter++;
+
+    if(now - start > error_log_throttle_period) {
+        if(prevented) {
+            log_date(stderr);
+            fprintf(stderr, "%s: Resuming logging from process '%s' (prevented %lu logs in the last %ld seconds).\n"
+                    , program_name
+                    , program_name
+                    , prevented
+                    , error_log_throttle_period
+            );
+        }
+
+        // restart the period accounting
+        start = now;
+        counter = 1;
+        prevented = 0;
+
+        // log this error
+        return 0;
+    }
+
+    if(counter > error_log_errors_per_period) {
+        if(!prevented) {
+            log_date(stderr);
+            fprintf(stderr, "%s: Too many logs (%lu logs in %ld seconds, threshold is set to %lu logs in %ld seconds). Preventing more logs from process '%s' for %ld seconds.\n"
+                    , program_name
+                    , counter
+                    , now - start
+                    , error_log_errors_per_period
+                    , error_log_throttle_period
+                    , program_name
+                    , start + error_log_throttle_period - now);
+        }
+
+        prevented++;
+
+        // prevent logging this error
+        return 1;
+    }
+
+    return 0;
 }
 
+// ----------------------------------------------------------------------------
+// print the date
+
+// FIXME
+// this should print the date in a buffer the way it
+// is now, logs from multiple threads may be multiplexed
+
 void log_date(FILE *out)
 {
-               char outstr[24];
-               time_t t;
-               struct tm *tmp, tmbuf;
+        char outstr[24];
+        time_t t;
+        struct tm *tmp, tmbuf;
 
-               t = time(NULL);
-               tmp = localtime_r(&t, &tmbuf);
+        t = time(NULL);
+        tmp = localtime_r(&t, &tmbuf);
 
-               if (tmp == NULL) return;
-               if (unlikely(strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0)) return;
+        if (tmp == NULL) return;
+        if (unlikely(strftime(outstr, sizeof(outstr), "%y-%m-%d %H:%M:%S", tmp) == 0)) return;
 
-               fprintf(out, "%s: ", outstr);
+        fprintf(out, "%s: ", outstr);
 }
 
+// ----------------------------------------------------------------------------
+// debug log
+
 void debug_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
 {
-       va_list args;
-
-       log_date(stdout);
-       va_start( args, fmt );
-       printf("DEBUG (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
-       vprintf(fmt, args);
-       va_end( args );
-       putchar('\n');
-
-       if(output_log_syslog) {
-               va_start( args, fmt );
-               vsyslog(LOG_ERR,  fmt, args );
-               va_end( args );
-       }
+    va_list args;
+
+    log_date(stdout);
+    va_start( args, fmt );
+    printf("DEBUG (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+    vprintf(fmt, args);
+    va_end( args );
+    putchar('\n');
+
+    if(output_log_syslog) {
+        va_start( args, fmt );
+        vsyslog(LOG_ERR,  fmt, args );
+        va_end( args );
+    }
+
+    fflush(stdout);
 }
 
+// ----------------------------------------------------------------------------
+// info log
+
 void info_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
 {
-       va_list args;
+    va_list args;
 
-       // prevent logging too much
-       if(error_log_limit(0)) return;
+    // prevent logging too much
+    if(error_log_limit(0)) return;
 
-       log_date(stderr);
+    log_date(stderr);
 
-       va_start( args, fmt );
-       if(debug_flags) fprintf(stderr, "INFO (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
-       else            fprintf(stderr, "INFO: %s: ", program_name);
-       vfprintf( stderr, fmt, args );
-       va_end( args );
+    va_start( args, fmt );
+    if(debug_flags) fprintf(stderr, "INFO (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+    else            fprintf(stderr, "INFO: %s: ", program_name);
+    vfprintf( stderr, fmt, args );
+    va_end( args );
 
-       fputc('\n', stderr);
+    fputc('\n', stderr);
 
-       if(error_log_syslog) {
-               va_start( args, fmt );
-               vsyslog(LOG_INFO,  fmt, args );
-               va_end( args );
-       }
+    if(error_log_syslog) {
+        va_start( args, fmt );
+        vsyslog(LOG_INFO,  fmt, args );
+        va_end( args );
+    }
 }
 
+// ----------------------------------------------------------------------------
+// error log
+
 void error_int( const char *prefix, const char *file, const char *function, const unsigned long line, const char *fmt, ... )
 {
-       va_list args;
-
-       // prevent logging too much
-       if(error_log_limit(0)) return;
-
-       log_date(stderr);
-
-       va_start( args, fmt );
-       if(debug_flags) fprintf(stderr, "%s (%04lu@%-10.10s:%-15.15s): %s: ", prefix, line, file, function, program_name);
-       else            fprintf(stderr, "%s: %s: ", prefix, program_name);
-       vfprintf( stderr, fmt, args );
-       va_end( args );
-
-       if(errno) {
-               char buf[1024];
-               fprintf(stderr, " (errno %d, %s)\n", errno, strerror_r(errno, buf, 1023));
-               errno = 0;
-       }
-       else
-               fputc('\n', stderr);
-
-       if(error_log_syslog) {
-               va_start( args, fmt );
-               vsyslog(LOG_ERR,  fmt, args );
-               va_end( args );
-       }
+    va_list args;
+
+    // prevent logging too much
+    if(error_log_limit(0)) return;
+
+    log_date(stderr);
+
+    va_start( args, fmt );
+    if(debug_flags) fprintf(stderr, "%s (%04lu@%-10.10s:%-15.15s): %s: ", prefix, line, file, function, program_name);
+    else            fprintf(stderr, "%s: %s: ", prefix, program_name);
+    vfprintf( stderr, fmt, args );
+    va_end( args );
+
+    if(errno) {
+        char buf[1024];
+        fprintf(stderr, " (errno %d, %s)\n", errno, strerror_r(errno, buf, 1023));
+        errno = 0;
+    }
+    else
+        fputc('\n', stderr);
+
+    if(error_log_syslog) {
+        va_start( args, fmt );
+        vsyslog(LOG_ERR,  fmt, args );
+        va_end( args );
+    }
 }
 
 void fatal_int( const char *file, const char *function, const unsigned long line, const char *fmt, ... )
 {
-       va_list args;
+    va_list args;
 
-       log_date(stderr);
+    log_date(stderr);
 
-       va_start( args, fmt );
-       if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
-       else            fprintf(stderr, "FATAL: %s: ", program_name);
-       vfprintf( stderr, fmt, args );
-       va_end( args );
+    va_start( args, fmt );
+    if(debug_flags) fprintf(stderr, "FATAL (%04lu@%-10.10s:%-15.15s): %s: ", line, file, function, program_name);
+    else            fprintf(stderr, "FATAL: %s: ", program_name);
+    vfprintf( stderr, fmt, args );
+    va_end( args );
 
-       perror(" # ");
-       fputc('\n', stderr);
+    perror(" # ");
+    fputc('\n', stderr);
 
-       if(error_log_syslog) {
-               va_start( args, fmt );
-               vsyslog(LOG_CRIT,  fmt, args );
-               va_end( args );
-       }
+    if(error_log_syslog) {
+        va_start( args, fmt );
+        vsyslog(LOG_CRIT,  fmt, args );
+        va_end( args );
+    }
 
-       netdata_cleanup_and_exit(1);
+    netdata_cleanup_and_exit(1);
 }
 
+// ----------------------------------------------------------------------------
+// access log
+
 void log_access( const char *fmt, ... )
 {
-       va_list args;
-
-       if(stdaccess) {
-               log_date(stdaccess);
-
-               va_start( args, fmt );
-               vfprintf( stdaccess, fmt, args );
-               va_end( args );
-               fputc('\n', stdaccess);
-       }
-
-       if(access_log_syslog) {
-               va_start( args, fmt );
-               vsyslog(LOG_INFO,  fmt, args );
-               va_end( args );
-       }
+    va_list args;
+
+    if(stdaccess) {
+        log_date(stdaccess);
+
+        va_start( args, fmt );
+        vfprintf( stdaccess, fmt, args );
+        va_end( args );
+        fputc('\n', stdaccess);
+    }
+
+    if(access_log_syslog) {
+        va_start( args, fmt );
+        vsyslog(LOG_INFO,  fmt, args );
+        va_end( args );
+    }
 }
 
index 2c0f5cd7a1884c96afc520ad2ad4f122294e36a8..93d95321ee3599d36276d8388a8a3ffa7f17b097 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -1,20 +1,14 @@
-#include <stdio.h>
-#include <stdarg.h>
-#include <time.h>
-
-#include "main.h"
-
 #ifndef NETDATA_LOG_H
 #define NETDATA_LOG_H 1
 
-#define D_WEB_BUFFER           0x00000001
-#define D_WEB_CLIENT           0x00000002
-#define D_LISTENER             0x00000004
-#define D_WEB_DATA             0x00000008
-#define D_OPTIONS              0x00000010
+#define D_WEB_BUFFER        0x00000001
+#define D_WEB_CLIENT        0x00000002
+#define D_LISTENER          0x00000004
+#define D_WEB_DATA          0x00000008
+#define D_OPTIONS           0x00000010
 #define D_PROCNETDEV_LOOP   0x00000020
-#define D_RRD_STATS            0x00000040
-#define D_WEB_CLIENT_ACCESS    0x00000080
+#define D_RRD_STATS         0x00000040
+#define D_WEB_CLIENT_ACCESS 0x00000080
 #define D_TC_LOOP           0x00000100
 #define D_DEFLATE           0x00000200
 #define D_CONFIG            0x00000400
 #define D_CHILDS            0x00001000
 #define D_EXIT              0x00002000
 #define D_CHECKS            0x00004000
-#define D_NFACCT_LOOP          0x00008000
-#define D_PROCFILE                     0x00010000
-#define D_RRD_CALLS                    0x00020000
-#define D_DICTIONARY           0x00040000
-#define D_MEMORY                       0x00080000
+#define D_NFACCT_LOOP       0x00008000
+#define D_PROCFILE          0x00010000
+#define D_RRD_CALLS         0x00020000
+#define D_DICTIONARY        0x00040000
+#define D_MEMORY            0x00080000
 #define D_CGROUP            0x00100000
-#define D_REGISTRY                     0x00200000
+#define D_REGISTRY          0x00200000
+#define D_VARIABLES         0x00400000
+#define D_HEALTH            0x00800000
 
 //#define DEBUG (D_WEB_CLIENT_ACCESS|D_LISTENER|D_RRD_STATS)
 //#define DEBUG 0xffffffff
@@ -38,11 +34,13 @@ extern unsigned long long debug_flags;
 
 extern const char *program_name;
 
-extern int silent;
-
-extern int access_fd;
+extern int stdaccess_fd;
 extern FILE *stdaccess;
 
+extern const char *stdaccess_filename;
+extern const char *stderr_filename;
+extern const char *stdout_filename;
+
 extern int access_log_syslog;
 extern int error_log_syslog;
 extern int output_log_syslog;
@@ -51,10 +49,13 @@ extern time_t error_log_throttle_period;
 extern unsigned long error_log_errors_per_period;
 extern int error_log_limit(int reset);
 
+extern void open_all_log_files();
+extern void reopen_all_log_files();
+
 #define error_log_limit_reset() do { error_log_limit(1); } while(0)
 #define error_log_limit_unlimited() do { error_log_throttle_period = 0; } while(0)
 
-#define debug(type, args...) do { if(unlikely(!silent && (debug_flags & type))) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
+#define debug(type, args...) do { if(unlikely(debug_flags & type)) debug_int(__FILE__, __FUNCTION__, __LINE__, ##args); } while(0)
 #define info(args...)    info_int(__FILE__, __FUNCTION__, __LINE__, ##args)
 #define infoerr(args...) error_int("INFO", __FILE__, __FUNCTION__, __LINE__, ##args)
 #define error(args...)   error_int("ERROR", __FILE__, __FUNCTION__, __LINE__, ##args)
index 41590c56daf1dde0d9ec47836df41d648e1a28f5..55742ad3cb1cd51da9381db7f191c70c5b2124aa 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <errno.h>
-#include <getopt.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/wait.h>
-#include <syslog.h>
-#include <unistd.h>
-
-#include "appconfig.h"
 #include "common.h"
-#include "daemon.h"
-#include "log.h"
-#include "popen.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "web_client.h"
-#include "web_server.h"
-
-#include "unit_test.h"
-
-#include "plugin_checks.h"
-#include "plugin_idlejitter.h"
-#include "plugin_nfacct.h"
-#include "registry.h"
-#include "plugin_proc.h"
-#include "plugin_tc.h"
-#include "plugins_d.h"
-
-#include "main.h"
 
 extern void *cgroups_main(void *ptr);
 
-volatile sig_atomic_t netdata_exit = 0;
-
 void netdata_cleanup_and_exit(int ret) {
-       netdata_exit = 1;
+    netdata_exit = 1;
 
-       error_log_limit_unlimited();
+    error_log_limit_unlimited();
 
-       info("Called: netdata_cleanup_and_exit()");
-       rrdset_save_all();
-       // kill_childs();
+    info("Called: netdata_cleanup_and_exit()");
+#ifdef NETDATA_INTERNAL_CHECKS
+    rrdset_free_all();
+#else
+    rrdset_save_all();
+#endif
+    // kill_childs();
 
-       if(pidfile[0]) {
-               if(unlink(pidfile) != 0)
-                       error("Cannot unlink pidfile '%s'.", pidfile);
-       }
+    if(pidfile[0]) {
+        if(unlink(pidfile) != 0)
+            error("Cannot unlink pidfile '%s'.", pidfile);
+    }
 
-       info("NetData exiting. Bye bye...");
-       exit(ret);
+    info("NetData exiting. Bye bye...");
+    exit(ret);
 }
 
 struct netdata_static_thread {
-       char *name;
+    char *name;
 
-       char *config_section;
-       char *config_name;
+    char *config_section;
+    char *config_name;
 
-       int enabled;
+    int enabled;
 
-       pthread_t *thread;
+    pthread_t *thread;
 
-       void (*init_routine) (void);
-       void *(*start_routine) (void *);
+    void (*init_routine) (void);
+    void *(*start_routine) (void *);
 } static_threads[] = {
 #ifdef INTERNAL_PLUGIN_NFACCT
 // nfacct requires root access
-       // so, we build it as an external plugin with setuid to root
-       {"nfacct",              "plugins",  "nfacct",     1, NULL, NULL, nfacct_main},
+    // so, we build it as an external plugin with setuid to root
+    {"nfacct",              "plugins",  "nfacct",     1, NULL, NULL, nfacct_main},
 #endif
 
-       {"tc",                 "plugins",   "tc",         1, NULL, NULL, tc_main},
-       {"idlejitter",         "plugins",   "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
-       {"proc",               "plugins",   "proc",       1, NULL, NULL, proc_main},
-       {"cgroups",            "plugins",   "cgroups",    1, NULL, NULL, cgroups_main},
-       {"plugins.d",           NULL,       NULL,         1, NULL, NULL, pluginsd_main},
-       {"check",               "plugins",  "checks",     0, NULL, NULL, checks_main},
-       {"web",                 NULL,       NULL,         1, NULL, NULL, socket_listen_main_multi_threaded},
-       {"web-single-threaded", NULL,       NULL,         0, NULL, NULL, socket_listen_main_single_threaded},
-       {NULL,                  NULL,       NULL,         0, NULL, NULL, NULL}
+    {"tc",                 "plugins",   "tc",         1, NULL, NULL, tc_main},
+    {"idlejitter",         "plugins",   "idlejitter", 1, NULL, NULL, cpuidlejitter_main},
+    {"proc",               "plugins",   "proc",       1, NULL, NULL, proc_main},
+    {"cgroups",            "plugins",   "cgroups",    1, NULL, NULL, cgroups_main},
+    {"check",              "plugins",   "checks",     0, NULL, NULL, checks_main},
+    {"health",              NULL,       NULL,         1, NULL, NULL, health_main},
+    {"plugins.d",           NULL,       NULL,         1, NULL, NULL, pluginsd_main},
+    {"web",                 NULL,       NULL,         1, NULL, NULL, socket_listen_main_multi_threaded},
+    {"web-single-threaded", NULL,       NULL,         0, NULL, NULL, socket_listen_main_single_threaded},
+    {NULL,                  NULL,       NULL,         0, NULL, NULL, NULL}
 };
 
 void web_server_threading_selection(void) {
-       int threaded = config_get_boolean("global", "multi threaded web server", 1);
+    int threaded = config_get_boolean("global", "multi threaded web server", 1);
 
-       int i;
-       for(i = 0; static_threads[i].name ; i++) {
-               if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
-                       static_threads[i].enabled = threaded?1:0;
+    int i;
+    for(i = 0; static_threads[i].name ; i++) {
+        if(static_threads[i].start_routine == socket_listen_main_multi_threaded)
+            static_threads[i].enabled = threaded?1:0;
 
-               if(static_threads[i].start_routine == socket_listen_main_single_threaded)
-                       static_threads[i].enabled = threaded?0:1;
-       }
+        if(static_threads[i].start_routine == socket_listen_main_single_threaded)
+            static_threads[i].enabled = threaded?0:1;
+    }
 
-       web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
+    web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
 
-       web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
+    web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
 
 #ifdef NETDATA_WITH_ZLIB
-       web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
-
-       char *s = config_get("global", "web compression strategy", "default");
-       if(!strcmp(s, "default"))
-               web_gzip_strategy = Z_DEFAULT_STRATEGY;
-       else if(!strcmp(s, "filtered"))
-               web_gzip_strategy = Z_FILTERED;
-       else if(!strcmp(s, "huffman only"))
-               web_gzip_strategy = Z_HUFFMAN_ONLY;
-       else if(!strcmp(s, "rle"))
-               web_gzip_strategy = Z_RLE;
-       else if(!strcmp(s, "fixed"))
-               web_gzip_strategy = Z_FIXED;
-       else {
-               error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
-               web_gzip_strategy = Z_DEFAULT_STRATEGY;
-       }
-
-       web_gzip_level = (int)config_get_number("global", "web compression level", 3);
-       if(web_gzip_level < 1) {
-               error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
-               web_gzip_level = 1;
-       }
-       else if(web_gzip_level > 9) {
-               error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
-               web_gzip_level = 9;
-       }
+    web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
+
+    char *s = config_get("global", "web compression strategy", "default");
+    if(!strcmp(s, "default"))
+        web_gzip_strategy = Z_DEFAULT_STRATEGY;
+    else if(!strcmp(s, "filtered"))
+        web_gzip_strategy = Z_FILTERED;
+    else if(!strcmp(s, "huffman only"))
+        web_gzip_strategy = Z_HUFFMAN_ONLY;
+    else if(!strcmp(s, "rle"))
+        web_gzip_strategy = Z_RLE;
+    else if(!strcmp(s, "fixed"))
+        web_gzip_strategy = Z_FIXED;
+    else {
+        error("Invalid compression strategy '%s'. Valid strategies are 'default', 'filtered', 'huffman only', 'rle' and 'fixed'. Proceeding with 'default'.", s);
+        web_gzip_strategy = Z_DEFAULT_STRATEGY;
+    }
+
+    web_gzip_level = (int)config_get_number("global", "web compression level", 3);
+    if(web_gzip_level < 1) {
+        error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 1 (fastest compression).", web_gzip_level);
+        web_gzip_level = 1;
+    }
+    else if(web_gzip_level > 9) {
+        error("Invalid compression level %d. Valid levels are 1 (fastest) to 9 (best ratio). Proceeding with level 9 (best compression).", web_gzip_level);
+        web_gzip_level = 9;
+    }
 #endif /* NETDATA_WITH_ZLIB */
 }
 
 
 int killpid(pid_t pid, int sig)
 {
-       int ret = -1;
-       debug(D_EXIT, "Request to kill pid %d", pid);
-
-       errno = 0;
-       if(kill(pid, 0) == -1) {
-               switch(errno) {
-                       case ESRCH:
-                               error("Request to kill pid %d, but it is not running.", pid);
-                               break;
-
-                       case EPERM:
-                               error("Request to kill pid %d, but I do not have enough permissions.", pid);
-                               break;
-
-                       default:
-                               error("Request to kill pid %d, but I received an error.", pid);
-                               break;
-               }
-       }
-       else {
-               errno = 0;
-               ret = kill(pid, sig);
-               if(ret == -1) {
-                       switch(errno) {
-                               case ESRCH:
-                                       error("Cannot kill pid %d, but it is not running.", pid);
-                                       break;
-
-                               case EPERM:
-                                       error("Cannot kill pid %d, but I do not have enough permissions.", pid);
-                                       break;
-
-                               default:
-                                       error("Cannot kill pid %d, but I received an error.", pid);
-                                       break;
-                       }
-               }
-       }
-
-       return ret;
+    int ret = -1;
+    debug(D_EXIT, "Request to kill pid %d", pid);
+
+    errno = 0;
+    if(kill(pid, 0) == -1) {
+        switch(errno) {
+            case ESRCH:
+                error("Request to kill pid %d, but it is not running.", pid);
+                break;
+
+            case EPERM:
+                error("Request to kill pid %d, but I do not have enough permissions.", pid);
+                break;
+
+            default:
+                error("Request to kill pid %d, but I received an error.", pid);
+                break;
+        }
+    }
+    else {
+        errno = 0;
+        ret = kill(pid, sig);
+        if(ret == -1) {
+            switch(errno) {
+                case ESRCH:
+                    error("Cannot kill pid %d, but it is not running.", pid);
+                    break;
+
+                case EPERM:
+                    error("Cannot kill pid %d, but I do not have enough permissions.", pid);
+                    break;
+
+                default:
+                    error("Cannot kill pid %d, but I received an error.", pid);
+                    break;
+            }
+        }
+    }
+
+    return ret;
 }
 
 void kill_childs()
 {
-       siginfo_t info;
-
-       struct web_client *w;
-       for(w = web_clients; w ; w = w->next) {
-               debug(D_EXIT, "Stopping web client %s", w->client_ip);
-               pthread_cancel(w->thread);
-               pthread_join(w->thread, NULL);
-       }
-
-       int i;
-       for (i = 0; static_threads[i].name != NULL ; i++) {
-               if(static_threads[i].thread) {
-                       debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
-                       pthread_cancel(*static_threads[i].thread);
-                       pthread_join(*static_threads[i].thread, NULL);
-                       static_threads[i].thread = NULL;
-               }
-       }
-
-       if(tc_child_pid) {
-               info("Killing tc-qos-helper procees");
-               if(killpid(tc_child_pid, SIGTERM) != -1)
-                       waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
-       }
-       tc_child_pid = 0;
-
-       struct plugind *cd;
-       for(cd = pluginsd_root ; cd ; cd = cd->next) {
-               debug(D_EXIT, "Stopping %s plugin thread", cd->id);
-               pthread_cancel(cd->thread);
-               pthread_join(cd->thread, NULL);
-
-               if(cd->pid && !cd->obsolete) {
-                       debug(D_EXIT, "killing %s plugin process", cd->id);
-                       if(killpid(cd->pid, SIGTERM) != -1)
-                               waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
-               }
-       }
-
-       // if, for any reason there is any child exited
-       // catch it here
-       waitid(P_PID, 0, &info, WEXITED|WNOHANG);
-
-       debug(D_EXIT, "All threads/childs stopped.");
+    siginfo_t info;
+
+    struct web_client *w;
+    for(w = web_clients; w ; w = w->next) {
+        debug(D_EXIT, "Stopping web client %s", w->client_ip);
+        pthread_cancel(w->thread);
+        pthread_join(w->thread, NULL);
+    }
+
+    int i;
+    for (i = 0; static_threads[i].name != NULL ; i++) {
+        if(static_threads[i].thread) {
+            debug(D_EXIT, "Stopping %s thread", static_threads[i].name);
+            pthread_cancel(*static_threads[i].thread);
+            pthread_join(*static_threads[i].thread, NULL);
+            static_threads[i].thread = NULL;
+        }
+    }
+
+    if(tc_child_pid) {
+        info("Killing tc-qos-helper procees");
+        if(killpid(tc_child_pid, SIGTERM) != -1)
+            waitid(P_PID, (id_t) tc_child_pid, &info, WEXITED);
+    }
+    tc_child_pid = 0;
+
+    struct plugind *cd;
+    for(cd = pluginsd_root ; cd ; cd = cd->next) {
+        debug(D_EXIT, "Stopping %s plugin thread", cd->id);
+        pthread_cancel(cd->thread);
+        pthread_join(cd->thread, NULL);
+
+        if(cd->pid && !cd->obsolete) {
+            debug(D_EXIT, "killing %s plugin process", cd->id);
+            if(killpid(cd->pid, SIGTERM) != -1)
+                waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
+        }
+    }
+
+    // if, for any reason there is any child exited
+    // catch it here
+    waitid(P_PID, 0, &info, WEXITED|WNOHANG);
+
+    debug(D_EXIT, "All threads/childs stopped.");
 }
 
 struct option_def options[] = {
-       // opt description                                                       arg name                     default value
-       {'c', "Load alternate configuration file",                               "config_file",                          CONFIG_DIR "/" CONFIG_FILENAME},
-       {'D', "Disable fork into background",                                    NULL,                                   NULL},
-       {'h', "Display help message",                                            NULL,                                   NULL},
-       {'P', "File to save a pid while running",                                "FILE",                                 NULL},
-       {'i', "The IP address to listen to.",                                    "address",                              "All addresses"},
-       {'p', "Port to listen. Can be from 1 to 65535.",                         "port_number",                          "19999"},
-       {'s', "Path to access host /proc and /sys when running in a container.", "PATH",                                 NULL},
-       {'t', "The frequency in seconds, for data collection. \
+    // opt description                                                       arg name                     default value
+    {'c', "Load alternate configuration file",                               "config_file",                          CONFIG_DIR "/" CONFIG_FILENAME},
+    {'D', "Disable fork into background",                                    NULL,                                   NULL},
+    {'h', "Display help message",                                            NULL,                                   NULL},
+    {'P', "File to save a pid while running",                                "FILE",                                 NULL},
+    {'i', "The IP address to listen to.",                                    "address",                              "All addresses"},
+    {'k', "Check daemon configuration.",                                     NULL,                                   NULL},
+    {'p', "Port to listen. Can be from 1 to 65535.",                         "port_number",                          "19999"},
+    {'s', "Path to access host /proc and /sys when running in a container.", "PATH",                                 NULL},
+    {'t', "The frequency in seconds, for data collection. \
 Same as 'update every' config file option.",                                 "seconds",                              "1"},
-       {'u', "System username to run as.",                                      "username",                             "netdata"},
-       {'v', "Version of the program",                                          NULL,                                   NULL},
-       {'W', "vendor options.",                                                 "stacksize=<size>|unittest|debug_flag", NULL},
+    {'u', "System username to run as.",                                      "username",                             "netdata"},
+    {'v', "Version of the program",                                          NULL,                                   NULL},
+    {'W', "vendor options.",                                                 "stacksize=<size>|unittest|debug_flag", NULL},
 };
 
 void help(int exitcode) {
-       FILE *stream;
-       if(exitcode == 0)
-               stream = stdout;
-       else
-               stream = stderr;
-
-       int num_opts = sizeof(options) / sizeof(struct option_def);
-       int i;
-       int max_len_arg = 0;
-
-       // Compute maximum argument length
-       for( i = 0; i < num_opts; i++ ) {
-               if(options[i].arg_name) {
-                       int len_arg = strlen(options[i].arg_name);
-                       if(len_arg > max_len_arg) max_len_arg = len_arg;
-               }
-       }
-
-       fprintf(stream, "SYNOPSIS: netdata [options]\n");
-       fprintf(stream, "\n");
-       fprintf(stream, "Options:\n");
-
-       // Output options description.
-       for( i = 0; i < num_opts; i++ ) {
-               fprintf(stream, "  -%c %-*s  %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
-               if(options[i].default_value) {
-                       fprintf(stream, " Default: %s\n", options[i].default_value);
-               } else {
-                       fprintf(stream, "\n");
-               }
-       }
-
-       fflush(stream);
-       exit(exitcode);
+    FILE *stream;
+    if(exitcode == 0)
+        stream = stdout;
+    else
+        stream = stderr;
+
+    int num_opts = sizeof(options) / sizeof(struct option_def);
+    int i;
+    int max_len_arg = 0;
+
+    // Compute maximum argument length
+    for( i = 0; i < num_opts; i++ ) {
+        if(options[i].arg_name) {
+            int len_arg = (int)strlen(options[i].arg_name);
+            if(len_arg > max_len_arg) max_len_arg = len_arg;
+        }
+    }
+
+    fprintf(stream, "SYNOPSIS: netdata [options]\n");
+    fprintf(stream, "\n");
+    fprintf(stream, "Options:\n");
+
+    // Output options description.
+    for( i = 0; i < num_opts; i++ ) {
+        fprintf(stream, "  -%c %-*s  %s", options[i].val, max_len_arg, options[i].arg_name ? options[i].arg_name : "", options[i].description);
+        if(options[i].default_value) {
+            fprintf(stream, " Default: %s\n", options[i].default_value);
+        } else {
+            fprintf(stream, "\n");
+        }
+    }
+
+    fflush(stream);
+    exit(exitcode);
 }
 
 // TODO: Remove this function with the nix major release.
 void remove_option(int opt_index, int *argc, char **argv) {
-       int i = opt_index;
-       // remove the options.
-       do {
-               *argc = *argc - 1;
-               for(i = opt_index; i < *argc; i++) {
-                       argv[i] = argv[i+1];
-               }
-               i = opt_index;
-       } while(argv[i][0] != '-' && opt_index >= *argc);
+    int i = opt_index;
+    // remove the options.
+    do {
+        *argc = *argc - 1;
+        for(i = opt_index; i < *argc; i++) {
+            argv[i] = argv[i+1];
+        }
+        i = opt_index;
+    } while(argv[i][0] != '-' && opt_index >= *argc);
 }
 
 
 int main(int argc, char **argv)
 {
-       int i;
-       int config_loaded = 0;
-       int dont_fork = 0;
-       size_t wanted_stacksize = 0, stacksize = 0;
-       pthread_attr_t attr;
-
-       // global initialization
-       get_HZ();
-
-       // set the name for logging
-       program_name = "netdata";
-
-       // parse command line.
-
-       // parse depercated options
-       // TODO: Remove this block with the next major release.
-       {
-               i = 1;
-               while(i < argc) {
-                       if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
-                               strncpyz(pidfile, argv[i+1], FILENAME_MAX);
-                               fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
-                               remove_option(i, &argc, argv);
-                       }
-                       else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
-                               dont_fork = 1;
-                               fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
-                               remove_option(i, &argc, argv);
-                       }
-                       else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
-                               config_set("global", "host access prefix", argv[i+1]);
-                               fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
-                               remove_option(i, &argc, argv);
-                       }
-                       else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
-                               config_set("global", "history", argv[i+1]);
-                               fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
-                               remove_option(i, &argc, argv);
-                       }
-                       else i++;
-               }
-       }
-
-       // parse options
-       {
-               int num_opts = sizeof(options) / sizeof(struct option_def);
-               char optstring[(num_opts * 2) + 1];
-
-               int string_i = 0;
-               for( i = 0; i < num_opts; i++ ) {
-                       optstring[string_i] = options[i].val;
-                       string_i++;
-                       if(options[i].arg_name) {
-                               optstring[string_i] = ':';
-                               string_i++;
-                       }
-               }
-
-               int opt;
-               while( (opt = getopt(argc, argv, optstring)) != -1 ) {
-                       switch(opt) {
-                               case 'c':
-                                       if(load_config(optarg, 1) != 1) {
-                                               error("Cannot load configuration file %s.", optarg);
-                                               exit(1);
-                                       }
-                                       else {
-                                               debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
-                                               config_loaded = 1;
-                                       }
-                                       break;
-                               case 'D':
-                                       dont_fork = 1;
-                                       break;
-                               case 'h':
-                                       help(0);
-                                       break;
-                               case 'i':
-                                       config_set("global", "bind to", optarg);
-                                       break;
-                               case 'P':
-                                       strncpy(pidfile, optarg, FILENAME_MAX);
-                                       pidfile[FILENAME_MAX] = '\0';
-                                       break;
-                               case 'p':
-                                       config_set("global", "default port", optarg);
-                                       break;
-                               case 's':
-                                       config_set("global", "host access prefix", optarg);
-                                       break;
-                               case 't':
-                                       config_set("global", "update every", optarg);
-                                       break;
-                               case 'u':
-                                       config_set("global", "run as user", optarg);
-                                       break;
-                               case 'v':
-                                       // TODO: Outsource version to makefile which can compute version from git.
-                                       printf("netdata 1.2.1_master\n");
-                                       return 0;
-                                       break;
-                               case 'W':
-                                       {
-                                               char* stacksize = "stacksize=";
-                                               char* debug_flags_string = "debug_flags=";
-                                               if(strcmp(optarg, "unittest") == 0) {
-                                                       rrd_update_every = 1;
-                                                       if(run_all_mockup_tests()) exit(1);
-                                                       if(unit_test_storage()) exit(1);
-                                                       fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
-                                                       exit(0);
-                                               } else if(strncmp(optarg, stacksize, strlen(stacksize)) == 0) {
-                                                       optarg += strlen(stacksize);
-                                                       config_set("global", "pthread stack size", optarg);
-                                               } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
-                                                       optarg += strlen(debug_flags_string);
-                                                       config_set("global", "debug flags",  optarg);
-                                                       debug_flags = strtoull(optarg, NULL, 0);
-                                               }
-                                       }
-                                       break;
-                               default: /* ? */
-                                       help(1);
-                                       break;
-                       }
-               }
-       }
-
-       if(!config_loaded) load_config(NULL, 0);
-
-       // prepare configuration environment variables for the plugins
-       setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory"   , CONFIG_DIR) , 1);
-       setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory"  , PLUGINS_DIR), 1);
-       setenv("NETDATA_WEB_DIR"    , config_get("global", "web files directory", WEB_DIR)    , 1);
-       setenv("NETDATA_CACHE_DIR"  , config_get("global", "cache directory"    , CACHE_DIR)  , 1);
-       setenv("NETDATA_LIB_DIR"    , config_get("global", "lib directory"      , VARLIB_DIR) , 1);
-       setenv("NETDATA_LOG_DIR"    , config_get("global", "log directory"      , LOG_DIR)    , 1);
-       setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "")         , 1);
-       setenv("HOME"               , config_get("global", "home directory"     , CACHE_DIR)  , 1);
-
-       // 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);
-
-       {
-               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);
-       }
-
-       // cd to /tmp to avoid any plugins writing files at random places
-       if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
-
-       char *input_log_file = NULL;
-       char *output_log_file = NULL;
-       char *error_log_file = NULL;
-       char *access_log_file = NULL;
-       char *user = NULL;
-       {
-               char *flags = config_get("global", "debug flags",  "0x00000000");
-               setenv("NETDATA_DEBUG_FLAGS", flags, 1);
-
-               debug_flags = strtoull(flags, NULL, 0);
-               debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
-
-               if(debug_flags != 0) {
-                       struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
-                       if(setrlimit(RLIMIT_CORE, &rl) != 0)
-                               info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
-                       prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
-               }
-
-               // --------------------------------------------------------------------
+    int i, check_config = 0;
+    int config_loaded = 0;
+    int dont_fork = 0;
+    size_t wanted_stacksize = 0, stacksize = 0;
+    pthread_attr_t attr;
+
+    // global initialization
+    get_HZ();
+
+    // set the name for logging
+    program_name = "netdata";
+
+    // parse command line.
+
+    // parse depercated options
+    // TODO: Remove this block with the next major release.
+    {
+        i = 1;
+        while(i < argc) {
+            if(strcmp(argv[i], "-pidfile") == 0 && (i+1) < argc) {
+                strncpyz(pidfile, argv[i+1], FILENAME_MAX);
+                fprintf(stderr, "%s: deprecated option -- %s -- please use -P instead.\n", argv[0], argv[i]);
+                remove_option(i, &argc, argv);
+            }
+            else if(strcmp(argv[i], "-nodaemon") == 0 || strcmp(argv[i], "-nd") == 0) {
+                dont_fork = 1;
+                fprintf(stderr, "%s: deprecated option -- %s -- please use -D instead.\n ", argv[0], argv[i]);
+                remove_option(i, &argc, argv);
+            }
+            else if(strcmp(argv[i], "-ch") == 0 && (i+1) < argc) {
+                config_set("global", "host access prefix", argv[i+1]);
+                fprintf(stderr, "%s: deprecated option -- %s -- please use -s instead.\n", argv[0], argv[i]);
+                remove_option(i, &argc, argv);
+            }
+            else if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
+                config_set("global", "history", argv[i+1]);
+                fprintf(stderr, "%s: deprecated option -- %s -- This option will be removed with V2.*.\n", argv[0], argv[i]);
+                remove_option(i, &argc, argv);
+            }
+            else i++;
+        }
+    }
+
+    // parse options
+    {
+        int num_opts = sizeof(options) / sizeof(struct option_def);
+        char optstring[(num_opts * 2) + 1];
+
+        int string_i = 0;
+        for( i = 0; i < num_opts; i++ ) {
+            optstring[string_i] = options[i].val;
+            string_i++;
+            if(options[i].arg_name) {
+                optstring[string_i] = ':';
+                string_i++;
+            }
+        }
+
+        int opt;
+        while( (opt = getopt(argc, argv, optstring)) != -1 ) {
+            switch(opt) {
+                case 'c':
+                    if(load_config(optarg, 1) != 1) {
+                        error("Cannot load configuration file %s.", optarg);
+                        exit(1);
+                    }
+                    else {
+                        debug(D_OPTIONS, "Configuration loaded from %s.", optarg);
+                        config_loaded = 1;
+                    }
+                    break;
+                case 'D':
+                    dont_fork = 1;
+                    break;
+                case 'h':
+                    help(0);
+                    break;
+                case 'i':
+                    config_set("global", "bind to", optarg);
+                    break;
+                case 'k':
+                    dont_fork = 1;
+                    check_config = 1;
+                    break;
+                case 'P':
+                    strncpy(pidfile, optarg, FILENAME_MAX);
+                    pidfile[FILENAME_MAX] = '\0';
+                    break;
+                case 'p':
+                    config_set("global", "default port", optarg);
+                    break;
+                case 's':
+                    config_set("global", "host access prefix", optarg);
+                    break;
+                case 't':
+                    config_set("global", "update every", optarg);
+                    break;
+                case 'u':
+                    config_set("global", "run as user", optarg);
+                    break;
+                case 'v':
+                    // TODO: Outsource version to makefile which can compute version from git.
+                    printf("netdata 1.2.1_master\n");
+                    return 0;
+                case 'W':
+                    {
+                        char* stacksize_string = "stacksize=";
+                        char* debug_flags_string = "debug_flags=";
+                        if(strcmp(optarg, "unittest") == 0) {
+                            rrd_update_every = 1;
+                            if(run_all_mockup_tests()) exit(1);
+                            if(unit_test_storage()) exit(1);
+                            fprintf(stderr, "\n\nALL TESTS PASSED\n\n");
+                            exit(0);
+                        } else if(strncmp(optarg, stacksize_string, strlen(stacksize_string)) == 0) {
+                            optarg += strlen(stacksize_string);
+                            config_set("global", "pthread stack size", optarg);
+                        } else if(strncmp(optarg, debug_flags_string, strlen(debug_flags_string)) == 0) {
+                            optarg += strlen(debug_flags_string);
+                            config_set("global", "debug flags",  optarg);
+                            debug_flags = strtoull(optarg, NULL, 0);
+                        }
+                    }
+                    break;
+                default: /* ? */
+                    help(1);
+                    break;
+            }
+        }
+    }
+
+    if(!config_loaded) load_config(NULL, 0);
+
+    // prepare configuration environment variables for the plugins
+    setenv("NETDATA_CONFIG_DIR" , config_get("global", "config directory"   , CONFIG_DIR) , 1);
+    setenv("NETDATA_PLUGINS_DIR", config_get("global", "plugins directory"  , PLUGINS_DIR), 1);
+    setenv("NETDATA_WEB_DIR"    , config_get("global", "web files directory", WEB_DIR)    , 1);
+    setenv("NETDATA_CACHE_DIR"  , config_get("global", "cache directory"    , CACHE_DIR)  , 1);
+    setenv("NETDATA_LIB_DIR"    , config_get("global", "lib directory"      , VARLIB_DIR) , 1);
+    setenv("NETDATA_LOG_DIR"    , config_get("global", "log directory"      , LOG_DIR)    , 1);
+    setenv("NETDATA_HOST_PREFIX", config_get("global", "host access prefix" , "")         , 1);
+    setenv("HOME"               , config_get("global", "home directory"     , CACHE_DIR)  , 1);
+
+    // 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);
+
+    {
+        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);
+    }
+
+    // cd to /tmp to avoid any plugins writing files at random places
+    if(chdir("/tmp")) error("netdata: ERROR: Cannot cd to /tmp");
+
+    char *user = NULL;
+    {
+        char *flags = config_get("global", "debug flags",  "0x00000000");
+        setenv("NETDATA_DEBUG_FLAGS", flags, 1);
+
+        debug_flags = strtoull(flags, NULL, 0);
+        debug(D_OPTIONS, "Debug flags set to '0x%8llx'.", debug_flags);
+
+        if(debug_flags != 0) {
+            struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
+            if(setrlimit(RLIMIT_CORE, &rl) != 0)
+                info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+            prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+        }
+
+        // --------------------------------------------------------------------
 
 #ifdef MADV_MERGEABLE
-               enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
+        enable_ksm = config_get_boolean("global", "memory deduplication (ksm)", enable_ksm);
 #else
 #warning "Kernel memory deduplication (KSM) is not available"
 #endif
 
-               // --------------------------------------------------------------------
-
-
-               global_host_prefix = config_get("global", "host access prefix", "");
-               setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
-
-               // --------------------------------------------------------------------
-
-               output_log_file = config_get("global", "debug log", LOG_DIR "/debug.log");
-               if(strcmp(output_log_file, "syslog") == 0) {
-                       output_log_syslog = 1;
-                       output_log_file = NULL;
-               }
-               else if(strcmp(output_log_file, "none") == 0) {
-                       output_log_syslog = 0;
-                       output_log_file = NULL;
-               }
-               else output_log_syslog = 0;
-
-               // --------------------------------------------------------------------
-
-               error_log_file = config_get("global", "error log", LOG_DIR "/error.log");
-               if(strcmp(error_log_file, "syslog") == 0) {
-                       error_log_syslog = 1;
-                       error_log_file = NULL;
-               }
-               else if(strcmp(error_log_file, "none") == 0) {
-                       error_log_syslog = 0;
-                       error_log_file = NULL;
-                       // optimization - do not even generate debug log entries
-               }
-               else error_log_syslog = 0;
-
-               error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
-               setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
-
-               error_log_errors_per_period = (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);
-
-               // --------------------------------------------------------------------
-
-               access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
-               if(strcmp(access_log_file, "syslog") == 0) {
-                       access_log_syslog = 1;
-                       access_log_file = NULL;
-               }
-               else if(strcmp(access_log_file, "none") == 0) {
-                       access_log_syslog = 0;
-                       access_log_file = NULL;
-               }
-               else access_log_syslog = 0;
-
-               // --------------------------------------------------------------------
-
-               rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
-
-               // --------------------------------------------------------------------
-
-               {
-                       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);
-               }
-
-               // --------------------------------------------------------------------
-
-               rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
-               if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
-                       info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
-                       rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
-               }
-               else {
-                       debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
-               }
-
-               // --------------------------------------------------------------------
-
-               rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
-               if(rrd_update_every < 1 || rrd_update_every > 600) {
-                       info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
-                       rrd_update_every = UPDATE_EVERY;
-               }
-               else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
-
-               // let the plugins know the min update_every
-               {
-                       char buf[16];
-                       snprintfz(buf, 15, "%d", rrd_update_every);
-                       setenv("NETDATA_UPDATE_EVERY", buf, 1);
-               }
-
-               // --------------------------------------------------------------------
-
-               // block signals while initializing threads.
-               // this causes the threads to block signals.
-               sigset_t sigset;
-               sigfillset(&sigset);
-
-               if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
-                       error("Could not block signals for threads");
-               }
-
-               // Catch signals which we want to use to quit savely
-               struct sigaction sa;
-               sigemptyset(&sa.sa_mask);
-               sigaddset(&sa.sa_mask, SIGHUP);
-               sigaddset(&sa.sa_mask, SIGINT);
-               sigaddset(&sa.sa_mask, SIGTERM);
-               sa.sa_handler = sig_handler_exit;
-               sa.sa_flags = 0;
-               if(sigaction(SIGHUP, &sa, NULL) == -1) {
-                       error("Failed to change signal handler for SIGHUP");
-               }
-               if(sigaction(SIGINT, &sa, NULL) == -1) {
-                       error("Failed to change signal handler for SIGINT");
-               }
-               if(sigaction(SIGTERM, &sa, NULL) == -1) {
-                       error("Failed to change signal handler for SIGTERM");
-               }
-
-               // save database on SIGUSR1
-               sa.sa_handler = sig_handler_save;
-               if(sigaction(SIGUSR1, &sa, NULL) == -1) {
-                       error("Failed to change signal handler for SIGUSR1");
-               }
-
-               // Ignore SIGPIPE completely.
-               // INFO: If we add signals here we have to unblock them
-               // at popen.c when running a external plugin.
-               sa.sa_handler = SIG_IGN;
-               if(sigaction(SIGPIPE, &sa, NULL) == -1) {
-                       error("Failed to change signal handler for SIGPIPE");
-               }
-
-               // --------------------------------------------------------------------
-
-               i = pthread_attr_init(&attr);
-               if(i != 0)
-                       fatal("pthread_attr_init() failed with code %d.", i);
-
-               i = pthread_attr_getstacksize(&attr, &stacksize);
-               if(i != 0)
-                       fatal("pthread_attr_getstacksize() failed with code %d.", i);
-               else
-                       debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
-
-               wanted_stacksize = config_get_number("global", "pthread stack size", stacksize);
-
-               // --------------------------------------------------------------------
-
-               for (i = 0; static_threads[i].name != NULL ; i++) {
-                       struct netdata_static_thread *st = &static_threads[i];
-
-                       if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
-                       if(st->enabled && st->init_routine) st->init_routine();
-               }
-
-               // --------------------------------------------------------------------
-
-               // get the user we should run
-               // IMPORTANT: this is required before web_files_uid()
-               user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");
-
-               // IMPORTANT: these have to run once, while single threaded
-               web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
-               web_files_gid();
-
-               // --------------------------------------------------------------------
-
-               create_listen_sockets();
-       }
-
-       // never become a problem
-       if(nice(20) == -1) error("Cannot lower my CPU priority.");
-
-       if(become_daemon(dont_fork, 0, user, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1)
-               fatal("Cannot demonize myself.");
+        // --------------------------------------------------------------------
+
+        global_host_prefix = config_get("global", "host access prefix", "");
+        setenv("NETDATA_HOST_PREFIX", global_host_prefix, 1);
+
+        // --------------------------------------------------------------------
+
+        stdout_filename    = config_get("global", "debug log",  LOG_DIR "/debug.log");
+        stderr_filename    = config_get("global", "error log",  LOG_DIR "/error.log");
+        stdaccess_filename = config_get("global", "access log", LOG_DIR "/access.log");
+
+        error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
+        setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
+
+        error_log_errors_per_period = (unsigned long)config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
+        setenv("NETDATA_ERRORS_PER_PERIOD"     , config_get("global", "errors to trigger flood protection", ""), 1);
+
+        if(check_config) {
+            stdout_filename = stderr_filename = stdaccess_filename = "system";
+            error_log_throttle_period = 0;
+            error_log_errors_per_period = 0;
+        }
+
+        // --------------------------------------------------------------------
+
+        rrd_memory_mode = rrd_memory_mode_id(config_get("global", "memory mode", rrd_memory_mode_name(rrd_memory_mode)));
+
+        // --------------------------------------------------------------------
+
+        {
+            char hostnamebuf[HOSTNAME_MAX + 1];
+            if(gethostname(hostnamebuf, HOSTNAME_MAX) == -1)
+                error("WARNING: Cannot get machine hostname.");
+            hostname = config_get("global", "hostname", hostnamebuf);
+            debug(D_OPTIONS, "hostname set to '%s'", hostname);
+        }
+
+        // --------------------------------------------------------------------
+
+        rrd_default_history_entries = (int) config_get_number("global", "history", RRD_DEFAULT_HISTORY_ENTRIES);
+        if(rrd_default_history_entries < 5 || rrd_default_history_entries > RRD_HISTORY_ENTRIES_MAX) {
+            info("Invalid save lines %d given. Defaulting to %d.", rrd_default_history_entries, RRD_DEFAULT_HISTORY_ENTRIES);
+            rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+        }
+        else {
+            debug(D_OPTIONS, "save lines set to %d.", rrd_default_history_entries);
+        }
+
+        // --------------------------------------------------------------------
+
+        rrd_update_every = (int) config_get_number("global", "update every", UPDATE_EVERY);
+        if(rrd_update_every < 1 || rrd_update_every > 600) {
+            info("Invalid update timer %d given. Defaulting to %d.", rrd_update_every, UPDATE_EVERY_MAX);
+            rrd_update_every = UPDATE_EVERY;
+        }
+        else debug(D_OPTIONS, "update timer set to %d.", rrd_update_every);
+
+        // let the plugins know the min update_every
+        {
+            char buf[16];
+            snprintfz(buf, 15, "%d", rrd_update_every);
+            setenv("NETDATA_UPDATE_EVERY", buf, 1);
+        }
+
+        // --------------------------------------------------------------------
+
+        // block signals while initializing threads.
+        // this causes the threads to block signals.
+        sigset_t sigset;
+        sigfillset(&sigset);
+
+        if(pthread_sigmask(SIG_BLOCK, &sigset, NULL) == -1) {
+            error("Could not block signals for threads");
+        }
+
+        // Catch signals which we want to use
+        struct sigaction sa;
+        sigemptyset(&sa.sa_mask);
+        sigaddset(&sa.sa_mask, SIGINT);
+        sigaddset(&sa.sa_mask, SIGTERM);
+        sa.sa_handler = sig_handler_exit;
+        sa.sa_flags = 0;
+        if(sigaction(SIGINT, &sa, NULL) == -1) {
+            error("Failed to change signal handler for SIGINT");
+        }
+        if(sigaction(SIGTERM, &sa, NULL) == -1) {
+            error("Failed to change signal handler for SIGTERM");
+        }
+
+        sigemptyset(&sa.sa_mask);
+        sigaddset(&sa.sa_mask, SIGHUP);
+        sa.sa_handler = sig_handler_logrotate;
+        sa.sa_flags = 0;
+        if(sigaction(SIGHUP, &sa, NULL) == -1) {
+            error("Failed to change signal handler for SIGHUP");
+        }
+
+        // save database on SIGUSR1
+        sigemptyset(&sa.sa_mask);
+        sigaddset(&sa.sa_mask, SIGUSR1);
+        sa.sa_handler = sig_handler_save;
+        if(sigaction(SIGUSR1, &sa, NULL) == -1) {
+            error("Failed to change signal handler for SIGUSR1");
+        }
+
+        // Ignore SIGPIPE completely.
+        // INFO: If we add signals here we have to unblock them
+        // at popen.c when running a external plugin.
+        sigemptyset(&sa.sa_mask);
+        sigaddset(&sa.sa_mask, SIGPIPE);
+        sa.sa_handler = SIG_IGN;
+        if(sigaction(SIGPIPE, &sa, NULL) == -1) {
+            error("Failed to change signal handler for SIGPIPE");
+        }
+
+        // --------------------------------------------------------------------
+
+        i = pthread_attr_init(&attr);
+        if(i != 0)
+            fatal("pthread_attr_init() failed with code %d.", i);
+
+        i = pthread_attr_getstacksize(&attr, &stacksize);
+        if(i != 0)
+            fatal("pthread_attr_getstacksize() failed with code %d.", i);
+        else
+            debug(D_OPTIONS, "initial pthread stack size is %zu bytes", stacksize);
+
+        wanted_stacksize = (size_t)config_get_number("global", "pthread stack size", (long)stacksize);
+
+        // --------------------------------------------------------------------
+
+        for (i = 0; static_threads[i].name != NULL ; i++) {
+            struct netdata_static_thread *st = &static_threads[i];
+
+            if(st->config_name) st->enabled = config_get_boolean(st->config_section, st->config_name, st->enabled);
+            if(st->enabled && st->init_routine) st->init_routine();
+        }
+
+        // --------------------------------------------------------------------
+
+        // get the user we should run
+        // IMPORTANT: this is required before web_files_uid()
+        user = config_get("global", "run as user"    , (getuid() == 0)?NETDATA_USER:"");
+
+        // IMPORTANT: these have to run once, while single threaded
+        web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
+        web_files_gid();
+
+        // --------------------------------------------------------------------
+
+        if(!check_config)
+            create_listen_sockets();
+    }
+
+    // initialize the log files
+    open_all_log_files();
 
 #ifdef NETDATA_INTERNAL_CHECKS
-       if(debug_flags != 0) {
-               struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
-               if(setrlimit(RLIMIT_CORE, &rl) != 0)
-                       info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
-               prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
-       }
+    if(debug_flags != 0) {
+        struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
+        if(setrlimit(RLIMIT_CORE, &rl) != 0)
+            info("Cannot request unlimited core dumps for debugging... Proceeding anyway...");
+        prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+    }
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-       if(output_log_syslog || error_log_syslog || access_log_syslog)
-               openlog("netdata", LOG_PID, LOG_DAEMON);
+    // fork, switch user, create pid file, set process priority
+    if(become_daemon(dont_fork, user) == -1)
+        fatal("Cannot demonize myself.");
+
+    info("NetData started on pid %d", getpid());
+
+
+    // ------------------------------------------------------------------------
+    // get default pthread stack size
 
-       info("NetData started on pid %d", getpid());
+    if(stacksize < wanted_stacksize) {
+        i = pthread_attr_setstacksize(&attr, wanted_stacksize);
+        if(i != 0)
+            fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
+        else
+            info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
+    }
 
+    // ------------------------------------------------------------------------
+    // initialize the registry
 
-       // ------------------------------------------------------------------------
-       // get default pthread stack size
+    registry_init();
 
-       if(stacksize < wanted_stacksize) {
-               i = pthread_attr_setstacksize(&attr, wanted_stacksize);
-               if(i != 0)
-                       fatal("pthread_attr_setstacksize() to %zu bytes, failed with code %d.", wanted_stacksize, i);
-               else
-                       info("Successfully set pthread stacksize to %zu bytes", wanted_stacksize);
-       }
+    // ------------------------------------------------------------------------
+    // initialize health monitoring
 
-       // --------------------------------------------------------------------
-       // initialize the registry
+    health_init();
 
-       registry_init();
+    if(check_config)
+        exit(1);
 
-       // ------------------------------------------------------------------------
-       // spawn the threads
+    // ------------------------------------------------------------------------
+    // spawn the threads
 
-       web_server_threading_selection();
+    web_server_threading_selection();
 
-       for (i = 0; static_threads[i].name != NULL ; i++) {
-               struct netdata_static_thread *st = &static_threads[i];
+    for (i = 0; static_threads[i].name != NULL ; i++) {
+        struct netdata_static_thread *st = &static_threads[i];
 
-               if(st->enabled) {
-                       st->thread = malloc(sizeof(pthread_t));
-                       if(!st->thread)
-                               fatal("Cannot allocate pthread_t memory");
+        if(st->enabled) {
+            st->thread = mallocz(sizeof(pthread_t));
 
-                       info("Starting thread %s.", st->name);
+            info("Starting thread %s.", st->name);
 
-                       if(pthread_create(st->thread, &attr, st->start_routine, NULL))
-                               error("failed to create new thread for %s.", st->name);
+            if(pthread_create(st->thread, &attr, st->start_routine, NULL))
+                error("failed to create new thread for %s.", st->name);
 
-                       else if(pthread_detach(*st->thread))
-                               error("Cannot request detach of newly created %s thread.", st->name);
-               }
-               else info("Not starting thread %s.", st->name);
-       }
+            else if(pthread_detach(*st->thread))
+                error("Cannot request detach of newly created %s thread.", st->name);
+        }
+        else info("Not starting thread %s.", st->name);
+    }
 
-       // ------------------------------------------------------------------------
-       // block signals while initializing threads.
-       sigset_t sigset;
-       sigfillset(&sigset);
+    // ------------------------------------------------------------------------
+    // block signals while initializing threads.
+    sigset_t sigset;
+    sigfillset(&sigset);
 
-       if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
-               error("Could not unblock signals for threads");
-       }
+    if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1) {
+        error("Could not unblock signals for threads");
+    }
 
-       // Handle flags set in the signal handler.
-       while(1) {
-               pause();
-               if(netdata_exit) {
-                       info("Exit main loop of netdata.");
-                       netdata_cleanup_and_exit(0);
-                       exit(0);
-               }
-       }
+    // Handle flags set in the signal handler.
+    while(1) {
+        pause();
+        if(netdata_exit) {
+            info("Exit main loop of netdata.");
+            netdata_cleanup_and_exit(0);
+            exit(0);
+        }
+    }
 }
index a41aa0c2bdcb1e01c25f1378ef82fa60a9c2763d..646827fbd919de42422a70e0869d5eb19f823a34 100644 (file)
@@ -1,25 +1,21 @@
-#include <getopt.h>
-
 #ifndef NETDATA_MAIN_H
 #define NETDATA_MAIN_H 1
 
-#include <signal.h>
-
 extern volatile sig_atomic_t netdata_exit;
 
 /**
  * This struct contains information about command line options.
  */
 struct option_def {
-       /** The option character */
-       const char val;
-       /** The name of the long option. */
-       const char *description;
-       /** Short descripton what the option does */
-       /** Name of the argument displayed in SYNOPSIS */
-       const char *arg_name;
-       /** Default value if not set */
-       const char *default_value;
+    /** The option character */
+    const char val;
+    /** The name of the long option. */
+    const char *description;
+    /** Short descripton what the option does */
+    /** Name of the argument displayed in SYNOPSIS */
+    const char *arg_name;
+    /** Default value if not set */
+    const char *default_value;
 };
 
 /**
index 379fb9a84616aefa04794ccbae238808526c707a..007d6565fa30b6d5cd1563c66d3b759ea47b18cb 100644 (file)
@@ -1,97 +1,84 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_checks.h"
 
 void *checks_main(void *ptr)
 {
-       if(ptr) { ; }
+    if(ptr) { ; }
 
-       info("CHECKS thread created with task id %d", gettid());
+    info("CHECKS thread created with task id %d", gettid());
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
 
-       unsigned long long usec = 0, susec = rrd_update_every * 1000000ULL, loop_usec = 0, total_susec = 0;
-       struct timeval now, last, loop;
+    unsigned long long usec = 0, susec = rrd_update_every * 1000000ULL, loop_usec = 0, total_susec = 0;
+    struct timeval now, last, loop;
 
-       RRDSET *check1, *check2, *check3, *apps_cpu = NULL;
+    RRDSET *check1, *check2, *check3, *apps_cpu = NULL;
 
-       check1 = rrdset_create("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
-       rrddim_add(check1, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
-       rrddim_add(check1, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    check1 = rrdset_create("netdata", "check1", NULL, "netdata", NULL, "Caller gives microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
+    rrddim_add(check1, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
+    rrddim_add(check1, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
 
-       check2 = rrdset_create("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
-       rrddim_add(check2, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
-       rrddim_add(check2, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
+    check2 = rrdset_create("netdata", "check2", NULL, "netdata", NULL, "Netdata calcs microseconds", "a million !", 99999, rrd_update_every, RRDSET_TYPE_LINE);
+    rrddim_add(check2, "absolute", NULL, -1, 1, RRDDIM_ABSOLUTE);
+    rrddim_add(check2, "incremental", NULL, 1, 1, RRDDIM_INCREMENTAL);
 
-       check3 = rrdset_create("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference", "microseconds diff", 99999, rrd_update_every, RRDSET_TYPE_LINE);
-       rrddim_add(check3, "caller", NULL, 1, 1, RRDDIM_ABSOLUTE);
-       rrddim_add(check3, "netdata", NULL, 1, 1, RRDDIM_ABSOLUTE);
-       rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    check3 = rrdset_create("netdata", "checkdt", NULL, "netdata", NULL, "Clock difference", "microseconds diff", 99999, rrd_update_every, RRDSET_TYPE_LINE);
+    rrddim_add(check3, "caller", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    rrddim_add(check3, "netdata", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    rrddim_add(check3, "apps.plugin", NULL, 1, 1, RRDDIM_ABSOLUTE);
 
-       gettimeofday(&last, NULL);
-       while(1) {
-               usleep(susec);
+    gettimeofday(&last, NULL);
+    while(1) {
+        usleep(susec);
 
-               // find the time to sleep in order to wait exactly update_every seconds
-               gettimeofday(&now, NULL);
-               loop_usec = usecdiff(&now, &last);
-               usec = loop_usec - susec;
-               debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec);
+        // find the time to sleep in order to wait exactly update_every seconds
+        gettimeofday(&now, NULL);
+        loop_usec = usec_dt(&now, &last);
+        usec = loop_usec - susec;
+        debug(D_PROCNETDEV_LOOP, "CHECK: last loop took %llu usec (worked for %llu, sleeped for %llu).", loop_usec, usec, susec);
 
-               if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
-               else susec = rrd_update_every * 1000000ULL / 2ULL;
+        if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
+        else susec = rrd_update_every * 1000000ULL / 2ULL;
 
-               // --------------------------------------------------------------------
-               // Calculate loop time
+        // --------------------------------------------------------------------
+        // Calculate loop time
 
-               last.tv_sec = now.tv_sec;
-               last.tv_usec = now.tv_usec;
-               total_susec += loop_usec;
+        last.tv_sec = now.tv_sec;
+        last.tv_usec = now.tv_usec;
+        total_susec += loop_usec;
 
-               // --------------------------------------------------------------------
-               // check chart 1
+        // --------------------------------------------------------------------
+        // check chart 1
 
-               if(check1->counter_done) rrdset_next_usec(check1, loop_usec);
-               rrddim_set(check1, "absolute", 1000000);
-               rrddim_set(check1, "incremental", total_susec);
-               rrdset_done(check1);
+        if(check1->counter_done) rrdset_next_usec(check1, loop_usec);
+        rrddim_set(check1, "absolute", 1000000);
+        rrddim_set(check1, "incremental", total_susec);
+        rrdset_done(check1);
 
-               // --------------------------------------------------------------------
-               // check chart 2
+        // --------------------------------------------------------------------
+        // check chart 2
 
-               if(check2->counter_done) rrdset_next(check2);
-               rrddim_set(check2, "absolute", 1000000);
-               rrddim_set(check2, "incremental", total_susec);
-               rrdset_done(check2);
+        if(check2->counter_done) rrdset_next(check2);
+        rrddim_set(check2, "absolute", 1000000);
+        rrddim_set(check2, "incremental", total_susec);
+        rrdset_done(check2);
 
-               // --------------------------------------------------------------------
-               // check chart 3
+        // --------------------------------------------------------------------
+        // check chart 3
 
-               if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu");
-               if(check3->counter_done) rrdset_next_usec(check3, loop_usec);
-               gettimeofday(&loop, NULL);
-               rrddim_set(check3, "caller", (long long)usecdiff(&loop, &check1->last_collected_time));
-               rrddim_set(check3, "netdata", (long long)usecdiff(&loop, &check2->last_collected_time));
-               if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long)usecdiff(&loop, &apps_cpu->last_collected_time));
-               rrdset_done(check3);
-       }
+        if(!apps_cpu) apps_cpu = rrdset_find("apps.cpu");
+        if(check3->counter_done) rrdset_next_usec(check3, loop_usec);
+        gettimeofday(&loop, NULL);
+        rrddim_set(check3, "caller", (long long) usec_dt(&loop, &check1->last_collected_time));
+        rrddim_set(check3, "netdata", (long long) usec_dt(&loop, &check2->last_collected_time));
+        if(apps_cpu) rrddim_set(check3, "apps.plugin", (long long) usec_dt(&loop, &apps_cpu->last_collected_time));
+        rrdset_done(check3);
+    }
 
-       pthread_exit(NULL);
-       return NULL;
+    pthread_exit(NULL);
+    return NULL;
 }
 
index 56c22a160187b129115c252221b2a230c8748ab7..30c6d87088157aa6122e3bf0659cb23be40190fe 100644 (file)
@@ -1,68 +1,54 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "global_statistics.h"
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_idlejitter.h"
 
 #define CPU_IDLEJITTER_SLEEP_TIME_MS 20
 
 void *cpuidlejitter_main(void *ptr)
 {
-       if(ptr) { ; }
+    if(ptr) { ; }
 
-       info("CPU Idle Jitter thread created with task id %d", gettid());
+    info("CPU Idle Jitter thread created with task id %d", gettid());
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
 
-       int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
-       if(sleep_ms <= 0) {
-               config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
-               sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
-       }
+    int sleep_ms = (int) config_get_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
+    if(sleep_ms <= 0) {
+        config_set_number("plugin:idlejitter", "loop time in ms", CPU_IDLEJITTER_SLEEP_TIME_MS);
+        sleep_ms = CPU_IDLEJITTER_SLEEP_TIME_MS;
+    }
 
-       RRDSET *st = rrdset_find("system.idlejitter");
-       if(!st) {
-               st = rrdset_create("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter", "microseconds lost/s", 9999, rrd_update_every, RRDSET_TYPE_LINE);
-               rrddim_add(st, "jitter", NULL, 1, 1, RRDDIM_ABSOLUTE);
-       }
+    RRDSET *st = rrdset_find("system.idlejitter");
+    if(!st) {
+        st = rrdset_create("system", "idlejitter", NULL, "processes", NULL, "CPU Idle Jitter", "microseconds lost/s", 9999, rrd_update_every, RRDSET_TYPE_LINE);
+        rrddim_add(st, "jitter", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    }
 
-       struct timeval before, after;
-       unsigned long long counter;
-       for(counter = 0; 1 ;counter++) {
-               unsigned long long usec = 0, susec = 0;
+    struct timeval before, after;
+    unsigned long long counter;
+    for(counter = 0; 1 ;counter++) {
+        unsigned long long usec = 0, susec = 0;
 
-               while(susec < (rrd_update_every * 1000000ULL)) {
+        while(susec < (rrd_update_every * 1000000ULL)) {
 
-                       gettimeofday(&before, NULL);
-                       usleep(sleep_ms * 1000);
-                       gettimeofday(&after, NULL);
+            gettimeofday(&before, NULL);
+            usleep(sleep_ms * 1000);
+            gettimeofday(&after, NULL);
 
-                       // calculate the time it took for a full loop
-                       usec = usecdiff(&after, &before);
-                       susec += usec;
-               }
-               usec -= (sleep_ms * 1000);
+            // calculate the time it took for a full loop
+            usec = usec_dt(&after, &before);
+            susec += usec;
+        }
+        usec -= (sleep_ms * 1000);
 
-               if(counter) rrdset_next(st);
-               rrddim_set(st, "jitter", usec);
-               rrdset_done(st);
-       }
+        if(counter) rrdset_next(st);
+        rrddim_set(st, "jitter", usec);
+        rrdset_done(st);
+    }
 
-       pthread_exit(NULL);
-       return NULL;
+    pthread_exit(NULL);
+    return NULL;
 }
 
index 6cde66e0c70f77e7e86313e419b569383a1b057a..58d667c364fcee44843e73d1033f27aa34dbc738 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
 #ifdef INTERNAL_PLUGIN_NFACCT
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <time.h>
-#include <errno.h>
-
 #include <libmnl/libmnl.h>
 #include <libnetfilter_acct/libnetfilter_acct.h>
 
-#include "main.h"
-#include "global_statistics.h"
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 struct mynfacct {
-       const char *name;
-       uint64_t pkts;
-       uint64_t bytes;
-       struct nfacct *nfacct;
+    const char *name;
+    uint64_t pkts;
+    uint64_t bytes;
+    struct nfacct *nfacct;
 };
 
 struct nfacct_list {
-       int size;
-       int len;
-       struct mynfacct data[];
+    int size;
+    int len;
+    struct mynfacct data[];
 } *nfacct_list = NULL;
 
 static int nfacct_callback(const struct nlmsghdr *nlh, void *data) {
-       if(data) {};
-
-       if(!nfacct_list || nfacct_list->len == nfacct_list->size) {
-               int size = (nfacct_list) ? nfacct_list->size : 0;
-               int len = (nfacct_list) ? nfacct_list->len : 0;
-               size++;
-
-               info("nfacct.plugin: increasing nfacct_list to size %d", size);
-
-               nfacct_list = realloc(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
-               if(!nfacct_list) {
-                       error("nfacct.plugin: cannot allocate nfacct_list.");
-                       return MNL_CB_OK;
-               }
-
-               nfacct_list->data[len].nfacct = nfacct_alloc();
-               if(!nfacct_list->data[size - 1].nfacct) {
-                       error("nfacct.plugin: nfacct_alloc() failed.");
-                       free(nfacct_list);
-                       nfacct_list = NULL;
-                       return MNL_CB_OK;
-               }
-
-               nfacct_list->size = size;
-               nfacct_list->len = len;
-       }
-
-       if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 0) {
-               error("nfacct.plugin: nfacct_nlmsg_parse_payload() failed.");
-               return MNL_CB_OK;
-       }
-
-       nfacct_list->data[nfacct_list->len].name  = nfacct_attr_get_str(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_NAME);
-       nfacct_list->data[nfacct_list->len].pkts  = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_PKTS);
-       nfacct_list->data[nfacct_list->len].bytes = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_BYTES);
-
-       nfacct_list->len++;
-       return MNL_CB_OK;
+    if(data) {};
+
+    if(!nfacct_list || nfacct_list->len == nfacct_list->size) {
+        int size = (nfacct_list) ? nfacct_list->size : 0;
+        int len = (nfacct_list) ? nfacct_list->len : 0;
+        size++;
+
+        info("nfacct.plugin: increasing nfacct_list to size %d", size);
+
+        nfacct_list = realloc(nfacct_list, sizeof(struct nfacct_list) + (sizeof(struct mynfacct) * size));
+        if(!nfacct_list) {
+            error("nfacct.plugin: cannot allocate nfacct_list.");
+            return MNL_CB_OK;
+        }
+
+        nfacct_list->data[len].nfacct = nfacct_alloc();
+        if(!nfacct_list->data[size - 1].nfacct) {
+            error("nfacct.plugin: nfacct_alloc() failed.");
+            free(nfacct_list);
+            nfacct_list = NULL;
+            return MNL_CB_OK;
+        }
+
+        nfacct_list->size = size;
+        nfacct_list->len = len;
+    }
+
+    if(nfacct_nlmsg_parse_payload(nlh, nfacct_list->data[nfacct_list->len].nfacct) < 0) {
+        error("nfacct.plugin: nfacct_nlmsg_parse_payload() failed.");
+        return MNL_CB_OK;
+    }
+
+    nfacct_list->data[nfacct_list->len].name  = nfacct_attr_get_str(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_NAME);
+    nfacct_list->data[nfacct_list->len].pkts  = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_PKTS);
+    nfacct_list->data[nfacct_list->len].bytes = nfacct_attr_get_u64(nfacct_list->data[nfacct_list->len].nfacct, NFACCT_ATTR_BYTES);
+
+    nfacct_list->len++;
+    return MNL_CB_OK;
 }
 
 void *nfacct_main(void *ptr) {
-       if(ptr) { ; }
+    if(ptr) { ; }
 
-       info("NFACCT thread created with task id %d", gettid());
+    info("NFACCT thread created with task id %d", gettid());
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("nfacct.plugin: Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("nfacct.plugin: Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("nfacct.plugin: Cannot set pthread cancel state to ENABLE.");
 
-       char buf[MNL_SOCKET_BUFFER_SIZE];
-       struct mnl_socket *nl = NULL;
-       struct nlmsghdr *nlh = NULL;
-       unsigned int seq = 0, portid = 0;
+    char buf[MNL_SOCKET_BUFFER_SIZE];
+    struct mnl_socket *nl = NULL;
+    struct nlmsghdr *nlh = NULL;
+    unsigned int seq = 0, portid = 0;
 
-       seq = time(NULL) - 1;
+    seq = time(NULL) - 1;
 
-       nl  = mnl_socket_open(NETLINK_NETFILTER);
-       if(!nl) {
-               error("nfacct.plugin: mnl_socket_open() failed");
-               pthread_exit(NULL);
-               return NULL;
-       }
+    nl  = mnl_socket_open(NETLINK_NETFILTER);
+    if(!nl) {
+        error("nfacct.plugin: mnl_socket_open() failed");
+        pthread_exit(NULL);
+        return NULL;
+    }
 
-       if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
-               mnl_socket_close(nl);
-               error("nfacct.plugin: mnl_socket_bind() failed");
-               pthread_exit(NULL);
-               return NULL;
-       }
-       portid = mnl_socket_get_portid(nl);
+    if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+        mnl_socket_close(nl);
+        error("nfacct.plugin: mnl_socket_bind() failed");
+        pthread_exit(NULL);
+        return NULL;
+    }
+    portid = mnl_socket_get_portid(nl);
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
-       struct timeval last, now;
-       unsigned long long usec = 0, susec = 0;
-       RRDSET *st = NULL;
+    struct timeval last, now;
+    unsigned long long usec = 0, susec = 0;
+    RRDSET *st = NULL;
 
-       gettimeofday(&last, NULL);
+    gettimeofday(&last, NULL);
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
-       while(1) {
-               if(unlikely(netdata_exit)) break;
+    while(1) {
+        if(unlikely(netdata_exit)) break;
 
-               seq++;
+        seq++;
 
-               nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
-               if(!nlh) {
-                       mnl_socket_close(nl);
-                       error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
-                       pthread_exit(NULL);
-                       return NULL;
-               }
+        nlh = nfacct_nlmsg_build_hdr(buf, NFNL_MSG_ACCT_GET, NLM_F_DUMP, seq);
+        if(!nlh) {
+            mnl_socket_close(nl);
+            error("nfacct.plugin: nfacct_nlmsg_build_hdr() failed");
+            pthread_exit(NULL);
+            return NULL;
+        }
 
-               if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
-                       error("nfacct.plugin: mnl_socket_send");
-                       pthread_exit(NULL);
-                       return NULL;
-               }
+        if(mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
+            error("nfacct.plugin: mnl_socket_send");
+            pthread_exit(NULL);
+            return NULL;
+        }
 
-               if(nfacct_list) nfacct_list->len = 0;
+        if(nfacct_list) nfacct_list->len = 0;
 
-               int ret;
-               while((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
-                       if((ret = mnl_cb_run(buf, ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
-               }
+        int ret;
+        while((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
+            if((ret = mnl_cb_run(buf, ret, seq, portid, nfacct_callback, NULL)) <= 0) break;
+        }
 
-               if (ret == -1) {
-                       error("nfacct.plugin: error communicating with kernel.");
-                       pthread_exit(NULL);
-                       return NULL;
-               }
+        if (ret == -1) {
+            error("nfacct.plugin: error communicating with kernel.");
+            pthread_exit(NULL);
+            return NULL;
+        }
 
-               // --------------------------------------------------------------------
+        // --------------------------------------------------------------------
 
-               gettimeofday(&now, NULL);
-               usec = usecdiff(&now, &last) - susec;
-               debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
+        gettimeofday(&now, NULL);
+        usec = usecdiff(&now, &last) - susec;
+        debug(D_NFACCT_LOOP, "nfacct.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).", usec + susec, usec, susec);
 
-               if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
-               else susec = rrd_update_every * 1000000ULL / 2ULL;
+        if(usec < (rrd_update_every * 1000000ULL / 2ULL)) susec = (rrd_update_every * 1000000ULL) - usec;
+        else susec = rrd_update_every * 1000000ULL / 2ULL;
 
 
-               // --------------------------------------------------------------------
+        // --------------------------------------------------------------------
 
-               if(nfacct_list && nfacct_list->len) {
-                       int i;
+        if(nfacct_list && nfacct_list->len) {
+            int i;
 
-                       st = rrdset_find_bytype("netfilter", "nfacct_packets");
-                       if(!st) {
-                               st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 1006, rrd_update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_find_bytype("netfilter", "nfacct_packets");
+            if(!st) {
+                st = rrdset_create("netfilter", "nfacct_packets", NULL, "nfacct", NULL, "Netfilter Accounting Packets", "packets/s", 1006, rrd_update_every, RRDSET_TYPE_STACKED);
 
-                               for(i = 0; i < nfacct_list->len ; i++)
-                                       rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
+                for(i = 0; i < nfacct_list->len ; i++)
+                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
 
-                       for(i = 0; i < nfacct_list->len ; i++) {
-                               RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
+            for(i = 0; i < nfacct_list->len ; i++) {
+                RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
 
-                               if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
-                               if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].pkts);
-                       }
+                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, rrd_update_every, RRDDIM_INCREMENTAL);
+                if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].pkts);
+            }
 
-                       rrdset_done(st);
+            rrdset_done(st);
 
-                       // ----------------------------------------------------------------
+            // ----------------------------------------------------------------
 
-                       st = rrdset_find_bytype("netfilter", "nfacct_bytes");
-                       if(!st) {
-                               st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 1007, rrd_update_every, RRDSET_TYPE_STACKED);
+            st = rrdset_find_bytype("netfilter", "nfacct_bytes");
+            if(!st) {
+                st = rrdset_create("netfilter", "nfacct_bytes", NULL, "nfacct", NULL, "Netfilter Accounting Bandwidth", "kilobytes/s", 1007, rrd_update_every, RRDSET_TYPE_STACKED);
 
-                               for(i = 0; i < nfacct_list->len ; i++)
-                                       rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
+                for(i = 0; i < nfacct_list->len ; i++)
+                    rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
 
-                       for(i = 0; i < nfacct_list->len ; i++) {
-                               RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
+            for(i = 0; i < nfacct_list->len ; i++) {
+                RRDDIM *rd = rrddim_find(st, nfacct_list->data[i].name);
 
-                               if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
-                               if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].bytes);
-                       }
+                if(!rd) rd = rrddim_add(st, nfacct_list->data[i].name, NULL, 1, 1000 * rrd_update_every, RRDDIM_INCREMENTAL);
+                if(rd) rrddim_set_by_pointer(st, rd, nfacct_list->data[i].bytes);
+            }
 
-                       rrdset_done(st);
-               }
+            rrdset_done(st);
+        }
 
-               // --------------------------------------------------------------------
+        // --------------------------------------------------------------------
 
-               usleep(susec);
+        usleep(susec);
 
-               // copy current to last
-               bcopy(&now, &last, sizeof(struct timeval));
-       }
+        // copy current to last
+        bcopy(&now, &last, sizeof(struct timeval));
+    }
 
-       mnl_socket_close(nl);
-       pthread_exit(NULL);
-       return NULL;
+    mnl_socket_close(nl);
+    pthread_exit(NULL);
+    return NULL;
 }
 #endif
index e7b8d50c87d64c2695c4b63373eb92418f496cdf..a1bf314de7c34bf4a44081763773e4a750cb7e86 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "global_statistics.h"
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "main.h"
-#include "registry.h"
 
 void *proc_main(void *ptr)
 {
-       (void)ptr;
-
-       unsigned long long old_web_requests = 0, old_web_usec = 0,
-                       old_content_size = 0, old_compressed_content_size = 0;
-
-       collected_number compression_ratio = -1, average_response_time = -1;
-
-       info("PROC Plugin thread created with task id %d", gettid());
-
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
-
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
-
-       struct rusage me, thread;
-
-       // disable (by default) various interface that are not needed
-       config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0);
-       config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0);
-
-       // when ZERO, attempt to do it
-       int vdo_proc_net_dev                    = !config_get_boolean("plugin:proc", "/proc/net/dev", 1);
-       int vdo_proc_diskstats                  = !config_get_boolean("plugin:proc", "/proc/diskstats", 1);
-       int vdo_proc_net_snmp                   = !config_get_boolean("plugin:proc", "/proc/net/snmp", 1);
-       int vdo_proc_net_snmp6                  = !config_get_boolean("plugin:proc", "/proc/net/snmp6", 1);
-       int vdo_proc_net_netstat                = !config_get_boolean("plugin:proc", "/proc/net/netstat", 1);
-       int vdo_proc_net_stat_conntrack = !config_get_boolean("plugin:proc", "/proc/net/stat/conntrack", 1);
-       int vdo_proc_net_ip_vs_stats    = !config_get_boolean("plugin:proc", "/proc/net/ip_vs/stats", 1);
-       int vdo_proc_net_stat_synproxy  = !config_get_boolean("plugin:proc", "/proc/net/stat/synproxy", 1);
-       int vdo_proc_stat                               = !config_get_boolean("plugin:proc", "/proc/stat", 1);
-       int vdo_proc_meminfo                    = !config_get_boolean("plugin:proc", "/proc/meminfo", 1);
-       int vdo_proc_vmstat                     = !config_get_boolean("plugin:proc", "/proc/vmstat", 1);
-       int vdo_proc_net_rpc_nfsd               = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfsd", 1);
-       int vdo_proc_sys_kernel_random_entropy_avail    = !config_get_boolean("plugin:proc", "/proc/sys/kernel/random/entropy_avail", 1);
-       int vdo_proc_interrupts                 = !config_get_boolean("plugin:proc", "/proc/interrupts", 1);
-       int vdo_proc_softirqs                   = !config_get_boolean("plugin:proc", "/proc/softirqs", 1);
-       int vdo_proc_loadavg                    = !config_get_boolean("plugin:proc", "/proc/loadavg", 1);
-       int vdo_sys_kernel_mm_ksm               = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1);
-       int vdo_cpu_netdata                     = !config_get_boolean("plugin:proc", "netdata server resources", 1);
-
-       // keep track of the time each module was called
-       unsigned long long sutime_proc_net_dev = 0ULL;
-       unsigned long long sutime_proc_diskstats = 0ULL;
-       unsigned long long sutime_proc_net_snmp = 0ULL;
-       unsigned long long sutime_proc_net_snmp6 = 0ULL;
-       unsigned long long sutime_proc_net_netstat = 0ULL;
-       unsigned long long sutime_proc_net_stat_conntrack = 0ULL;
-       unsigned long long sutime_proc_net_ip_vs_stats = 0ULL;
-       unsigned long long sutime_proc_net_stat_synproxy = 0ULL;
-       unsigned long long sutime_proc_stat = 0ULL;
-       unsigned long long sutime_proc_meminfo = 0ULL;
-       unsigned long long sutime_proc_vmstat = 0ULL;
-       unsigned long long sutime_proc_net_rpc_nfsd = 0ULL;
-       unsigned long long sutime_proc_sys_kernel_random_entropy_avail = 0ULL;
-       unsigned long long sutime_proc_interrupts = 0ULL;
-       unsigned long long sutime_proc_softirqs = 0ULL;
-       unsigned long long sutime_proc_loadavg = 0ULL;
-       unsigned long long sutime_sys_kernel_mm_ksm = 0ULL;
-
-       // the next time we will run - aligned properly
-       unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
-       unsigned long long sunow;
-
-       RRDSET *stcpu = NULL, *stcpu_thread = NULL, *stclients = NULL, *streqs = NULL, *stbytes = NULL, *stduration = NULL,
-                       *stcompression = NULL;
-
-       for(;1;) {
-               if(unlikely(netdata_exit)) break;
-
-               // delay until it is our time to run
-               while((sunow = timems()) < sunext)
-                       usecsleep(sunext - sunow);
-
-               // find the next time we need to run
-               while(timems() > sunext)
-                       sunext += rrd_update_every * 1000000ULL;
-
-               if(unlikely(netdata_exit)) break;
-
-               // BEGIN -- the job to be done
-
-               if(!vdo_sys_kernel_mm_ksm) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_kernel_mm_ksm().");
-
-                       sunow = timems();
-                       vdo_sys_kernel_mm_ksm = do_sys_kernel_mm_ksm(rrd_update_every, (sutime_sys_kernel_mm_ksm > 0)?sunow - sutime_sys_kernel_mm_ksm:0ULL);
-                       sutime_sys_kernel_mm_ksm = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_loadavg) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_loadavg().");
-                       sunow = timems();
-                       vdo_proc_loadavg = do_proc_loadavg(rrd_update_every, (sutime_proc_loadavg > 0)?sunow - sutime_proc_loadavg:0ULL);
-                       sutime_proc_loadavg = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_interrupts) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_interrupts().");
-                       sunow = timems();
-                       vdo_proc_interrupts = do_proc_interrupts(rrd_update_every, (sutime_proc_interrupts > 0)?sunow - sutime_proc_interrupts:0ULL);
-                       sutime_proc_interrupts = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_softirqs) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_softirqs().");
-                       sunow = timems();
-                       vdo_proc_softirqs = do_proc_softirqs(rrd_update_every, (sutime_proc_softirqs > 0)?sunow - sutime_proc_softirqs:0ULL);
-                       sutime_proc_softirqs = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_sys_kernel_random_entropy_avail) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_sys_kernel_random_entropy_avail().");
-                       sunow = timems();
-                       vdo_proc_sys_kernel_random_entropy_avail = do_proc_sys_kernel_random_entropy_avail(rrd_update_every, (sutime_proc_sys_kernel_random_entropy_avail > 0)?sunow - sutime_proc_sys_kernel_random_entropy_avail:0ULL);
-                       sutime_proc_sys_kernel_random_entropy_avail = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_dev) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_dev().");
-                       sunow = timems();
-                       vdo_proc_net_dev = do_proc_net_dev(rrd_update_every, (sutime_proc_net_dev > 0)?sunow - sutime_proc_net_dev:0ULL);
-                       sutime_proc_net_dev = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_diskstats) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_diskstats().");
-                       sunow = timems();
-                       vdo_proc_diskstats = do_proc_diskstats(rrd_update_every, (sutime_proc_diskstats > 0)?sunow - sutime_proc_diskstats:0ULL);
-                       sutime_proc_diskstats = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_snmp) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp().");
-                       sunow = timems();
-                       vdo_proc_net_snmp = do_proc_net_snmp(rrd_update_every, (sutime_proc_net_snmp > 0)?sunow - sutime_proc_net_snmp:0ULL);
-                       sutime_proc_net_snmp = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_snmp6) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp6().");
-                       sunow = timems();
-                       vdo_proc_net_snmp6 = do_proc_net_snmp6(rrd_update_every, (sutime_proc_net_snmp6 > 0)?sunow - sutime_proc_net_snmp6:0ULL);
-                       sutime_proc_net_snmp6 = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_netstat) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_netstat().");
-                       sunow = timems();
-                       vdo_proc_net_netstat = do_proc_net_netstat(rrd_update_every, (sutime_proc_net_netstat > 0)?sunow - sutime_proc_net_netstat:0ULL);
-                       sutime_proc_net_netstat = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_stat_conntrack) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_stat_conntrack().");
-                       sunow = timems();
-                       vdo_proc_net_stat_conntrack     = do_proc_net_stat_conntrack(rrd_update_every, (sutime_proc_net_stat_conntrack > 0)?sunow - sutime_proc_net_stat_conntrack:0ULL);
-                       sutime_proc_net_stat_conntrack = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_ip_vs_stats) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_ip_vs_stats().");
-                       sunow = timems();
-                       vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(rrd_update_every, (sutime_proc_net_ip_vs_stats > 0)?sunow - sutime_proc_net_ip_vs_stats:0ULL);
-                       sutime_proc_net_ip_vs_stats = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_stat_synproxy) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_stat_synproxy().");
-                       sunow = timems();
-                       vdo_proc_net_stat_synproxy = do_proc_net_stat_synproxy(rrd_update_every, (sutime_proc_net_stat_synproxy > 0)?sunow - sutime_proc_net_stat_synproxy:0ULL);
-                       sutime_proc_net_stat_synproxy = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_stat) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_stat().");
-                       sunow = timems();
-                       vdo_proc_stat = do_proc_stat(rrd_update_every, (sutime_proc_stat > 0)?sunow - sutime_proc_stat:0ULL);
-                       sutime_proc_stat = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_meminfo) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_meminfo().");
-                       sunow = timems();
-                       vdo_proc_meminfo = do_proc_meminfo(rrd_update_every, (sutime_proc_meminfo > 0)?sunow - sutime_proc_meminfo:0ULL);
-                       sutime_proc_meminfo = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_vmstat) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_vmstat().");
-                       sunow = timems();
-                       vdo_proc_vmstat = do_proc_vmstat(rrd_update_every, (sutime_proc_vmstat > 0)?sunow - sutime_proc_vmstat:0ULL);
-                       sutime_proc_vmstat = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               if(!vdo_proc_net_rpc_nfsd) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfsd().");
-                       sunow = timems();
-                       vdo_proc_net_rpc_nfsd = do_proc_net_rpc_nfsd(rrd_update_every, (sutime_proc_net_rpc_nfsd > 0)?sunow - sutime_proc_net_rpc_nfsd:0ULL);
-                       sutime_proc_net_rpc_nfsd = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
-
-               // END -- the job is done
-
-               // --------------------------------------------------------------------
-
-               if(!vdo_cpu_netdata) {
-                       getrusage(RUSAGE_THREAD, &thread);
-                       getrusage(RUSAGE_SELF, &me);
-
-                       if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_proc_cpu");
-                       if(!stcpu_thread) {
-                               stcpu_thread = rrdset_create("netdata", "plugin_proc_cpu", NULL, "proc.internal", NULL, "NetData Proc Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
-
-                               rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
-                               rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(stcpu_thread);
-
-                       rrddim_set(stcpu_thread, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
-                       rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
-                       rrdset_done(stcpu_thread);
-
-                       // ----------------------------------------------------------------
-
-                       if(!stcpu) stcpu = rrdset_find("netdata.server_cpu");
-                       if(!stcpu) {
-                               stcpu = rrdset_create("netdata", "server_cpu", NULL, "netdata", NULL, "NetData CPU usage", "milliseconds/s", 130000, rrd_update_every, RRDSET_TYPE_STACKED);
-
-                               rrddim_add(stcpu, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
-                               rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(stcpu);
-
-                       rrddim_set(stcpu, "user"  , me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec);
-                       rrddim_set(stcpu, "system", me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec);
-                       rrdset_done(stcpu);
-
-                       // ----------------------------------------------------------------
-
-                       if(!stclients) stclients = rrdset_find("netdata.clients");
-                       if(!stclients) {
-                               stclients = rrdset_create("netdata", "clients", NULL, "netdata", NULL, "NetData Web Clients", "connected clients", 130100, rrd_update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(stclients, "clients",  NULL,  1, 1, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next(stclients);
-
-                       rrddim_set(stclients, "clients", global_statistics.connected_clients);
-                       rrdset_done(stclients);
-
-                       // ----------------------------------------------------------------
-
-                       if(!streqs) streqs = rrdset_find("netdata.requests");
-                       if(!streqs) {
-                               streqs = rrdset_create("netdata", "requests", NULL, "netdata", NULL, "NetData Web Requests", "requests/s", 130200, rrd_update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(streqs, "requests",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(streqs);
-
-                       rrddim_set(streqs, "requests", global_statistics.web_requests);
-                       rrdset_done(streqs);
-
-                       // ----------------------------------------------------------------
-
-                       if(!stbytes) stbytes = rrdset_find("netdata.net");
-                       if(!stbytes) {
-                               stbytes = rrdset_create("netdata", "net", NULL, "netdata", NULL, "NetData Network Traffic", "kilobits/s", 130300, rrd_update_every, RRDSET_TYPE_AREA);
-
-                               rrddim_add(stbytes, "in",  NULL,  8, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(stbytes, "out",  NULL,  -8, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(stbytes);
-
-                       rrddim_set(stbytes, "in", global_statistics.bytes_received);
-                       rrddim_set(stbytes, "out", global_statistics.bytes_sent);
-                       rrdset_done(stbytes);
-
-                       // ----------------------------------------------------------------
-
-                       if(!stduration) stduration = rrdset_find("netdata.response_time");
-                       if(!stduration) {
-                               stduration = rrdset_create("netdata", "response_time", NULL, "netdata", NULL, "NetData Average API Response Time", "ms/request", 130400, rrd_update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(stduration, "response_time", "response time",  1, 1000, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next(stduration);
-
-                       unsigned long long gweb_usec     = global_statistics.web_usec;
-                       unsigned long long gweb_requests = global_statistics.web_requests;
-
-                       unsigned long long web_usec     = gweb_usec     - old_web_usec;
-                       unsigned long long web_requests = gweb_requests - old_web_requests;
-
-                       old_web_usec     = gweb_usec;
-                       old_web_requests = gweb_requests;
-
-                       if(web_requests)
-                               average_response_time =  web_usec / web_requests;
-
-                       if(average_response_time != -1)
-                               rrddim_set(stduration, "response_time", average_response_time);
-
-                       rrdset_done(stduration);
-
-                       // ----------------------------------------------------------------
-
-                       if(!stcompression) stcompression = rrdset_find("netdata.compression_ratio");
-                       if(!stcompression) {
-                               stcompression = rrdset_create("netdata", "compression_ratio", NULL, "netdata", NULL, "NetData API Responses Compression Savings Ratio", "percentage", 130500, rrd_update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(stcompression, "savings", NULL,  1, 1000, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next(stcompression);
-
-                       // since we don't lock here to read the global statistics
-                       // read the smaller value first
-                       unsigned long long gcompressed_content_size = global_statistics.compressed_content_size;
-                       unsigned long long gcontent_size            = global_statistics.content_size;
-
-                       unsigned long long compressed_content_size  = gcompressed_content_size - old_compressed_content_size;
-                       unsigned long long content_size             = gcontent_size            - old_content_size;
-
-                       old_compressed_content_size = gcompressed_content_size;
-                       old_content_size            = gcontent_size;
-
-                       if(content_size && content_size >= compressed_content_size)
-                               compression_ratio = ((content_size - compressed_content_size) * 100 * 1000) / content_size;
-
-                       if(compression_ratio != -1)
-                               rrddim_set(stcompression, "savings", compression_ratio);
-
-                       rrdset_done(stcompression);
-
-                       // ----------------------------------------------------------------
-
-                       registry_statistics();
-               }
-       }
-
-       pthread_exit(NULL);
-       return NULL;
+    (void)ptr;
+
+    info("PROC Plugin thread created with task id %d", gettid());
+
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
+
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
+
+    // disable (by default) various interface that are not needed
+    config_get_boolean("plugin:proc:/proc/net/dev:lo", "enabled", 0);
+    config_get_boolean("plugin:proc:/proc/net/dev:fireqos_monitor", "enabled", 0);
+
+    // when ZERO, attempt to do it
+    int vdo_proc_net_dev            = !config_get_boolean("plugin:proc", "/proc/net/dev", 1);
+    int vdo_proc_diskstats          = !config_get_boolean("plugin:proc", "/proc/diskstats", 1);
+    int vdo_proc_net_snmp           = !config_get_boolean("plugin:proc", "/proc/net/snmp", 1);
+    int vdo_proc_net_snmp6          = !config_get_boolean("plugin:proc", "/proc/net/snmp6", 1);
+    int vdo_proc_net_netstat        = !config_get_boolean("plugin:proc", "/proc/net/netstat", 1);
+    int vdo_proc_net_stat_conntrack = !config_get_boolean("plugin:proc", "/proc/net/stat/conntrack", 1);
+    int vdo_proc_net_ip_vs_stats    = !config_get_boolean("plugin:proc", "/proc/net/ip_vs/stats", 1);
+    int vdo_proc_net_stat_synproxy  = !config_get_boolean("plugin:proc", "/proc/net/stat/synproxy", 1);
+    int vdo_proc_stat               = !config_get_boolean("plugin:proc", "/proc/stat", 1);
+    int vdo_proc_meminfo            = !config_get_boolean("plugin:proc", "/proc/meminfo", 1);
+    int vdo_proc_vmstat             = !config_get_boolean("plugin:proc", "/proc/vmstat", 1);
+    int vdo_proc_net_rpc_nfsd       = !config_get_boolean("plugin:proc", "/proc/net/rpc/nfsd", 1);
+    int vdo_proc_sys_kernel_random_entropy_avail    = !config_get_boolean("plugin:proc", "/proc/sys/kernel/random/entropy_avail", 1);
+    int vdo_proc_interrupts         = !config_get_boolean("plugin:proc", "/proc/interrupts", 1);
+    int vdo_proc_softirqs           = !config_get_boolean("plugin:proc", "/proc/softirqs", 1);
+    int vdo_proc_loadavg            = !config_get_boolean("plugin:proc", "/proc/loadavg", 1);
+    int vdo_sys_kernel_mm_ksm       = !config_get_boolean("plugin:proc", "/sys/kernel/mm/ksm", 1);
+    int vdo_cpu_netdata             = !config_get_boolean("plugin:proc", "netdata server resources", 1);
+
+    // keep track of the time each module was called
+    unsigned long long sutime_proc_net_dev = 0ULL;
+    unsigned long long sutime_proc_diskstats = 0ULL;
+    unsigned long long sutime_proc_net_snmp = 0ULL;
+    unsigned long long sutime_proc_net_snmp6 = 0ULL;
+    unsigned long long sutime_proc_net_netstat = 0ULL;
+    unsigned long long sutime_proc_net_stat_conntrack = 0ULL;
+    unsigned long long sutime_proc_net_ip_vs_stats = 0ULL;
+    unsigned long long sutime_proc_net_stat_synproxy = 0ULL;
+    unsigned long long sutime_proc_stat = 0ULL;
+    unsigned long long sutime_proc_meminfo = 0ULL;
+    unsigned long long sutime_proc_vmstat = 0ULL;
+    unsigned long long sutime_proc_net_rpc_nfsd = 0ULL;
+    unsigned long long sutime_proc_sys_kernel_random_entropy_avail = 0ULL;
+    unsigned long long sutime_proc_interrupts = 0ULL;
+    unsigned long long sutime_proc_softirqs = 0ULL;
+    unsigned long long sutime_proc_loadavg = 0ULL;
+    unsigned long long sutime_sys_kernel_mm_ksm = 0ULL;
+
+    // the next time we will run - aligned properly
+    unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
+    unsigned long long sunow;
+
+    for(;1;) {
+        if(unlikely(netdata_exit)) break;
+
+        // delay until it is our time to run
+        while((sunow = time_usec()) < sunext)
+            sleep_usec(sunext - sunow);
+
+        // find the next time we need to run
+        while(time_usec() > sunext)
+            sunext += rrd_update_every * 1000000ULL;
+
+        if(unlikely(netdata_exit)) break;
+
+        // BEGIN -- the job to be done
+
+        if(!vdo_sys_kernel_mm_ksm) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_kernel_mm_ksm().");
+
+            sunow = time_usec();
+            vdo_sys_kernel_mm_ksm = do_sys_kernel_mm_ksm(rrd_update_every, (sutime_sys_kernel_mm_ksm > 0)?sunow - sutime_sys_kernel_mm_ksm:0ULL);
+            sutime_sys_kernel_mm_ksm = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_loadavg) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_loadavg().");
+            sunow = time_usec();
+            vdo_proc_loadavg = do_proc_loadavg(rrd_update_every, (sutime_proc_loadavg > 0)?sunow - sutime_proc_loadavg:0ULL);
+            sutime_proc_loadavg = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_interrupts) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_interrupts().");
+            sunow = time_usec();
+            vdo_proc_interrupts = do_proc_interrupts(rrd_update_every, (sutime_proc_interrupts > 0)?sunow - sutime_proc_interrupts:0ULL);
+            sutime_proc_interrupts = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_softirqs) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_softirqs().");
+            sunow = time_usec();
+            vdo_proc_softirqs = do_proc_softirqs(rrd_update_every, (sutime_proc_softirqs > 0)?sunow - sutime_proc_softirqs:0ULL);
+            sutime_proc_softirqs = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_sys_kernel_random_entropy_avail) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_sys_kernel_random_entropy_avail().");
+            sunow = time_usec();
+            vdo_proc_sys_kernel_random_entropy_avail = do_proc_sys_kernel_random_entropy_avail(rrd_update_every, (sutime_proc_sys_kernel_random_entropy_avail > 0)?sunow - sutime_proc_sys_kernel_random_entropy_avail:0ULL);
+            sutime_proc_sys_kernel_random_entropy_avail = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_dev) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_dev().");
+            sunow = time_usec();
+            vdo_proc_net_dev = do_proc_net_dev(rrd_update_every, (sutime_proc_net_dev > 0)?sunow - sutime_proc_net_dev:0ULL);
+            sutime_proc_net_dev = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_diskstats) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_diskstats().");
+            sunow = time_usec();
+            vdo_proc_diskstats = do_proc_diskstats(rrd_update_every, (sutime_proc_diskstats > 0)?sunow - sutime_proc_diskstats:0ULL);
+            sutime_proc_diskstats = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_snmp) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp().");
+            sunow = time_usec();
+            vdo_proc_net_snmp = do_proc_net_snmp(rrd_update_every, (sutime_proc_net_snmp > 0)?sunow - sutime_proc_net_snmp:0ULL);
+            sutime_proc_net_snmp = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_snmp6) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_snmp6().");
+            sunow = time_usec();
+            vdo_proc_net_snmp6 = do_proc_net_snmp6(rrd_update_every, (sutime_proc_net_snmp6 > 0)?sunow - sutime_proc_net_snmp6:0ULL);
+            sutime_proc_net_snmp6 = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_netstat) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_netstat().");
+            sunow = time_usec();
+            vdo_proc_net_netstat = do_proc_net_netstat(rrd_update_every, (sutime_proc_net_netstat > 0)?sunow - sutime_proc_net_netstat:0ULL);
+            sutime_proc_net_netstat = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_stat_conntrack) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_stat_conntrack().");
+            sunow = time_usec();
+            vdo_proc_net_stat_conntrack = do_proc_net_stat_conntrack(rrd_update_every, (sutime_proc_net_stat_conntrack > 0)?sunow - sutime_proc_net_stat_conntrack:0ULL);
+            sutime_proc_net_stat_conntrack = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_ip_vs_stats) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_ip_vs_stats().");
+            sunow = time_usec();
+            vdo_proc_net_ip_vs_stats = do_proc_net_ip_vs_stats(rrd_update_every, (sutime_proc_net_ip_vs_stats > 0)?sunow - sutime_proc_net_ip_vs_stats:0ULL);
+            sutime_proc_net_ip_vs_stats = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_stat_synproxy) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_net_stat_synproxy().");
+            sunow = time_usec();
+            vdo_proc_net_stat_synproxy = do_proc_net_stat_synproxy(rrd_update_every, (sutime_proc_net_stat_synproxy > 0)?sunow - sutime_proc_net_stat_synproxy:0ULL);
+            sutime_proc_net_stat_synproxy = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_stat) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_stat().");
+            sunow = time_usec();
+            vdo_proc_stat = do_proc_stat(rrd_update_every, (sutime_proc_stat > 0)?sunow - sutime_proc_stat:0ULL);
+            sutime_proc_stat = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_meminfo) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_meminfo().");
+            sunow = time_usec();
+            vdo_proc_meminfo = do_proc_meminfo(rrd_update_every, (sutime_proc_meminfo > 0)?sunow - sutime_proc_meminfo:0ULL);
+            sutime_proc_meminfo = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_vmstat) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling vdo_proc_vmstat().");
+            sunow = time_usec();
+            vdo_proc_vmstat = do_proc_vmstat(rrd_update_every, (sutime_proc_vmstat > 0)?sunow - sutime_proc_vmstat:0ULL);
+            sutime_proc_vmstat = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        if(!vdo_proc_net_rpc_nfsd) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_proc_net_rpc_nfsd().");
+            sunow = time_usec();
+            vdo_proc_net_rpc_nfsd = do_proc_net_rpc_nfsd(rrd_update_every, (sutime_proc_net_rpc_nfsd > 0)?sunow - sutime_proc_net_rpc_nfsd:0ULL);
+            sutime_proc_net_rpc_nfsd = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
+
+        // END -- the job is done
+
+        // --------------------------------------------------------------------
+
+        if(!vdo_cpu_netdata) {
+            global_statistics_charts();
+            registry_statistics();
+        }
+    }
+
+    pthread_exit(NULL);
+    return NULL;
 }
index 251cfeeb4922e6b9ebc89b7089b1b2c3985a37d2..7d521818ed50133be8ab96e766375c9f6375c7c0 100644 (file)
@@ -1,23 +1,7 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-
-#include "avl.h"
-#include "log.h"
 #include "common.h"
-#include "appconfig.h"
-#include "rrd.h"
-#include "popen.h"
-#include "plugin_tc.h"
-#include "main.h"
 
-#define RRD_TYPE_TC                                    "tc"
-#define RRD_TYPE_TC_LEN                                strlen(RRD_TYPE_TC)
+#define RRD_TYPE_TC                 "tc"
+#define RRD_TYPE_TC_LEN             strlen(RRD_TYPE_TC)
 
 // ----------------------------------------------------------------------------
 // /sbin/tc processor
 #define TC_LINE_MAX 1024
 
 struct tc_class {
-       avl avl;
-
-       char *id;
-       uint32_t hash;
-
-       char *name;
-
-       char *leafid;
-       uint32_t leaf_hash;
-
-       char *parentid;
-       uint32_t parent_hash;
-
-       char hasparent;
-       char isleaf;
-       unsigned long long bytes;
-       unsigned long long packets;
-       unsigned long long dropped;
-       unsigned long long overlimits;
-       unsigned long long requeues;
-       unsigned long long lended;
-       unsigned long long borrowed;
-       unsigned long long giants;
-       unsigned long long tokens;
-       unsigned long long ctokens;
-
-       RRDDIM *rd_bytes;
-       RRDDIM *rd_packets;
-       RRDDIM *rd_dropped;
-
-       char name_updated;
-       char updated;   // updated bytes
-       int seen;               // seen in the tc list (even without bytes)
-
-       struct tc_class *next;
-       struct tc_class *prev;
+    avl avl;
+
+    char *id;
+    uint32_t hash;
+
+    char *name;
+
+    char *leafid;
+    uint32_t leaf_hash;
+
+    char *parentid;
+    uint32_t parent_hash;
+
+    char hasparent;
+    char isleaf;
+    unsigned long long bytes;
+    unsigned long long packets;
+    unsigned long long dropped;
+    unsigned long long overlimits;
+    unsigned long long requeues;
+    unsigned long long lended;
+    unsigned long long borrowed;
+    unsigned long long giants;
+    unsigned long long tokens;
+    unsigned long long ctokens;
+
+    RRDDIM *rd_bytes;
+    RRDDIM *rd_packets;
+    RRDDIM *rd_dropped;
+
+    char name_updated;
+    char updated;   // updated bytes
+    int seen;       // seen in the tc list (even without bytes)
+
+    struct tc_class *next;
+    struct tc_class *prev;
 };
 
 struct tc_device {
-       avl avl;
+    avl avl;
 
-       char *id;
-       uint32_t hash;
+    char *id;
+    uint32_t hash;
 
-       char *name;
-       char *family;
+    char *name;
+    char *family;
 
-       char name_updated;
-       char family_updated;
+    char name_updated;
+    char family_updated;
 
-       char enabled;
-       char enabled_bytes;
-       char enabled_packets;
-       char enabled_dropped;
+    char enabled;
+    char enabled_bytes;
+    char enabled_packets;
+    char enabled_dropped;
 
-       RRDSET *st_bytes;
-       RRDSET *st_packets;
-       RRDSET *st_dropped;
+    RRDSET *st_bytes;
+    RRDSET *st_packets;
+    RRDSET *st_dropped;
 
-       avl_tree classes_index;
+    avl_tree classes_index;
 
-       struct tc_class *classes;
+    struct tc_class *classes;
 
-       struct tc_device *next;
-       struct tc_device *prev;
+    struct tc_device *next;
+    struct tc_device *prev;
 };
 
 
@@ -100,25 +84,25 @@ struct tc_device *tc_device_root = NULL;
 // tc_device index
 
 static int tc_device_compare(void* a, void* b) {
-       if(((struct tc_device *)a)->hash < ((struct tc_device *)b)->hash) return -1;
-       else if(((struct tc_device *)a)->hash > ((struct tc_device *)b)->hash) return 1;
-       else return strcmp(((struct tc_device *)a)->id, ((struct tc_device *)b)->id);
+    if(((struct tc_device *)a)->hash < ((struct tc_device *)b)->hash) return -1;
+    else if(((struct tc_device *)a)->hash > ((struct tc_device *)b)->hash) return 1;
+    else return strcmp(((struct tc_device *)a)->id, ((struct tc_device *)b)->id);
 }
 
 avl_tree tc_device_root_index = {
-               NULL,
-               tc_device_compare
+        NULL,
+        tc_device_compare
 };
 
 #define tc_device_index_add(st) avl_insert(&tc_device_root_index, (avl *)(st))
 #define tc_device_index_del(st) avl_remove(&tc_device_root_index, (avl *)(st))
 
 static inline struct tc_device *tc_device_index_find(const char *id, uint32_t hash) {
-       struct tc_device tmp;
-       tmp.id = (char *)id;
-       tmp.hash = (hash)?hash:simple_hash(tmp.id);
+    struct tc_device tmp;
+    tmp.id = (char *)id;
+    tmp.hash = (hash)?hash:simple_hash(tmp.id);
 
-       return (struct tc_device *)avl_search(&(tc_device_root_index), (avl *)&tmp);
+    return (struct tc_device *)avl_search(&(tc_device_root_index), (avl *)&tmp);
 }
 
 
@@ -126,787 +110,764 @@ static inline struct tc_device *tc_device_index_find(const char *id, uint32_t ha
 // tc_class index
 
 static int tc_class_compare(void* a, void* b) {
-       if(((struct tc_class *)a)->hash < ((struct tc_class *)b)->hash) return -1;
-       else if(((struct tc_class *)a)->hash > ((struct tc_class *)b)->hash) return 1;
-       else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id);
+    if(((struct tc_class *)a)->hash < ((struct tc_class *)b)->hash) return -1;
+    else if(((struct tc_class *)a)->hash > ((struct tc_class *)b)->hash) return 1;
+    else return strcmp(((struct tc_class *)a)->id, ((struct tc_class *)b)->id);
 }
 
 #define tc_class_index_add(st, rd) avl_insert(&((st)->classes_index), (avl *)(rd))
 #define tc_class_index_del(st, rd) avl_remove(&((st)->classes_index), (avl *)(rd))
 
 static inline struct tc_class *tc_class_index_find(struct tc_device *st, const char *id, uint32_t hash) {
-       struct tc_class tmp;
-       tmp.id = (char *)id;
-       tmp.hash = (hash)?hash:simple_hash(tmp.id);
+    struct tc_class tmp;
+    tmp.id = (char *)id;
+    tmp.hash = (hash)?hash:simple_hash(tmp.id);
 
-       return (struct tc_class *)avl_search(&(st->classes_index), (avl *) &tmp);
+    return (struct tc_class *)avl_search(&(st->classes_index), (avl *) &tmp);
 }
 
 // ----------------------------------------------------------------------------
 
 static inline void tc_class_free(struct tc_device *n, struct tc_class *c) {
-       debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen);
-
-       if(c->next) c->next->prev = c->prev;
-       if(c->prev) c->prev->next = c->next;
-       if(n->classes == c) {
-               if(c->next) n->classes = c->next;
-               else n->classes = c->prev;
-       }
-
-       tc_class_index_del(n, c);
-
-       if(c->id) free(c->id);
-       if(c->name) free(c->name);
-       if(c->leafid) free(c->leafid);
-       if(c->parentid) free(c->parentid);
-
-       free(c);
+    debug(D_TC_LOOP, "Removing from device '%s' class '%s', parentid '%s', leafid '%s', seen=%d", n->id, c->id, c->parentid?c->parentid:"", c->leafid?c->leafid:"", c->seen);
+
+    if(c->next) c->next->prev = c->prev;
+    if(c->prev) c->prev->next = c->next;
+    if(n->classes == c) {
+        if(c->next) n->classes = c->next;
+        else n->classes = c->prev;
+    }
+
+    tc_class_index_del(n, c);
+
+    freez(c->id);
+    freez(c->name);
+    freez(c->leafid);
+    freez(c->parentid);
+    freez(c);
 }
 
 static inline void tc_device_classes_cleanup(struct tc_device *d) {
-       static int cleanup_every = 999;
-
-       if(unlikely(cleanup_every > 0)) {
-               cleanup_every = (int) config_get_number("plugin:tc", "cleanup unused classes every", 60);
-               if(cleanup_every < 0) cleanup_every = -cleanup_every;
-       }
-
-       d->name_updated = 0;
-       d->family_updated = 0;
-
-       struct tc_class *c = d->classes;
-       while(c) {
-               if(unlikely(cleanup_every > 0 && c->seen >= cleanup_every)) {
-                       struct tc_class *nc = c->next;
-                       tc_class_free(d, c);
-                       c = nc;
-               }
-               else {
-                       c->updated = 0;
-                       c->name_updated = 0;
-
-                       c = c->next;
-               }
-       }
+    static int cleanup_every = 999;
+
+    if(unlikely(cleanup_every > 0)) {
+        cleanup_every = (int) config_get_number("plugin:tc", "cleanup unused classes every", 60);
+        if(cleanup_every < 0) cleanup_every = -cleanup_every;
+    }
+
+    d->name_updated = 0;
+    d->family_updated = 0;
+
+    struct tc_class *c = d->classes;
+    while(c) {
+        if(unlikely(cleanup_every > 0 && c->seen >= cleanup_every)) {
+            struct tc_class *nc = c->next;
+            tc_class_free(d, c);
+            c = nc;
+        }
+        else {
+            c->updated = 0;
+            c->name_updated = 0;
+
+            c = c->next;
+        }
+    }
 }
 
 static inline void tc_device_commit(struct tc_device *d) {
-       static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1;
-
-       if(unlikely(enable_new_interfaces == -1)) {
-               enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES);
-               enable_bytes          = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-               enable_packets        = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-               enable_dropped        = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       }
-
-       // we only need to add leaf classes
-       struct tc_class *c, *x;
-       unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0;
-       int active_classes = 0;
-
-       // set all classes
-       for(c = d->classes ; c ; c = c->next) {
-               c->isleaf = 1;
-               c->hasparent = 0;
-       }
-
-       // mark the classes as leafs and parents
-       for(c = d->classes ; c ; c = c->next) {
-               if(unlikely(!c->updated)) continue;
-
-               for(x = d->classes ; x ; x = x->next) {
-                       if(unlikely(!x->updated)) continue;
-
-                       if(unlikely(c == x)) continue;
-
-                       if(x->parentid && (
-                               (               c->hash      == x->parent_hash && strcmp(c->id,     x->parentid) == 0) ||
-                               (c->leafid   && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) {
-                               // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id);
-                               c->isleaf = 0;
-                               x->hasparent = 1;
-                       }
-               }
-       }
-
-       // debugging:
-       /*
-       for ( c = d->classes ; c ; c = c->next) {
-               if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
-               else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
-       }
-       */
-
-       // we need at least a class
-       for(c = d->classes ; c ; c = c->next) {
-               // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen);
-               if(!c->updated) continue;
-               if(c->isleaf && c->hasparent) {
-                       active_classes++;
-                       bytes_sum += c->bytes;
-                       packets_sum += c->packets;
-                       dropped_sum += c->dropped;
-               }
-       }
-
-       if(unlikely(!active_classes)) {
-               debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id);
-               tc_device_classes_cleanup(d);
-               return;
-       }
-
-       if(unlikely(d->enabled == -1)) {
-               char var_name[CONFIG_MAX_NAME + 1];
-               snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
-               d->enabled         = config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces);
-
-               snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id);
-               d->enabled_bytes   = config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes);
-
-               snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id);
-               d->enabled_packets = config_get_boolean_ondemand("plugin:tc", var_name, enable_packets);
-
-               snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id);
-               d->enabled_dropped = config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped);
-       }
-
-       if(likely(d->enabled)) {
-               // --------------------------------------------------------------------
-               // bytes
-
-               if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) {
-                       d->enabled_bytes = CONFIG_ONDEMAND_YES;
-
-                       if(unlikely(!d->st_bytes)) {
-                               d->st_bytes = rrdset_find_bytype(RRD_TYPE_TC, d->id);
-                               if(unlikely(!d->st_bytes)) {
-                                       debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id);
-                                       d->st_bytes = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED);
-                               }
-                       }
-                       else {
-                               debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id);
-                               rrdset_next_plugins(d->st_bytes);
-
-                               if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) {
-                                       rrdset_set_name(d->st_bytes, d->name);
-                                       d->name_updated = 0;
-                               }
-
-                               // FIXME
-                               // update the family
-                       }
-
-                       for(c = d->classes ; c ; c = c->next) {
-                               if(unlikely(!c->updated)) continue;
-
-                               if(c->isleaf && c->hasparent) {
-                                       c->seen++;
-
-                                       if(unlikely(!c->rd_bytes)) {
-                                               c->rd_bytes = rrddim_find(d->st_bytes, c->id);
-                                               if(unlikely(!c->rd_bytes)) {
-                                                       debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_bytes->id, c->id, c->name);
-
-                                                       // new class, we have to add it
-                                                       c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL);
-                                               }
-                                               else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_bytes->id, c->id);
-                                       }
-
-                                       rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes);
-
-                                       // if it has a name, different to the id
-                                       if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
-                                               // update the rrd dimension with the new name
-                                               debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_bytes->id, c->rd_bytes->id, c->name);
-                                               rrddim_set_name(d->st_bytes, c->rd_bytes, c->name);
-                                       }
-                               }
-                       }
-                       rrdset_done(d->st_bytes);
-               }
-
-               // --------------------------------------------------------------------
-               // packets
-               
-               if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) {
-                       d->enabled_packets = CONFIG_ONDEMAND_YES;
-
-                       if(unlikely(!d->st_packets)) {
-                               char id[RRD_ID_LENGTH_MAX + 1];
-                               char name[RRD_ID_LENGTH_MAX + 1];
-                               snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id);
-                               snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id);
-
-                               d->st_packets = rrdset_find_bytype(RRD_TYPE_TC, id);
-                               if(unlikely(!d->st_packets)) {
-                                       debug(D_TC_LOOP, "TC: Creating new _packets chart for device '%s'", d->name?d->name:d->id);
-                                       d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, RRDSET_TYPE_STACKED);
-                               }
-                       }
-                       else {
-                               debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id);
-                               rrdset_next_plugins(d->st_packets);
-
-                               // FIXME
-                               // update the family
-                       }
-
-                       for(c = d->classes ; c ; c = c->next) {
-                               if(unlikely(!c->updated)) continue;
-
-                               if(c->isleaf && c->hasparent) {
-                                       if(unlikely(!c->rd_packets)) {
-                                               c->rd_packets = rrddim_find(d->st_packets, c->id);
-                                               if(unlikely(!c->rd_packets)) {
-                                                       debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_packets->id, c->id, c->name);
-
-                                                       // new class, we have to add it
-                                                       c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
-                                               }
-                                               else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_packets->id, c->id);
-                                       }
-
-                                       rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets);
-
-                                       // if it has a name, different to the id
-                                       if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
-                                               // update the rrd dimension with the new name
-                                               debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_packets->id, c->rd_packets->id, c->name);
-                                               rrddim_set_name(d->st_packets, c->rd_packets, c->name);
-                                       }
-                               }
-                       }
-                       rrdset_done(d->st_packets);
-               }
-
-               // --------------------------------------------------------------------
-               // dropped
-               
-               if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) {
-                       d->enabled_dropped = CONFIG_ONDEMAND_YES;
-                       
-                       if(unlikely(!d->st_dropped)) {
-                               char id[RRD_ID_LENGTH_MAX + 1];
-                               char name[RRD_ID_LENGTH_MAX + 1];
-                               snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id);
-                               snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id);
-
-                               d->st_dropped = rrdset_find_bytype(RRD_TYPE_TC, id);
-                               if(unlikely(!d->st_dropped)) {
-                                       debug(D_TC_LOOP, "TC: Creating new _dropped chart for device '%s'", d->name?d->name:d->id);
-                                       d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, RRDSET_TYPE_STACKED);
-                               }
-                       }
-                       else {
-                               debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id);
-                               rrdset_next_plugins(d->st_dropped);
-
-                               // FIXME
-                               // update the family
-                       }
-
-                       for(c = d->classes ; c ; c = c->next) {
-                               if(unlikely(!c->updated)) continue;
-
-                               if(c->isleaf && c->hasparent) {
-                                       if(unlikely(!c->rd_dropped)) {
-                                               c->rd_dropped = rrddim_find(d->st_dropped, c->id);
-                                               if(unlikely(!c->rd_dropped)) {
-                                                       debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_dropped->id, c->id, c->name);
-
-                                                       // new class, we have to add it
-                                                       c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
-                                               }
-                                               else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_dropped->id, c->id);
-                                       }
-
-                                       rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped);
-
-                                       // if it has a name, different to the id
-                                       if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
-                                               // update the rrd dimension with the new name
-                                               debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_dropped->id, c->rd_dropped->id, c->name);
-                                               rrddim_set_name(d->st_dropped, c->rd_dropped, c->name);
-                                       }
-                               }
-                       }
-                       rrdset_done(d->st_dropped);
-               }
-       }
-
-       tc_device_classes_cleanup(d);
+    static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1;
+
+    if(unlikely(enable_new_interfaces == -1)) {
+        enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES);
+        enable_bytes          = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+        enable_packets        = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+        enable_dropped        = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    }
+
+    // we only need to add leaf classes
+    struct tc_class *c, *x;
+    unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0;
+    int active_classes = 0;
+
+    // set all classes
+    for(c = d->classes ; c ; c = c->next) {
+        c->isleaf = 1;
+        c->hasparent = 0;
+    }
+
+    // mark the classes as leafs and parents
+    for(c = d->classes ; c ; c = c->next) {
+        if(unlikely(!c->updated)) continue;
+
+        for(x = d->classes ; x ; x = x->next) {
+            if(unlikely(!x->updated)) continue;
+
+            if(unlikely(c == x)) continue;
+
+            if(x->parentid && (
+                (               c->hash      == x->parent_hash && strcmp(c->id,     x->parentid) == 0) ||
+                (c->leafid   && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) {
+                // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id);
+                c->isleaf = 0;
+                x->hasparent = 1;
+            }
+        }
+    }
+
+    // debugging:
+    /*
+    for ( c = d->classes ; c ; c = c->next) {
+        if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
+        else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
+    }
+    */
+
+    // we need at least a class
+    for(c = d->classes ; c ; c = c->next) {
+        // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen);
+        if(!c->updated) continue;
+        if(c->isleaf && c->hasparent) {
+            active_classes++;
+            bytes_sum += c->bytes;
+            packets_sum += c->packets;
+            dropped_sum += c->dropped;
+        }
+    }
+
+    if(unlikely(!active_classes)) {
+        debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id);
+        tc_device_classes_cleanup(d);
+        return;
+    }
+
+    if(unlikely(d->enabled == -1)) {
+        char var_name[CONFIG_MAX_NAME + 1];
+        snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id);
+        d->enabled         = config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces);
+
+        snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id);
+        d->enabled_bytes   = config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes);
+
+        snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id);
+        d->enabled_packets = config_get_boolean_ondemand("plugin:tc", var_name, enable_packets);
+
+        snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id);
+        d->enabled_dropped = config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped);
+    }
+
+    if(likely(d->enabled)) {
+        // --------------------------------------------------------------------
+        // bytes
+
+        if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) {
+            d->enabled_bytes = CONFIG_ONDEMAND_YES;
+
+            if(unlikely(!d->st_bytes)) {
+                d->st_bytes = rrdset_find_bytype(RRD_TYPE_TC, d->id);
+                if(unlikely(!d->st_bytes)) {
+                    debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id);
+                    d->st_bytes = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED);
+                }
+            }
+            else {
+                debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id);
+                rrdset_next_plugins(d->st_bytes);
+
+                if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) {
+                    rrdset_set_name(d->st_bytes, d->name);
+                    d->name_updated = 0;
+                }
+
+                // FIXME
+                // update the family
+            }
+
+            for(c = d->classes ; c ; c = c->next) {
+                if(unlikely(!c->updated)) continue;
+
+                if(c->isleaf && c->hasparent) {
+                    c->seen++;
+
+                    if(unlikely(!c->rd_bytes)) {
+                        c->rd_bytes = rrddim_find(d->st_bytes, c->id);
+                        if(unlikely(!c->rd_bytes)) {
+                            debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_bytes->id, c->id, c->name);
+
+                            // new class, we have to add it
+                            c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL);
+                        }
+                        else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_bytes->id, c->id);
+                    }
+
+                    rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes);
+
+                    // if it has a name, different to the id
+                    if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+                        // update the rrd dimension with the new name
+                        debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_bytes->id, c->rd_bytes->id, c->name);
+                        rrddim_set_name(d->st_bytes, c->rd_bytes, c->name);
+                    }
+                }
+            }
+            rrdset_done(d->st_bytes);
+        }
+
+        // --------------------------------------------------------------------
+        // packets
+        
+        if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) {
+            d->enabled_packets = CONFIG_ONDEMAND_YES;
+
+            if(unlikely(!d->st_packets)) {
+                char id[RRD_ID_LENGTH_MAX + 1];
+                char name[RRD_ID_LENGTH_MAX + 1];
+                snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id);
+                snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id);
+
+                d->st_packets = rrdset_find_bytype(RRD_TYPE_TC, id);
+                if(unlikely(!d->st_packets)) {
+                    debug(D_TC_LOOP, "TC: Creating new _packets chart for device '%s'", d->name?d->name:d->id);
+                    d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, RRDSET_TYPE_STACKED);
+                }
+            }
+            else {
+                debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id);
+                rrdset_next_plugins(d->st_packets);
+
+                // FIXME
+                // update the family
+            }
+
+            for(c = d->classes ; c ; c = c->next) {
+                if(unlikely(!c->updated)) continue;
+
+                if(c->isleaf && c->hasparent) {
+                    if(unlikely(!c->rd_packets)) {
+                        c->rd_packets = rrddim_find(d->st_packets, c->id);
+                        if(unlikely(!c->rd_packets)) {
+                            debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_packets->id, c->id, c->name);
+
+                            // new class, we have to add it
+                            c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
+                        }
+                        else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_packets->id, c->id);
+                    }
+
+                    rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets);
+
+                    // if it has a name, different to the id
+                    if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+                        // update the rrd dimension with the new name
+                        debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_packets->id, c->rd_packets->id, c->name);
+                        rrddim_set_name(d->st_packets, c->rd_packets, c->name);
+                    }
+                }
+            }
+            rrdset_done(d->st_packets);
+        }
+
+        // --------------------------------------------------------------------
+        // dropped
+        
+        if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) {
+            d->enabled_dropped = CONFIG_ONDEMAND_YES;
+            
+            if(unlikely(!d->st_dropped)) {
+                char id[RRD_ID_LENGTH_MAX + 1];
+                char name[RRD_ID_LENGTH_MAX + 1];
+                snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id);
+                snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id);
+
+                d->st_dropped = rrdset_find_bytype(RRD_TYPE_TC, id);
+                if(unlikely(!d->st_dropped)) {
+                    debug(D_TC_LOOP, "TC: Creating new _dropped chart for device '%s'", d->name?d->name:d->id);
+                    d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, RRDSET_TYPE_STACKED);
+                }
+            }
+            else {
+                debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id);
+                rrdset_next_plugins(d->st_dropped);
+
+                // FIXME
+                // update the family
+            }
+
+            for(c = d->classes ; c ; c = c->next) {
+                if(unlikely(!c->updated)) continue;
+
+                if(c->isleaf && c->hasparent) {
+                    if(unlikely(!c->rd_dropped)) {
+                        c->rd_dropped = rrddim_find(d->st_dropped, c->id);
+                        if(unlikely(!c->rd_dropped)) {
+                            debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_dropped->id, c->id, c->name);
+
+                            // new class, we have to add it
+                            c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL);
+                        }
+                        else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_dropped->id, c->id);
+                    }
+
+                    rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped);
+
+                    // if it has a name, different to the id
+                    if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) {
+                        // update the rrd dimension with the new name
+                        debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_dropped->id, c->rd_dropped->id, c->name);
+                        rrddim_set_name(d->st_dropped, c->rd_dropped, c->name);
+                    }
+                }
+            }
+            rrdset_done(d->st_dropped);
+        }
+    }
+
+    tc_device_classes_cleanup(d);
 }
 
 static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name)
 {
-       struct tc_class *c = tc_class_index_find(d, id, 0);
-       if(likely(c)) {
-               if(likely(c->name))
-                       free(c->name);
-
-               c->name = NULL;
-
-               if(likely(name && *name && strcmp(c->id, name) != 0)) {
-                       debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name);
-                       c->name = strdup(name);
-                       if(likely(c->name))
-                               c->name_updated = 1;
-               }
-       }
+    struct tc_class *c = tc_class_index_find(d, id, 0);
+    if(likely(c)) {
+        freez(c->name);
+        c->name = NULL;
+
+        if(likely(name && *name && strcmp(c->id, name) != 0)) {
+            debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name);
+            c->name = strdupz(name);
+            c->name_updated = 1;
+        }
+    }
 }
 
 static inline void tc_device_set_device_name(struct tc_device *d, char *name) {
-       if(likely(d->name))
-               free(d->name);
-
-       d->name = NULL;
-
-       if(likely(name && *name && strcmp(d->id, name) != 0)) {
-               debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name);
-               d->name = strdup(name);
-               if(likely(d->name))
-                       d->name_updated = 1;
-       }
+    freez(d->name);
+    d->name = NULL;
+
+    if(likely(name && *name && strcmp(d->id, name) != 0)) {
+        debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name);
+        d->name = strdupz(name);
+        d->name_updated = 1;
+    }
 }
 
 static inline void tc_device_set_device_family(struct tc_device *d, char *family) {
-       if(likely(d->family))
-               free(d->family);
-
-       d->family = NULL;
-
-       if(likely(family && *family && strcmp(d->id, family) != 0)) {
-               debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family);
-               d->family = strdup(family);
-               if(likely(d->family))
-                       d->family_updated = 1;
-       }
-       // no need for null termination - it is already null
+    freez(d->family);
+    d->family = NULL;
+
+    if(likely(family && *family && strcmp(d->id, family) != 0)) {
+        debug(D_TC_LOOP, "TC: Setting device '%s' family to '%s'", d->id, family);
+        d->family = strdupz(family);
+        d->family_updated = 1;
+    }
+    // no need for null termination - it is already null
 }
 
 static inline struct tc_device *tc_device_create(char *id)
 {
-       struct tc_device *d = tc_device_index_find(id, 0);
-
-       if(!d) {
-               debug(D_TC_LOOP, "TC: Creating device '%s'", id);
-
-               d = calloc(1, sizeof(struct tc_device));
-               if(!d) {
-                       fatal("Cannot allocate memory for tc_device %s", id);
-                       return NULL;
-               }
-
-               d->id = strdup(id);
-               d->hash = simple_hash(d->id);
-               d->enabled = -1;
-
-               avl_init(&d->classes_index, tc_class_compare);
-               tc_device_index_add(d);
-
-               if(!tc_device_root) {
-                       tc_device_root = d;
-               }
-               else {
-                       d->next = tc_device_root;
-                       tc_device_root->prev = d;
-                       tc_device_root = d;
-               }
-       }
-
-       return(d);
+    struct tc_device *d = tc_device_index_find(id, 0);
+
+    if(!d) {
+        debug(D_TC_LOOP, "TC: Creating device '%s'", id);
+
+        d = callocz(1, sizeof(struct tc_device));
+
+        d->id = strdupz(id);
+        d->hash = simple_hash(d->id);
+        d->enabled = -1;
+
+        avl_init(&d->classes_index, tc_class_compare);
+        tc_device_index_add(d);
+
+        if(!tc_device_root) {
+            tc_device_root = d;
+        }
+        else {
+            d->next = tc_device_root;
+            tc_device_root->prev = d;
+            tc_device_root = d;
+        }
+    }
+
+    return(d);
 }
 
 static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid)
 {
-       struct tc_class *c = tc_class_index_find(n, id, 0);
+    struct tc_class *c = tc_class_index_find(n, id, 0);
 
-       if(!c) {
-               debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:"");
+    if(!c) {
+        debug(D_TC_LOOP, "TC: Creating in device '%s', class id '%s', parentid '%s', leafid '%s'", n->id, id, parentid?parentid:"", leafid?leafid:"");
 
-               c = calloc(1, sizeof(struct tc_class));
-               if(!c) {
-                       fatal("Cannot allocate memory for tc class");
-                       return NULL;
-               }
+        c = callocz(1, sizeof(struct tc_class));
 
-               if(n->classes) n->classes->prev = c;
-               c->next = n->classes;
-               n->classes = c;
+        if(n->classes) n->classes->prev = c;
+        c->next = n->classes;
+        n->classes = c;
 
-               c->id = strdup(id);
-               if(!c->id) {
-                       free(c);
-                       return NULL;
-               }
-               c->hash = simple_hash(c->id);
+        c->id = strdupz(id);
+        c->hash = simple_hash(c->id);
 
-               if(parentid && *parentid) {
-                       c->parentid = strdup(parentid);
-                       c->parent_hash = simple_hash(c->parentid);
-               }
+        if(parentid && *parentid) {
+            c->parentid = strdupz(parentid);
+            c->parent_hash = simple_hash(c->parentid);
+        }
 
-               if(leafid && *leafid) {
-                       c->leafid = strdup(leafid);
-                       c->leaf_hash = simple_hash(c->leafid);
-               }
+        if(leafid && *leafid) {
+            c->leafid = strdupz(leafid);
+            c->leaf_hash = simple_hash(c->leafid);
+        }
 
-               tc_class_index_add(n, c);
-       }
+        tc_class_index_add(n, c);
+    }
 
-       c->seen = 1;
+    c->seen = 1;
 
-       return(c);
+    return(c);
 }
 
 static inline void tc_device_free(struct tc_device *n)
 {
-       if(n->next) n->next->prev = n->prev;
-       if(n->prev) n->prev->next = n->next;
-       if(tc_device_root == n) {
-               if(n->next) tc_device_root = n->next;
-               else tc_device_root = n->prev;
-       }
-
-       tc_device_index_del(n);
+    if(n->next) n->next->prev = n->prev;
+    if(n->prev) n->prev->next = n->next;
+    if(tc_device_root == n) {
+        if(n->next) tc_device_root = n->next;
+        else tc_device_root = n->prev;
+    }
 
-       while(n->classes) tc_class_free(n, n->classes);
+    tc_device_index_del(n);
 
-       if(n->id) free(n->id);
-       if(n->name) free(n->name);
-       if(n->family) free(n->family);
+    while(n->classes) tc_class_free(n, n->classes);
 
-       free(n);
+    freez(n->id);
+    freez(n->name);
+    freez(n->family);
+    freez(n);
 }
 
 static inline void tc_device_free_all()
 {
-       while(tc_device_root)
-               tc_device_free(tc_device_root);
+    while(tc_device_root)
+        tc_device_free(tc_device_root);
 }
 
 #define MAX_WORDS 20
 
 static inline int tc_space(char c) {
-       switch(c) {
-       case ' ':
-       case '\t':
-       case '\r':
-       case '\n':
-               return 1;
-
-       default:
-               return 0;
-       }
+    switch(c) {
+    case ' ':
+    case '\t':
+    case '\r':
+    case '\n':
+        return 1;
+
+    default:
+        return 0;
+    }
 }
 
 static inline void tc_split_words(char *str, char **words, int max_words) {
-       char *s = str;
-       int i = 0;
+    char *s = str;
+    int i = 0;
 
-       // skip all white space
-       while(tc_space(*s)) s++;
+    // skip all white space
+    while(tc_space(*s)) s++;
 
-       // store the first word
-       words[i++] = s;
+    // store the first word
+    words[i++] = s;
 
-       // while we have something
-       while(*s) {
-               // if it is a space
-               if(unlikely(tc_space(*s))) {
+    // while we have something
+    while(*s) {
+        // if it is a space
+        if(unlikely(tc_space(*s))) {
 
-                       // terminate the word
-                       *s++ = '\0';
+            // terminate the word
+            *s++ = '\0';
 
-                       // skip all white space
-                       while(tc_space(*s)) s++;
+            // skip all white space
+            while(tc_space(*s)) s++;
 
-                       // if we reached the end, stop
-                       if(!*s) break;
+            // if we reached the end, stop
+            if(!*s) break;
 
-                       // store the next word
-                       if(i < max_words) words[i++] = s;
-                       else break;
-               }
-               else s++;
-       }
+            // store the next word
+            if(i < max_words) words[i++] = s;
+            else break;
+        }
+        else s++;
+    }
 
-       // terminate the words
-       while(i < max_words) words[i++] = NULL;
+    // terminate the words
+    while(i < max_words) words[i++] = NULL;
 }
 
 pid_t tc_child_pid = 0;
 void *tc_main(void *ptr) {
-       (void)ptr;
+    (void)ptr;
 
-       info("TC thread created with task id %d", gettid());
+    info("TC thread created with task id %d", gettid());
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
 
-       struct rusage thread;
-       RRDSET *stcpu = NULL, *sttime = NULL;
+    struct rusage thread;
+    RRDSET *stcpu = NULL, *sttime = NULL;
 
-       char buffer[TC_LINE_MAX+1] = "";
-       char *words[MAX_WORDS] = { NULL };
+    char buffer[TC_LINE_MAX+1] = "";
+    char *words[MAX_WORDS] = { NULL };
 
-       uint32_t BEGIN_HASH = simple_hash("BEGIN");
-       uint32_t END_HASH = simple_hash("END");
-       uint32_t CLASS_HASH = simple_hash("class");
-       uint32_t SENT_HASH = simple_hash("Sent");
-       uint32_t LENDED_HASH = simple_hash("lended:");
-       uint32_t TOKENS_HASH = simple_hash("tokens:");
-       uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME");
-       uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP");
-       uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME");
-       uint32_t WORKTIME_HASH = simple_hash("WORKTIME");
+    uint32_t BEGIN_HASH = simple_hash("BEGIN");
+    uint32_t END_HASH = simple_hash("END");
+    uint32_t CLASS_HASH = simple_hash("class");
+    uint32_t SENT_HASH = simple_hash("Sent");
+    uint32_t LENDED_HASH = simple_hash("lended:");
+    uint32_t TOKENS_HASH = simple_hash("tokens:");
+    uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME");
+    uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP");
+    uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME");
+    uint32_t WORKTIME_HASH = simple_hash("WORKTIME");
 #ifdef DETACH_PLUGINS_FROM_NETDATA
-       uint32_t MYPID_HASH = simple_hash("MYPID");
+    uint32_t MYPID_HASH = simple_hash("MYPID");
 #endif
-       uint32_t first_hash;
-
-       snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", config_get("plugins", "plugins directory", PLUGINS_DIR));
-       char *tc_script = config_get("plugin:tc", "script to run to get tc values", buffer);
-       
-       for(;1;) {
-               if(unlikely(netdata_exit)) break;
-
-               FILE *fp;
-               struct tc_device *device = NULL;
-               struct tc_class *class = NULL;
-
-               snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every);
-               debug(D_TC_LOOP, "executing '%s'", buffer);
-
-               fp = mypopen(buffer, &tc_child_pid);
-               if(unlikely(!fp)) {
-                       error("TC: Cannot popen(\"%s\", \"r\").", buffer);
-                       pthread_exit(NULL);
-                       return NULL;
-               }
-
-               while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
-                       if(unlikely(netdata_exit)) break;
-
-                       buffer[TC_LINE_MAX] = '\0';
-                       // debug(D_TC_LOOP, "TC: read '%s'", buffer);
-
-                       tc_split_words(buffer, words, MAX_WORDS);
-
-                       if(unlikely(!words[0] || !*words[0])) {
-                               // debug(D_TC_LOOP, "empty line");
-                               continue;
-                       }
-                       // else debug(D_TC_LOOP, "First word is '%s'", words[0]);
-
-                       first_hash = simple_hash(words[0]);
-
-                       if(unlikely(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0)) {
-                               // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]);
-
-                               // words[1] : class type
-                               // words[2] : N:XX
-                               // words[3] : parent or root
-                               if(likely(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0))) {
-                                       //char *type     = words[1];  // the class: htb, fq_codel, etc
-
-                                       // we are only interested for HTB classes
-                                       //if(strcmp(type, "htb") != 0) continue;
-
-                                       char *id       = words[2];      // the class major:minor
-                                       char *parent   = words[3];      // 'parent' or 'root'
-                                       char *parentid = words[4];      // the parent's id
-                                       char *leaf     = words[5];      // 'leaf'
-                                       char *leafid   = words[6];      // leafid
-
-                                       if(strcmp(parent, "root") == 0) {
-                                               parentid = NULL;
-                                               leafid = NULL;
-                                       }
-                                       else if(!leaf || strcmp(leaf, "leaf") != 0)
-                                               leafid = NULL;
-
-                                       char leafbuf[20 + 1] = "";
-                                       if(leafid && leafid[strlen(leafid) - 1] == ':') {
-                                               strncpyz(leafbuf, leafid, 20 - 1);
-                                               strcat(leafbuf, "1");
-                                               leafid = leafbuf;
-                                       }
-
-                                       class = tc_class_add(device, id, parentid, leafid);
-                               }
-                               else {
-                                       // clear the last class
-                                       class = NULL;
-                               }
-                       }
-                       else if(unlikely(first_hash == END_HASH && strcmp(words[0], "END") == 0)) {
-                               // debug(D_TC_LOOP, "END line");
-
-                               if(likely(device)) {
-                                       if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
-                                               error("Cannot set pthread cancel state to DISABLE.");
-
-                                       tc_device_commit(device);
-                                       // tc_device_free(device);
-
-                                       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-                                               error("Cannot set pthread cancel state to ENABLE.");
-                               }
-
-                               device = NULL;
-                               class = NULL;
-                       }
-                       else if(unlikely(first_hash == BEGIN_HASH && strcmp(words[0], "BEGIN") == 0)) {
-                               // debug(D_TC_LOOP, "BEGIN line on device '%s'", words[1]);
-
-                               if(likely(words[1] && *words[1])) {
-                                       device = tc_device_create(words[1]);
-                               }
-                               else {
-                                       // tc_device_free(device);
-                                       device = NULL;
-                               }
-
-                               class = NULL;
-                       }
-                       else if(unlikely(device && class && first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0)) {
-                               // debug(D_TC_LOOP, "SENT line '%s'", words[1]);
-                               if(likely(words[1] && *words[1])) {
-                                       class->bytes = strtoull(words[1], NULL, 10);
-                                       class->updated = 1;
-                               }
-                               else {
-                                       class->updated = 0;
-                               }
-
-                               if(likely(words[3] && *words[3]))
-                                       class->packets = strtoull(words[3], NULL, 10);
-
-                               if(likely(words[6] && *words[6]))
-                                       class->dropped = strtoull(words[6], NULL, 10);
-
-                               if(likely(words[8] && *words[8]))
-                                       class->overlimits = strtoull(words[8], NULL, 10);
-
-                               if(likely(words[10] && *words[10]))
-                                       class->requeues = strtoull(words[8], NULL, 10);
-                       }
-                       else if(unlikely(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0)) {
-                               // debug(D_TC_LOOP, "LENDED line '%s'", words[1]);
-                               if(likely(words[1] && *words[1]))
-                                       class->lended = strtoull(words[1], NULL, 10);
-
-                               if(likely(words[3] && *words[3]))
-                                       class->borrowed = strtoull(words[3], NULL, 10);
-
-                               if(likely(words[5] && *words[5]))
-                                       class->giants = strtoull(words[5], NULL, 10);
-                       }
-                       else if(unlikely(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0)) {
-                               // debug(D_TC_LOOP, "TOKENS line '%s'", words[1]);
-                               if(likely(words[1] && *words[1]))
-                                       class->tokens = strtoull(words[1], NULL, 10);
-
-                               if(likely(words[3] && *words[3]))
-                                       class->ctokens = strtoull(words[3], NULL, 10);
-                       }
-                       else if(unlikely(device && first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0)) {
-                               // debug(D_TC_LOOP, "SETDEVICENAME line '%s'", words[1]);
-                               if(likely(words[1] && *words[1]))
-                                       tc_device_set_device_name(device, words[1]);
-                       }
-                       else if(unlikely(device && first_hash == SETDEVICEGROUP_HASH && strcmp(words[0], "SETDEVICEGROUP") == 0)) {
-                               // debug(D_TC_LOOP, "SETDEVICEGROUP line '%s'", words[1]);
-                               if(likely(words[1] && *words[1]))
-                                       tc_device_set_device_family(device, words[1]);
-                       }
-                       else if(unlikely(device && first_hash == SETCLASSNAME_HASH && strcmp(words[0], "SETCLASSNAME") == 0)) {
-                               // debug(D_TC_LOOP, "SETCLASSNAME line '%s' '%s'", words[1], words[2]);
-                               char *id    = words[1];
-                               char *path  = words[2];
-                               if(likely(id && *id && path && *path))
-                                       tc_device_set_class_name(device, id, path);
-                       }
-                       else if(unlikely(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0)) {
-                               // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]);
-                               getrusage(RUSAGE_THREAD, &thread);
-
-                               if(unlikely(!stcpu)) stcpu = rrdset_find("netdata.plugin_tc_cpu");
-                               if(unlikely(!stcpu)) {
-                                       stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED);
-                                       rrddim_add(stcpu, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
-                                       rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(stcpu);
-
-                               rrddim_set(stcpu, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
-                               rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
-                               rrdset_done(stcpu);
-
-                               if(unlikely(!sttime)) stcpu = rrdset_find("netdata.plugin_tc_time");
-                               if(unlikely(!sttime)) {
-                                       sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA);
-                                       rrddim_add(sttime, "run_time",  "run time",  1, 1, RRDDIM_ABSOLUTE);
-                               }
-                               else rrdset_next(sttime);
-
-                               rrddim_set(sttime, "run_time", atoll(words[1]));
-                               rrdset_done(sttime);
-
-                       }
+    uint32_t first_hash;
+
+    snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", config_get("plugins", "plugins directory", PLUGINS_DIR));
+    char *tc_script = config_get("plugin:tc", "script to run to get tc values", buffer);
+    
+    for(;1;) {
+        if(unlikely(netdata_exit)) break;
+
+        FILE *fp;
+        struct tc_device *device = NULL;
+        struct tc_class *class = NULL;
+
+        snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every);
+        debug(D_TC_LOOP, "executing '%s'", buffer);
+
+        fp = mypopen(buffer, &tc_child_pid);
+        if(unlikely(!fp)) {
+            error("TC: Cannot popen(\"%s\", \"r\").", buffer);
+            pthread_exit(NULL);
+            return NULL;
+        }
+
+        while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
+            if(unlikely(netdata_exit)) break;
+
+            buffer[TC_LINE_MAX] = '\0';
+            // debug(D_TC_LOOP, "TC: read '%s'", buffer);
+
+            tc_split_words(buffer, words, MAX_WORDS);
+
+            if(unlikely(!words[0] || !*words[0])) {
+                // debug(D_TC_LOOP, "empty line");
+                continue;
+            }
+            // else debug(D_TC_LOOP, "First word is '%s'", words[0]);
+
+            first_hash = simple_hash(words[0]);
+
+            if(unlikely(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0)) {
+                // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]);
+
+                // words[1] : class type
+                // words[2] : N:XX
+                // words[3] : parent or root
+                if(likely(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0))) {
+                    //char *type     = words[1];  // the class: htb, fq_codel, etc
+
+                    // we are only interested for HTB classes
+                    //if(strcmp(type, "htb") != 0) continue;
+
+                    char *id       = words[2];  // the class major:minor
+                    char *parent   = words[3];  // 'parent' or 'root'
+                    char *parentid = words[4];  // the parent's id
+                    char *leaf     = words[5];  // 'leaf'
+                    char *leafid   = words[6];  // leafid
+
+                    if(strcmp(parent, "root") == 0) {
+                        parentid = NULL;
+                        leafid = NULL;
+                    }
+                    else if(!leaf || strcmp(leaf, "leaf") != 0)
+                        leafid = NULL;
+
+                    char leafbuf[20 + 1] = "";
+                    if(leafid && leafid[strlen(leafid) - 1] == ':') {
+                        strncpyz(leafbuf, leafid, 20 - 1);
+                        strcat(leafbuf, "1");
+                        leafid = leafbuf;
+                    }
+
+                    class = tc_class_add(device, id, parentid, leafid);
+                }
+                else {
+                    // clear the last class
+                    class = NULL;
+                }
+            }
+            else if(unlikely(first_hash == END_HASH && strcmp(words[0], "END") == 0)) {
+                // debug(D_TC_LOOP, "END line");
+
+                if(likely(device)) {
+                    if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
+                        error("Cannot set pthread cancel state to DISABLE.");
+
+                    tc_device_commit(device);
+                    // tc_device_free(device);
+
+                    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+                        error("Cannot set pthread cancel state to ENABLE.");
+                }
+
+                device = NULL;
+                class = NULL;
+            }
+            else if(unlikely(first_hash == BEGIN_HASH && strcmp(words[0], "BEGIN") == 0)) {
+                // debug(D_TC_LOOP, "BEGIN line on device '%s'", words[1]);
+
+                if(likely(words[1] && *words[1])) {
+                    device = tc_device_create(words[1]);
+                }
+                else {
+                    // tc_device_free(device);
+                    device = NULL;
+                }
+
+                class = NULL;
+            }
+            else if(unlikely(device && class && first_hash == SENT_HASH && strcmp(words[0], "Sent") == 0)) {
+                // debug(D_TC_LOOP, "SENT line '%s'", words[1]);
+                if(likely(words[1] && *words[1])) {
+                    class->bytes = strtoull(words[1], NULL, 10);
+                    class->updated = 1;
+                }
+                else {
+                    class->updated = 0;
+                }
+
+                if(likely(words[3] && *words[3]))
+                    class->packets = strtoull(words[3], NULL, 10);
+
+                if(likely(words[6] && *words[6]))
+                    class->dropped = strtoull(words[6], NULL, 10);
+
+                if(likely(words[8] && *words[8]))
+                    class->overlimits = strtoull(words[8], NULL, 10);
+
+                if(likely(words[10] && *words[10]))
+                    class->requeues = strtoull(words[8], NULL, 10);
+            }
+            else if(unlikely(device && class && class->updated && first_hash == LENDED_HASH && strcmp(words[0], "lended:") == 0)) {
+                // debug(D_TC_LOOP, "LENDED line '%s'", words[1]);
+                if(likely(words[1] && *words[1]))
+                    class->lended = strtoull(words[1], NULL, 10);
+
+                if(likely(words[3] && *words[3]))
+                    class->borrowed = strtoull(words[3], NULL, 10);
+
+                if(likely(words[5] && *words[5]))
+                    class->giants = strtoull(words[5], NULL, 10);
+            }
+            else if(unlikely(device && class && class->updated && first_hash == TOKENS_HASH && strcmp(words[0], "tokens:") == 0)) {
+                // debug(D_TC_LOOP, "TOKENS line '%s'", words[1]);
+                if(likely(words[1] && *words[1]))
+                    class->tokens = strtoull(words[1], NULL, 10);
+
+                if(likely(words[3] && *words[3]))
+                    class->ctokens = strtoull(words[3], NULL, 10);
+            }
+            else if(unlikely(device && first_hash == SETDEVICENAME_HASH && strcmp(words[0], "SETDEVICENAME") == 0)) {
+                // debug(D_TC_LOOP, "SETDEVICENAME line '%s'", words[1]);
+                if(likely(words[1] && *words[1]))
+                    tc_device_set_device_name(device, words[1]);
+            }
+            else if(unlikely(device && first_hash == SETDEVICEGROUP_HASH && strcmp(words[0], "SETDEVICEGROUP") == 0)) {
+                // debug(D_TC_LOOP, "SETDEVICEGROUP line '%s'", words[1]);
+                if(likely(words[1] && *words[1]))
+                    tc_device_set_device_family(device, words[1]);
+            }
+            else if(unlikely(device && first_hash == SETCLASSNAME_HASH && strcmp(words[0], "SETCLASSNAME") == 0)) {
+                // debug(D_TC_LOOP, "SETCLASSNAME line '%s' '%s'", words[1], words[2]);
+                char *id    = words[1];
+                char *path  = words[2];
+                if(likely(id && *id && path && *path))
+                    tc_device_set_class_name(device, id, path);
+            }
+            else if(unlikely(first_hash == WORKTIME_HASH && strcmp(words[0], "WORKTIME") == 0)) {
+                // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]);
+                getrusage(RUSAGE_THREAD, &thread);
+
+                if(unlikely(!stcpu)) stcpu = rrdset_find("netdata.plugin_tc_cpu");
+                if(unlikely(!stcpu)) {
+                    stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED);
+                    rrddim_add(stcpu, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
+                    rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(stcpu);
+
+                rrddim_set(stcpu, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
+                rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
+                rrdset_done(stcpu);
+
+                if(unlikely(!sttime)) stcpu = rrdset_find("netdata.plugin_tc_time");
+                if(unlikely(!sttime)) {
+                    sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA);
+                    rrddim_add(sttime, "run_time",  "run time",  1, 1, RRDDIM_ABSOLUTE);
+                }
+                else rrdset_next(sttime);
+
+                rrddim_set(sttime, "run_time", atoll(words[1]));
+                rrdset_done(sttime);
+
+            }
 #ifdef DETACH_PLUGINS_FROM_NETDATA
-                       else if(unlikely(first_hash == MYPID_HASH && (strcmp(words[0], "MYPID") == 0))) {
-                               // debug(D_TC_LOOP, "MYPID line '%s'", words[1]);
-                               char *id = words[1];
-                               pid_t pid = atol(id);
+            else if(unlikely(first_hash == MYPID_HASH && (strcmp(words[0], "MYPID") == 0))) {
+                // debug(D_TC_LOOP, "MYPID line '%s'", words[1]);
+                char *id = words[1];
+                pid_t pid = atol(id);
 
-                               if(likely(pid)) tc_child_pid = pid;
+                if(likely(pid)) tc_child_pid = pid;
 
-                               debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
-                       }
+                debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
+            }
 #endif
-                       //else {
-                       //      debug(D_TC_LOOP, "IGNORED line");
-                       //}
-               }
-
-               // fgets() failed or loop broke
-               int code = mypclose(fp, tc_child_pid);
-               tc_child_pid = 0;
-
-               if(unlikely(device)) {
-                       // tc_device_free(device);
-                       device = NULL;
-                       class = NULL;
-               }
-
-               if(unlikely(netdata_exit)) {
-                       tc_device_free_all();
-                       pthread_exit(NULL);
-                       return NULL;
-               }
-
-               if(code == 1 || code == 127) {
-                       // 1 = DISABLE
-                       // 127 = cannot even run it
-                       error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code);
-
-                       tc_device_free_all();
-                       pthread_exit(NULL);
-                       return NULL;
-               }
-
-               sleep((unsigned int) rrd_update_every);
-       }
-
-       pthread_exit(NULL);
-       return NULL;
+            //else {
+            //  debug(D_TC_LOOP, "IGNORED line");
+            //}
+        }
+
+        // fgets() failed or loop broke
+        int code = mypclose(fp, tc_child_pid);
+        tc_child_pid = 0;
+
+        if(unlikely(device)) {
+            // tc_device_free(device);
+            device = NULL;
+            class = NULL;
+        }
+
+        if(unlikely(netdata_exit)) {
+            tc_device_free_all();
+            pthread_exit(NULL);
+            return NULL;
+        }
+
+        if(code == 1 || code == 127) {
+            // 1 = DISABLE
+            // 127 = cannot even run it
+            error("TC: tc-qos-helper.sh exited with code %d. Disabling it.", code);
+
+            tc_device_free_all();
+            pthread_exit(NULL);
+            return NULL;
+        }
+
+        sleep((unsigned int) rrd_update_every);
+    }
+
+    pthread_exit(NULL);
+    return NULL;
 }
index 23757bbe7d9f8af448045f90fdff798f2df2fa68..0f3b8bc6450cd7fbfb296a4a7f991bd85e7c5852 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <sys/types.h>
-#include <dirent.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-
-#include "main.h"
 #include "common.h"
-#include "appconfig.h"
-#include "log.h"
-#include "rrd.h"
-#include "popen.h"
-#include "plugins_d.h"
-#include "../config.h"
 
 struct plugind *pluginsd_root = NULL;
 
 #define MAX_WORDS 20
 
 static inline int pluginsd_space(char c) {
-       switch(c) {
-       case ' ':
-       case '\t':
-       case '\r':
-       case '\n':
-       case '=':
-               return 1;
-
-       default:
-               return 0;
-       }
+    switch(c) {
+    case ' ':
+    case '\t':
+    case '\r':
+    case '\n':
+    case '=':
+        return 1;
+
+    default:
+        return 0;
+    }
 }
 
 static int pluginsd_split_words(char *str, char **words, int max_words) {
-       char *s = str, quote = 0;
-       int i = 0, j;
-
-       // skip all white space
-       while(unlikely(pluginsd_space(*s))) s++;
-
-       // check for quote
-       if(unlikely(*s == '\'' || *s == '"')) {
-               quote = *s;     // remember the quote
-               s++;            // skip the quote
-       }
-
-       // store the first word
-       words[i++] = s;
-
-       // while we have something
-       while(likely(*s)) {
-               // if it is escape
-               if(unlikely(*s == '\\' && s[1])) {
-                       s += 2;
-                       continue;
-               }
-
-               // if it is quote
-               else if(unlikely(*s == quote)) {
-                       quote = 0;
-                       *s = ' ';
-                       continue;
-               }
-
-               // if it is a space
-               else if(unlikely(quote == 0 && pluginsd_space(*s))) {
-
-                       // terminate the word
-                       *s++ = '\0';
-
-                       // skip all white space
-                       while(likely(pluginsd_space(*s))) s++;
-
-                       // check for quote
-                       if(unlikely(*s == '\'' || *s == '"')) {
-                               quote = *s;     // remember the quote
-                               s++;            // skip the quote
-                       }
-
-                       // if we reached the end, stop
-                       if(unlikely(!*s)) break;
-
-                       // store the next word
-                       if(likely(i < max_words)) words[i++] = s;
-                       else break;
-               }
-
-               // anything else
-               else s++;
-       }
-
-       // terminate the words
-       j = i;
-       while(likely(j < max_words)) words[j++] = NULL;
-
-       return i;
+    char *s = str, quote = 0;
+    int i = 0, j;
+
+    // skip all white space
+    while(unlikely(pluginsd_space(*s))) s++;
+
+    // check for quote
+    if(unlikely(*s == '\'' || *s == '"')) {
+        quote = *s; // remember the quote
+        s++;        // skip the quote
+    }
+
+    // store the first word
+    words[i++] = s;
+
+    // while we have something
+    while(likely(*s)) {
+        // if it is escape
+        if(unlikely(*s == '\\' && s[1])) {
+            s += 2;
+            continue;
+        }
+
+        // if it is quote
+        else if(unlikely(*s == quote)) {
+            quote = 0;
+            *s = ' ';
+            continue;
+        }
+
+        // if it is a space
+        else if(unlikely(quote == 0 && pluginsd_space(*s))) {
+
+            // terminate the word
+            *s++ = '\0';
+
+            // skip all white space
+            while(likely(pluginsd_space(*s))) s++;
+
+            // check for quote
+            if(unlikely(*s == '\'' || *s == '"')) {
+                quote = *s; // remember the quote
+                s++;        // skip the quote
+            }
+
+            // if we reached the end, stop
+            if(unlikely(!*s)) break;
+
+            // store the next word
+            if(likely(i < max_words)) words[i++] = s;
+            else break;
+        }
+
+        // anything else
+        else s++;
+    }
+
+    // terminate the words
+    j = i;
+    while(likely(j < max_words)) words[j++] = NULL;
+
+    return i;
 }
 
 
 void *pluginsd_worker_thread(void *arg)
 {
-       struct plugind *cd = (struct plugind *)arg;
-       char line[PLUGINSD_LINE_MAX + 1];
+    struct plugind *cd = (struct plugind *)arg;
+    char line[PLUGINSD_LINE_MAX + 1];
 
 #ifdef DETACH_PLUGINS_FROM_NETDATA
-       unsigned long long usec = 0, susec = 0;
-       struct timeval last = {0, 0} , now = {0, 0};
+    unsigned long long usec = 0, susec = 0;
+    struct timeval last = {0, 0} , now = {0, 0};
 #endif
 
-       char *words[MAX_WORDS] = { NULL };
-       uint32_t SET_HASH = simple_hash("SET");
-       uint32_t BEGIN_HASH = simple_hash("BEGIN");
-       uint32_t END_HASH = simple_hash("END");
-       uint32_t FLUSH_HASH = simple_hash("FLUSH");
-       uint32_t CHART_HASH = simple_hash("CHART");
-       uint32_t DIMENSION_HASH = simple_hash("DIMENSION");
-       uint32_t DISABLE_HASH = simple_hash("DISABLE");
+    char *words[MAX_WORDS] = { NULL };
+    uint32_t SET_HASH = simple_hash("SET");
+    uint32_t BEGIN_HASH = simple_hash("BEGIN");
+    uint32_t END_HASH = simple_hash("END");
+    uint32_t FLUSH_HASH = simple_hash("FLUSH");
+    uint32_t CHART_HASH = simple_hash("CHART");
+    uint32_t DIMENSION_HASH = simple_hash("DIMENSION");
+    uint32_t DISABLE_HASH = simple_hash("DISABLE");
 #ifdef DETACH_PLUGINS_FROM_NETDATA
-       uint32_t MYPID_HASH = simple_hash("MYPID");
-       uint32_t STOPPING_WAKE_ME_UP_PLEASE_HASH = simple_hash("STOPPING_WAKE_ME_UP_PLEASE");
+    uint32_t MYPID_HASH = simple_hash("MYPID");
+    uint32_t STOPPING_WAKE_ME_UP_PLEASE_HASH = simple_hash("STOPPING_WAKE_ME_UP_PLEASE");
 #endif
 
-       size_t count = 0;
-
-       while(likely(1)) {
-               if(unlikely(netdata_exit)) break;
-
-               FILE *fp = mypopen(cd->cmd, &cd->pid);
-               if(unlikely(!fp)) {
-                       error("Cannot popen(\"%s\", \"r\").", cd->cmd);
-                       break;
-               }
-
-               info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid);
-
-               RRDSET *st = NULL;
-               char *s;
-               uint32_t hash;
-
-               while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) {
-                       if(unlikely(netdata_exit)) break;
-
-                       line[PLUGINSD_LINE_MAX] = '\0';
-
-                       // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
-
-                       int w = pluginsd_split_words(line, words, MAX_WORDS);
-                       s = words[0];
-                       if(unlikely(!s || !*s || !w)) {
-                               // debug(D_PLUGINSD, "PLUGINSD: empty line");
-                               continue;
-                       }
-
-                       // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
-
-                       hash = simple_hash(s);
-
-                       if(likely(hash == SET_HASH && !strcmp(s, "SET"))) {
-                               char *dimension = words[1];
-                               char *value = words[2];
-
-                               if(unlikely(!dimension || !*dimension)) {
-                                       error("PLUGINSD: '%s' is requesting a SET on chart '%s', without a dimension. Disabling it.", cd->fullfilename, st->id);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               if(unlikely(!value || !*value)) value = NULL;
-
-                               if(unlikely(!st)) {
-                                       error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>");
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>");
-
-                               if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0));
-                       }
-                       else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
-                               char *id = words[1];
-                               char *microseconds_txt = words[2];
-
-                               if(unlikely(!id)) {
-                                       error("PLUGINSD: '%s' is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               st = rrdset_find(id);
-                               if(unlikely(!st)) {
-                                       error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               if(likely(st->counter_done)) {
-                                       unsigned long long microseconds = 0;
-                                       if(microseconds_txt && *microseconds_txt) microseconds = strtoull(microseconds_txt, NULL, 10);
-                                       if(microseconds) rrdset_next_usec(st, microseconds);
-                                       else rrdset_next_plugins(st);
-                               }
-                       }
-                       else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
-                               if(unlikely(!st)) {
-                                       error("PLUGINSD: '%s' is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id);
-
-                               rrdset_done(st);
-                               st = NULL;
-
-                               count++;
-                       }
-                       else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
-                               debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
-                               st = NULL;
-                       }
-                       else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
-                               int noname = 0;
-                               st = NULL;
-
-                               if((words[1]) != NULL && (words[2]) != NULL && strcmp(words[1], words[2]) == 0)
-                                       noname = 1;
-
-                               char *type = words[1];
-                               char *id = NULL;
-                               if(likely(type)) {
-                                       id = strchr(type, '.');
-                                       if(likely(id)) { *id = '\0'; id++; }
-                               }
-                               char *name = words[2];
-                               char *title = words[3];
-                               char *units = words[4];
-                               char *family = words[5];
-                               char *context = words[6];
-                               char *chart = words[7];
-                               char *priority_s = words[8];
-                               char *update_every_s = words[9];
-
-                               if(unlikely(!type || !*type || !id || !*id)) {
-                                       error("PLUGINSD: '%s' is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               int priority = 1000;
-                               if(likely(priority_s)) priority = atoi(priority_s);
-
-                               int update_every = cd->update_every;
-                               if(likely(update_every_s)) update_every = atoi(update_every_s);
-                               if(unlikely(!update_every)) update_every = cd->update_every;
-
-                               int chart_type = RRDSET_TYPE_LINE;
-                               if(unlikely(chart)) chart_type = rrdset_type_id(chart);
-
-                               if(unlikely(noname || !name || !*name || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0)) name = NULL;
-                               if(unlikely(!family || !*family)) family = NULL;
-                               if(unlikely(!context || !*context)) context = NULL;
-
-                               st = rrdset_find_bytype(type, id);
-                               if(unlikely(!st)) {
-                                       debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
-                                               , type, id
-                                               , name?name:""
-                                               , family?family:""
-                                               , context?context:""
-                                               , rrdset_type_name(chart_type)
-                                               , priority
-                                               , update_every
-                                               );
-
-                                       st = rrdset_create(type, id, name, family, context, title, units, priority, update_every, chart_type);
-                                       cd->update_every = update_every;
-                               }
-                               else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
-                       }
-                       else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
-                               char *id = words[1];
-                               char *name = words[2];
-                               char *algorithm = words[3];
-                               char *multiplier_s = words[4];
-                               char *divisor_s = words[5];
-                               char *options = words[6];
-
-                               if(unlikely(!id || !*id)) {
-                                       error("PLUGINSD: '%s' is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               if(unlikely(!st)) {
-                                       error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
-                                       cd->enabled = 0;
-                                       killpid(cd->pid, SIGTERM);
-                                       break;
-                               }
-
-                               long multiplier = 1;
-                               if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
-                               if(unlikely(!multiplier)) multiplier = 1;
-
-                               long divisor = 1;
-                               if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
-                               if(unlikely(!divisor)) divisor = 1;
-
-                               if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
-
-                               if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
-                                       , st->id
-                                       , id
-                                       , name?name:""
-                                       , rrddim_algorithm_name(rrddim_algorithm_id(algorithm))
-                                       , multiplier
-                                       , divisor
-                                       , options?options:""
-                                       );
-
-                               RRDDIM *rd = rrddim_find(st, id);
-                               if(unlikely(!rd)) {
-                                       rd = rrddim_add(st, id, name, multiplier, divisor, rrddim_algorithm_id(algorithm));
-                                       rd->flags = 0x00000000;
-                                       if(options && *options) {
-                                               if(strstr(options, "hidden") != NULL) rd->flags |= RRDDIM_FLAG_HIDDEN;
-                                               if(strstr(options, "noreset") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
-                                               if(strstr(options, "nooverflow") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
-                                       }
-                               }
-                               else if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
-                       }
-                       else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
-                               error("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
-                               cd->enabled = 0;
-                               killpid(cd->pid, SIGTERM);
-                               break;
-                       }
+    size_t count = 0;
+
+    while(likely(1)) {
+        if(unlikely(netdata_exit)) break;
+
+        FILE *fp = mypopen(cd->cmd, &cd->pid);
+        if(unlikely(!fp)) {
+            error("Cannot popen(\"%s\", \"r\").", cd->cmd);
+            break;
+        }
+
+        info("PLUGINSD: '%s' running on pid %d", cd->fullfilename, cd->pid);
+
+        RRDSET *st = NULL;
+        char *s;
+        uint32_t hash;
+
+        while(likely(fgets(line, PLUGINSD_LINE_MAX, fp) != NULL)) {
+            if(unlikely(netdata_exit)) break;
+
+            line[PLUGINSD_LINE_MAX] = '\0';
+
+            // debug(D_PLUGINSD, "PLUGINSD: %s: %s", cd->filename, line);
+
+            int w = pluginsd_split_words(line, words, MAX_WORDS);
+            s = words[0];
+            if(unlikely(!s || !*s || !w)) {
+                // debug(D_PLUGINSD, "PLUGINSD: empty line");
+                continue;
+            }
+
+            // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
+
+            hash = simple_hash(s);
+
+            if(likely(hash == SET_HASH && !strcmp(s, "SET"))) {
+                char *dimension = words[1];
+                char *value = words[2];
+
+                if(unlikely(!dimension || !*dimension)) {
+                    error("PLUGINSD: '%s' is requesting a SET on chart '%s', without a dimension. Disabling it.", cd->fullfilename, st->id);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                if(unlikely(!value || !*value)) value = NULL;
+
+                if(unlikely(!st)) {
+                    error("PLUGINSD: '%s' is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value?value:"<nothing>");
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value?value:"<nothing>");
+
+                if(value) rrddim_set(st, dimension, strtoll(value, NULL, 0));
+            }
+            else if(likely(hash == BEGIN_HASH && !strcmp(s, "BEGIN"))) {
+                char *id = words[1];
+                char *microseconds_txt = words[2];
+
+                if(unlikely(!id)) {
+                    error("PLUGINSD: '%s' is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                st = rrdset_find(id);
+                if(unlikely(!st)) {
+                    error("PLUGINSD: '%s' is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                if(likely(st->counter_done)) {
+                    unsigned long long microseconds = 0;
+                    if(microseconds_txt && *microseconds_txt) microseconds = strtoull(microseconds_txt, NULL, 10);
+                    if(microseconds) rrdset_next_usec(st, microseconds);
+                    else rrdset_next_plugins(st);
+                }
+            }
+            else if(likely(hash == END_HASH && !strcmp(s, "END"))) {
+                if(unlikely(!st)) {
+                    error("PLUGINSD: '%s' is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting an END on chart %s", cd->fullfilename, st->id);
+
+                rrdset_done(st);
+                st = NULL;
+
+                count++;
+            }
+            else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
+                debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
+                st = NULL;
+            }
+            else if(likely(hash == CHART_HASH && !strcmp(s, "CHART"))) {
+                int noname = 0;
+                st = NULL;
+
+                if((words[1]) != NULL && (words[2]) != NULL && strcmp(words[1], words[2]) == 0)
+                    noname = 1;
+
+                char *type = words[1];
+                char *id = NULL;
+                if(likely(type)) {
+                    id = strchr(type, '.');
+                    if(likely(id)) { *id = '\0'; id++; }
+                }
+                char *name = words[2];
+                char *title = words[3];
+                char *units = words[4];
+                char *family = words[5];
+                char *context = words[6];
+                char *chart = words[7];
+                char *priority_s = words[8];
+                char *update_every_s = words[9];
+
+                if(unlikely(!type || !*type || !id || !*id)) {
+                    error("PLUGINSD: '%s' is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                int priority = 1000;
+                if(likely(priority_s)) priority = atoi(priority_s);
+
+                int update_every = cd->update_every;
+                if(likely(update_every_s)) update_every = atoi(update_every_s);
+                if(unlikely(!update_every)) update_every = cd->update_every;
+
+                int chart_type = RRDSET_TYPE_LINE;
+                if(unlikely(chart)) chart_type = rrdset_type_id(chart);
+
+                if(unlikely(noname || !name || !*name || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0)) name = NULL;
+                if(unlikely(!family || !*family)) family = NULL;
+                if(unlikely(!context || !*context)) context = NULL;
+
+                st = rrdset_find_bytype(type, id);
+                if(unlikely(!st)) {
+                    debug(D_PLUGINSD, "PLUGINSD: Creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
+                        , type, id
+                        , name?name:""
+                        , family?family:""
+                        , context?context:""
+                        , rrdset_type_name(chart_type)
+                        , priority
+                        , update_every
+                        );
+
+                    st = rrdset_create(type, id, name, family, context, title, units, priority, update_every, chart_type);
+                    cd->update_every = update_every;
+                }
+                else debug(D_PLUGINSD, "PLUGINSD: Chart '%s' already exists. Not adding it again.", st->id);
+            }
+            else if(likely(hash == DIMENSION_HASH && !strcmp(s, "DIMENSION"))) {
+                char *id = words[1];
+                char *name = words[2];
+                char *algorithm = words[3];
+                char *multiplier_s = words[4];
+                char *divisor_s = words[5];
+                char *options = words[6];
+
+                if(unlikely(!id || !*id)) {
+                    error("PLUGINSD: '%s' is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                if(unlikely(!st)) {
+                    error("PLUGINSD: '%s' is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
+                    cd->enabled = 0;
+                    killpid(cd->pid, SIGTERM);
+                    break;
+                }
+
+                long multiplier = 1;
+                if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
+                if(unlikely(!multiplier)) multiplier = 1;
+
+                long divisor = 1;
+                if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
+                if(unlikely(!divisor)) divisor = 1;
+
+                if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
+
+                if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: Creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
+                    , st->id
+                    , id
+                    , name?name:""
+                    , rrddim_algorithm_name(rrddim_algorithm_id(algorithm))
+                    , multiplier
+                    , divisor
+                    , options?options:""
+                    );
+
+                RRDDIM *rd = rrddim_find(st, id);
+                if(unlikely(!rd)) {
+                    rd = rrddim_add(st, id, name, multiplier, divisor, rrddim_algorithm_id(algorithm));
+                    rd->flags = 0x00000000;
+                    if(options && *options) {
+                        if(strstr(options, "hidden") != NULL) rd->flags |= RRDDIM_FLAG_HIDDEN;
+                        if(strstr(options, "noreset") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
+                        if(strstr(options, "nooverflow") != NULL) rd->flags |= RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS;
+                    }
+                }
+                else if(unlikely(st->debug)) debug(D_PLUGINSD, "PLUGINSD: dimension %s/%s already exists. Not adding it again.", st->id, id);
+            }
+            else if(unlikely(hash == DISABLE_HASH && !strcmp(s, "DISABLE"))) {
+                error("PLUGINSD: '%s' called DISABLE. Disabling it.", cd->fullfilename);
+                cd->enabled = 0;
+                killpid(cd->pid, SIGTERM);
+                break;
+            }
 #ifdef DETACH_PLUGINS_FROM_NETDATA
-                       else if(likely(hash == MYPID_HASH && !strcmp(s, "MYPID"))) {
-                               char *pid_s = words[1];
-                               pid_t pid = strtod(pid_s, NULL, 0);
-
-                               if(likely(pid)) cd->pid = pid;
-                               debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
-                       }
-                       else if(likely(hash == STOPPING_WAKE_ME_UP_PLEASE_HASH && !strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE"))) {
-                               error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
-
-                               gettimeofday(&now, NULL);
-                               if(unlikely(!usec && !susec)) {
-                                       // our first run
-                                       susec = cd->rrd_update_every * 1000000ULL;
-                               }
-                               else {
-                                       // second+ run
-                                       usec = usecdiff(&now, &last) - susec;
-                                       error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
-                                       if(unlikely(usec < (rrd_update_every * 1000000ULL / 2ULL))) susec = (rrd_update_every * 1000000ULL) - usec;
-                                       else susec = rrd_update_every * 1000000ULL / 2ULL;
-                               }
-
-                               error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
-                               usleep(susec);
-                               killpid(cd->pid, SIGCONT);
-                               bcopy(&now, &last, sizeof(struct timeval));
-                               break;
-                       }
+            else if(likely(hash == MYPID_HASH && !strcmp(s, "MYPID"))) {
+                char *pid_s = words[1];
+                pid_t pid = strtod(pid_s, NULL, 0);
+
+                if(likely(pid)) cd->pid = pid;
+                debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
+            }
+            else if(likely(hash == STOPPING_WAKE_ME_UP_PLEASE_HASH && !strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE"))) {
+                error("PLUGINSD: '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
+
+                gettimeofday(&now, NULL);
+                if(unlikely(!usec && !susec)) {
+                    // our first run
+                    susec = cd->rrd_update_every * 1000000ULL;
+                }
+                else {
+                    // second+ run
+                    usec = usecdiff(&now, &last) - susec;
+                    error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
+                    if(unlikely(usec < (rrd_update_every * 1000000ULL / 2ULL))) susec = (rrd_update_every * 1000000ULL) - usec;
+                    else susec = rrd_update_every * 1000000ULL / 2ULL;
+                }
+
+                error("PLUGINSD: %s sleeping for %llu. Will kill with SIGCONT pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
+                usleep(susec);
+                killpid(cd->pid, SIGCONT);
+                bcopy(&now, &last, sizeof(struct timeval));
+                break;
+            }
 #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;
-                       }
-               }
-               if(likely(count)) {
-                       cd->successful_collections += count;
-                       cd->serial_failures = 0;
-               }
-               else
-                       cd->serial_failures++;
-
-               info("PLUGINSD: '%s' on pid %d stopped after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count);
-
-               // get the return code
-               int code = mypclose(fp, cd->pid);
-
-               if(netdata_exit) {
-                       cd->pid = 0;
-                       cd->enabled = 0;
-                       cd->obsolete = 1;
-                       pthread_exit(NULL);
-                       return NULL;
-               }
-
-               if(code != 0) {
-                       // the plugin reports failure
-
-                       if(likely(!cd->successful_collections)) {
-                               // nothing collected - disable it
-                               error("PLUGINSD: '%s' exited with error code %d. Disabling it.", cd->fullfilename, code);
-                               cd->enabled = 0;
-                       }
-                       else {
-                               // we have collected something
-
-                               if(likely(cd->serial_failures <= 10)) {
-                                       error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). Waiting a bit before starting it again.", cd->fullfilename, code, cd->successful_collections);
-                                       sleep((unsigned int) (cd->update_every * 10));
-                               }
-                               else {
-                                       error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). We tried %zu times to restart it, but it failed to generate data. Disabling it.", cd->fullfilename, code, cd->successful_collections, cd->serial_failures);
-                                       cd->enabled = 0;
-                               }
-                       }
-               }
-               else {
-                       // the plugin reports success
-
-                       if(unlikely(!cd->successful_collections)) {
-                               // we have collected nothing so far
-
-                               if(likely(cd->serial_failures <= 10)) {
-                                       error("PLUGINSD: '%s' (pid %d) does not generate useful output but it reports success (exits with 0). Waiting a bit before starting it again.", cd->fullfilename, cd->pid);
-                                       sleep((unsigned int) (cd->update_every * 10));
-                               }
-                               else {
-                                       error("PLUGINSD: '%s' (pid %d) does not generate useful output, although it reports success (exits with 0), but we have tried %zu times to collect something. Disabling it.", cd->fullfilename, cd->pid, cd->serial_failures);
-                                       cd->enabled = 0;
-                               }
-                       }
-                       else
-                               sleep((unsigned int) cd->update_every);
-               }
-               cd->pid = 0;
-
-               if(unlikely(!cd->enabled))
-                       break;
-       }
-
-       cd->obsolete = 1;
-       pthread_exit(NULL);
-       return NULL;
+            else {
+                error("PLUGINSD: '%s' is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
+                cd->enabled = 0;
+                killpid(cd->pid, SIGTERM);
+                break;
+            }
+        }
+        if(likely(count)) {
+            cd->successful_collections += count;
+            cd->serial_failures = 0;
+        }
+        else
+            cd->serial_failures++;
+
+        info("PLUGINSD: '%s' on pid %d stopped after %zu successful data collections (ENDs).", cd->fullfilename, cd->pid, count);
+
+        // get the return code
+        int code = mypclose(fp, cd->pid);
+
+        if(netdata_exit) {
+            cd->pid = 0;
+            cd->enabled = 0;
+            cd->obsolete = 1;
+            pthread_exit(NULL);
+            return NULL;
+        }
+
+        if(code != 0) {
+            // the plugin reports failure
+
+            if(likely(!cd->successful_collections)) {
+                // nothing collected - disable it
+                error("PLUGINSD: '%s' exited with error code %d. Disabling it.", cd->fullfilename, code);
+                cd->enabled = 0;
+            }
+            else {
+                // we have collected something
+
+                if(likely(cd->serial_failures <= 10)) {
+                    error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). Waiting a bit before starting it again.", cd->fullfilename, code, cd->successful_collections);
+                    sleep((unsigned int) (cd->update_every * 10));
+                }
+                else {
+                    error("PLUGINSD: '%s' exited with error code %d, but has given useful output in the past (%zu times). We tried %zu times to restart it, but it failed to generate data. Disabling it.", cd->fullfilename, code, cd->successful_collections, cd->serial_failures);
+                    cd->enabled = 0;
+                }
+            }
+        }
+        else {
+            // the plugin reports success
+
+            if(unlikely(!cd->successful_collections)) {
+                // we have collected nothing so far
+
+                if(likely(cd->serial_failures <= 10)) {
+                    error("PLUGINSD: '%s' (pid %d) does not generate useful output but it reports success (exits with 0). Waiting a bit before starting it again.", cd->fullfilename, cd->pid);
+                    sleep((unsigned int) (cd->update_every * 10));
+                }
+                else {
+                    error("PLUGINSD: '%s' (pid %d) does not generate useful output, although it reports success (exits with 0), but we have tried %zu times to collect something. Disabling it.", cd->fullfilename, cd->pid, cd->serial_failures);
+                    cd->enabled = 0;
+                }
+            }
+            else
+                sleep((unsigned int) cd->update_every);
+        }
+        cd->pid = 0;
+
+        if(unlikely(!cd->enabled))
+            break;
+    }
+
+    cd->obsolete = 1;
+    pthread_exit(NULL);
+    return NULL;
 }
 
-void *pluginsd_main(void *ptr)
-{
-       if(ptr) { ; }
-
-       info("PLUGINS.D thread created with task id %d", gettid());
-
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
-
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
-
-       char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
-       int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
-       int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
-       DIR *dir = NULL;
-       struct dirent *file = NULL;
-       struct plugind *cd;
-
-       // enable the apps plugin by default
-       // config_get_boolean("plugins", "apps", 1);
-
-       if(scan_frequency < 1) scan_frequency = 1;
-
-       while(likely(1)) {
-               if(unlikely(netdata_exit)) break;
-
-               dir = opendir(dir_name);
-               if(unlikely(!dir)) {
-                       error("Cannot open directory '%s'.", dir_name);
-                       pthread_exit(NULL);
-                       return NULL;
-               }
-
-               while(likely((file = readdir(dir)))) {
-                       if(unlikely(netdata_exit)) break;
-
-                       debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
-
-                       if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
-
-                       int len = (int) strlen(file->d_name);
-                       if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
-                       if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
-                               debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
-                               continue;
-                       }
-
-                       char pluginname[CONFIG_MAX_NAME + 1];
-                       snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
-                       int enabled = config_get_boolean("plugins", pluginname, automatic_run);
-
-                       if(unlikely(!enabled)) {
-                               debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
-                               continue;
-                       }
-
-                       // check if it runs already
-                       for(cd = pluginsd_root ; likely(cd) ; cd = cd->next) {
-                               if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
-                       }
-                       if(likely(cd && !cd->obsolete)) {
-                               debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
-                               continue;
-                       }
-
-                       // it is not running
-                       // allocate a new one, or use the obsolete one
-                       if(unlikely(!cd)) {
-                               cd = calloc(sizeof(struct plugind), 1);
-                               if(unlikely(!cd)) fatal("Cannot allocate memory for plugin.");
-
-                               snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
-
-                               strncpyz(cd->filename, file->d_name, FILENAME_MAX);
-                               snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);
-
-                               cd->enabled = enabled;
-                               cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
-                               cd->started_t = time(NULL);
-
-                               char *def = "";
-                               snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
-
-                               // link it
-                               if(likely(pluginsd_root)) cd->next = pluginsd_root;
-                               pluginsd_root = cd;
-                       }
-                       cd->obsolete = 0;
-
-                       if(unlikely(!cd->enabled)) continue;
-
-                       // spawn a new thread for it
-                       if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) {
-                               error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
-                               cd->obsolete = 1;
-                       }
-                       else if(unlikely(pthread_detach(cd->thread) != 0))
-                               error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
-               }
-
-               closedir(dir);
-               sleep((unsigned int) scan_frequency);
-       }
-
-       pthread_exit(NULL);
-       return NULL;
+void *pluginsd_main(void *ptr) {
+    (void)ptr;
+
+    info("PLUGINS.D thread created with task id %d", gettid());
+
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
+
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
+
+    char *dir_name = config_get("plugins", "plugins directory", PLUGINS_DIR);
+    int automatic_run = config_get_boolean("plugins", "enable running new plugins", 1);
+    int scan_frequency = (int) config_get_number("plugins", "check for new plugins every", 60);
+    DIR *dir = NULL;
+    struct dirent *file = NULL;
+    struct plugind *cd;
+
+    // enable the apps plugin by default
+    // config_get_boolean("plugins", "apps", 1);
+
+    if(scan_frequency < 1) scan_frequency = 1;
+
+    while(likely(1)) {
+        if(unlikely(netdata_exit)) break;
+
+        dir = opendir(dir_name);
+        if(unlikely(!dir)) {
+            error("Cannot open directory '%s'.", dir_name);
+            pthread_exit(NULL);
+            return NULL;
+        }
+
+        while(likely((file = readdir(dir)))) {
+            if(unlikely(netdata_exit)) break;
+
+            debug(D_PLUGINSD, "PLUGINSD: Examining file '%s'", file->d_name);
+
+            if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
+
+            int len = (int) strlen(file->d_name);
+            if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
+            if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
+                debug(D_PLUGINSD, "PLUGINSD: File '%s' does not end in '%s'.", file->d_name, PLUGINSD_FILE_SUFFIX);
+                continue;
+            }
+
+            char pluginname[CONFIG_MAX_NAME + 1];
+            snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
+            int enabled = config_get_boolean("plugins", pluginname, automatic_run);
+
+            if(unlikely(!enabled)) {
+                debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is not enabled", file->d_name);
+                continue;
+            }
+
+            // check if it runs already
+            for(cd = pluginsd_root ; likely(cd) ; cd = cd->next) {
+                if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
+            }
+            if(likely(cd && !cd->obsolete)) {
+                debug(D_PLUGINSD, "PLUGINSD: plugin '%s' is already running", cd->filename);
+                continue;
+            }
+
+            // it is not running
+            // allocate a new one, or use the obsolete one
+            if(unlikely(!cd)) {
+                cd = callocz(sizeof(struct plugind), 1);
+
+                snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
+
+                strncpyz(cd->filename, file->d_name, FILENAME_MAX);
+                snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", dir_name, cd->filename);
+
+                cd->enabled = enabled;
+                cd->update_every = (int) config_get_number(cd->id, "update every", rrd_update_every);
+                cd->started_t = time(NULL);
+
+                char *def = "";
+                snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
+
+                // link it
+                if(likely(pluginsd_root)) cd->next = pluginsd_root;
+                pluginsd_root = cd;
+            }
+            cd->obsolete = 0;
+
+            if(unlikely(!cd->enabled)) continue;
+
+            // spawn a new thread for it
+            if(unlikely(pthread_create(&cd->thread, NULL, pluginsd_worker_thread, cd) != 0)) {
+                error("PLUGINSD: failed to create new thread for plugin '%s'.", cd->filename);
+                cd->obsolete = 1;
+            }
+            else if(unlikely(pthread_detach(cd->thread) != 0))
+                error("PLUGINSD: Cannot request detach of newly created thread for plugin '%s'.", cd->filename);
+        }
+
+        closedir(dir);
+        sleep((unsigned int) scan_frequency);
+    }
+
+    pthread_exit(NULL);
+    return NULL;
 }
index 1a10dc0dc507dfce60dc4cfebf002e1aad8607b9..6f1fbd6e10fe85793a5f3c99871ab7e48ac284de 100644 (file)
@@ -1,7 +1,3 @@
-#include <sys/types.h>
-#include <unistd.h>
-
-
 #ifndef NETDATA_PLUGINS_D_H
 #define NETDATA_PLUGINS_D_H 1
 
 #define PLUGINSD_LINE_MAX 1024
 
 struct plugind {
-       char id[CONFIG_MAX_NAME+1];                     // config node id
+    char id[CONFIG_MAX_NAME+1];         // config node id
 
-       char filename[FILENAME_MAX+1];          // just the filename
-       char fullfilename[FILENAME_MAX+1];      // with path
-       char cmd[PLUGINSD_CMD_MAX+1];           // the command that is executes
+    char filename[FILENAME_MAX+1];      // just the filename
+    char fullfilename[FILENAME_MAX+1];  // with path
+    char cmd[PLUGINSD_CMD_MAX+1];       // the command that is executes
 
-       pid_t pid;
-       pthread_t thread;
+    pid_t pid;
+    pthread_t thread;
 
-       size_t successful_collections;          // the number of times we have seen
-                                                                               // values collected from this plugin
+    size_t successful_collections;      // the number of times we have seen
+                                        // values collected from this plugin
 
-       size_t serial_failures;                         // the number of times the plugin started
-                                                                               // without collecting values
+    size_t serial_failures;             // the number of times the plugin started
+                                        // without collecting values
 
-       int update_every;                                       // the plugin default data collection frequency
-       int obsolete;                                           // do not touch this structure after setting this to 1
-       int enabled;                                            // if this is enabled or not
+    int update_every;                   // the plugin default data collection frequency
+    int obsolete;                       // do not touch this structure after setting this to 1
+    int enabled;                        // if this is enabled or not
 
-       time_t started_t;
+    time_t started_t;
 
-       struct plugind *next;
+    struct plugind *next;
 };
 
 extern struct plugind *pluginsd_root;
index 4e2874f4cd8b2609d19455785f36dd4e1a347ad3..eee08b5ed9010fe6532625bf6d3e0cc969feffc9 100644 (file)
@@ -1,54 +1,43 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <signal.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include "log.h"
-#include "popen.h"
 #include "common.h"
 
 /*
 struct mypopen {
-       pid_t pid;
-       FILE *fp;
-       struct mypopen *next;
-       struct mypopen *prev;
+    pid_t pid;
+    FILE *fp;
+    struct mypopen *next;
+    struct mypopen *prev;
 };
 
 static struct mypopen *mypopen_root = NULL;
 
 static void mypopen_add(FILE *fp, pid_t *pid) {
-       struct mypopen *mp = malloc(sizeof(struct mypopen));
-       if(!mp) {
-               fatal("Cannot allocate %zu bytes", sizeof(struct mypopen))
-               return;
-       }
-
-       mp->fp = fp;
-       mp->pid = pid;
-       mp->next = popen_root;
-       mp->prev = NULL;
-       if(mypopen_root) mypopen_root->prev = mp;
-       mypopen_root = mp;
+    struct mypopen *mp = malloc(sizeof(struct mypopen));
+    if(!mp) {
+        fatal("Cannot allocate %zu bytes", sizeof(struct mypopen))
+        return;
+    }
+
+    mp->fp = fp;
+    mp->pid = pid;
+    mp->next = popen_root;
+    mp->prev = NULL;
+    if(mypopen_root) mypopen_root->prev = mp;
+    mypopen_root = mp;
 }
 
 static void mypopen_del(FILE *fp) {
-       struct mypopen *mp;
-
-       for(mp = mypopen_root; mp; mp = mp->next)
-               if(mp->fd == fp) break;
-
-       if(!mp) error("Cannot find mypopen() file pointer in open childs.");
-       else {
-               if(mp->next) mp->next->prev = mp->prev;
-               if(mp->prev) mp->prev->next = mp->next;
-               if(mypopen_root == mp) mypopen_root = mp->next;
-               free(mp);
-       }
+    struct mypopen *mp;
+
+    for(mp = mypopen_root; mp; mp = mp->next)
+        if(mp->fd == fp) break;
+
+    if(!mp) error("Cannot find mypopen() file pointer in open childs.");
+    else {
+        if(mp->next) mp->next->prev = mp->prev;
+        if(mp->prev) mp->prev->next = mp->next;
+        if(mypopen_root == mp) mypopen_root = mp->next;
+        free(mp);
+    }
 }
 */
 #define PIPE_READ 0
@@ -56,141 +45,141 @@ static void mypopen_del(FILE *fp) {
 
 FILE *mypopen(const char *command, pid_t *pidptr)
 {
-       int pipefd[2];
-
-       if(pipe(pipefd) == -1) return NULL;
-
-       int pid = fork();
-       if(pid == -1) {
-               close(pipefd[PIPE_READ]);
-               close(pipefd[PIPE_WRITE]);
-               return NULL;
-       }
-       if(pid != 0) {
-               // the parent
-               *pidptr = pid;
-               close(pipefd[PIPE_WRITE]);
-               FILE *fp = fdopen(pipefd[PIPE_READ], "r");
-               /*mypopen_add(fp, pid);*/
-               return(fp);
-       }
-       // the child
-
-       // close all files
-       int i;
-       for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
-               if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
-
-       // move the pipe to stdout
-       if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
-               dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
-               close(pipefd[PIPE_WRITE]);
-       }
+    int pipefd[2];
+
+    if(pipe(pipefd) == -1) return NULL;
+
+    int pid = fork();
+    if(pid == -1) {
+        close(pipefd[PIPE_READ]);
+        close(pipefd[PIPE_WRITE]);
+        return NULL;
+    }
+    if(pid != 0) {
+        // the parent
+        *pidptr = pid;
+        close(pipefd[PIPE_WRITE]);
+        FILE *fp = fdopen(pipefd[PIPE_READ], "r");
+        /*mypopen_add(fp, pid);*/
+        return(fp);
+    }
+    // the child
+
+    // close all files
+    int i;
+    for(i = (int) (sysconf(_SC_OPEN_MAX) - 1); i > 0; i--)
+        if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
+
+    // move the pipe to stdout
+    if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
+        dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
+        close(pipefd[PIPE_WRITE]);
+    }
 
 #ifdef DETACH_PLUGINS_FROM_NETDATA
-       // this was an attempt to detach the child and use the suspend mode charts.d
-       // unfortunatelly it does not work as expected.
+    // this was an attempt to detach the child and use the suspend mode charts.d
+    // unfortunatelly it does not work as expected.
 
-       // fork again to become session leader
-       pid = fork();
-       if(pid == -1)
-               error("pre-execution of command '%s' on pid %d: Cannot fork 2nd time.", command, getpid());
+    // fork again to become session leader
+    pid = fork();
+    if(pid == -1)
+        error("pre-execution of command '%s' on pid %d: Cannot fork 2nd time.", command, getpid());
 
-       if(pid != 0) {
-               // the parent
-               exit(0);
-       }
+    if(pid != 0) {
+        // the parent
+        exit(0);
+    }
 
-       // set a new process group id for just this child
-       if( setpgid(0, 0) != 0 )
-               error("pre-execution of command '%s' on pid %d: Cannot set a new process group.", command, getpid());
+    // set a new process group id for just this child
+    if( setpgid(0, 0) != 0 )
+        error("pre-execution of command '%s' on pid %d: Cannot set a new process group.", command, getpid());
 
-       if( getpgid(0) != getpid() )
-               error("pre-execution of command '%s' on pid %d: Cannot set a new process group. Process group set is incorrect. Expected %d, found %d", command, getpid(), getpid(), getpgid(0));
+    if( getpgid(0) != getpid() )
+        error("pre-execution of command '%s' on pid %d: Cannot set a new process group. Process group set is incorrect. Expected %d, found %d", command, getpid(), getpid(), getpgid(0));
 
-       if( setsid() != 0 )
-               error("pre-execution of command '%s' on pid %d: Cannot set session id.", command, getpid());
+    if( setsid() != 0 )
+        error("pre-execution of command '%s' on pid %d: Cannot set session id.", command, getpid());
 
-       fprintf(stdout, "MYPID %d\n", getpid());
-       fflush(NULL);
+    fprintf(stdout, "MYPID %d\n", getpid());
+    fflush(NULL);
 #endif
 
-       // reset all signals
-       {
-               sigset_t sigset;
-               sigfillset(&sigset);
-
-               if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1)
-                       error("pre-execution of command '%s' on pid %d: could not unblock signals for threads.", command, getpid());
-               
-               // We only need to reset ignored signals.
-               // Signals with signal handlers are reset by default.
-               struct sigaction sa;
-               sigemptyset(&sa.sa_mask);
-               sa.sa_handler = SIG_DFL;
-               sa.sa_flags = 0;
-               
-               if(sigaction(SIGUSR1, &sa, NULL) == -1)
-                       error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGUSR1.", command, getpid());
-
-               if(sigaction(SIGPIPE, &sa, NULL) == -1)
-                       error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGPIPE.", command, getpid());
-       }
-
-
-       info("executing command: '%s' on pid %d.", command, getpid());
-       execl("/bin/sh", "sh", "-c", command, NULL);
-       exit(1);
+    // reset all signals
+    {
+        sigset_t sigset;
+        sigfillset(&sigset);
+
+        if(pthread_sigmask(SIG_UNBLOCK, &sigset, NULL) == -1)
+            error("pre-execution of command '%s' on pid %d: could not unblock signals for threads.", command, getpid());
+        
+        // We only need to reset ignored signals.
+        // Signals with signal handlers are reset by default.
+        struct sigaction sa;
+        sigemptyset(&sa.sa_mask);
+        sa.sa_handler = SIG_DFL;
+        sa.sa_flags = 0;
+        
+        if(sigaction(SIGUSR1, &sa, NULL) == -1)
+            error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGUSR1.", command, getpid());
+
+        if(sigaction(SIGPIPE, &sa, NULL) == -1)
+            error("pre-execution of command '%s' on pid %d: failed to set default signal handler for SIGPIPE.", command, getpid());
+    }
+
+
+    info("executing command: '%s' on pid %d.", command, getpid());
+    execl("/bin/sh", "sh", "-c", command, NULL);
+    exit(1);
 }
 
 int mypclose(FILE *fp, pid_t pid) {
-       debug(D_EXIT, "Request to mypclose() on pid %d", pid);
-
-       /*mypopen_del(fp);*/
-       fclose(fp);
-
-       siginfo_t info;
-       if(waitid(P_PID, (id_t) pid, &info, WEXITED) != -1) {
-               switch(info.si_code) {
-                       case CLD_EXITED:
-                               if(info.si_status)
-                                       error("child pid %d exited with code %d.", info.si_pid, info.si_status);
-                               return(info.si_status);
-                               break;
-
-                       case CLD_KILLED:
-                               error("child pid %d killed by signal %d.", info.si_pid, info.si_status);
-                               return(-1);
-                               break;
-
-                       case CLD_DUMPED:
-                               error("child pid %d core dumped by signal %d.", info.si_pid, info.si_status);
-                               return(-2);
-                               break;
-
-                       case CLD_STOPPED:
-                               error("child pid %d stopped by signal %d.", info.si_pid, info.si_status);
-                               return(0);
-                               break;
-
-                       case CLD_TRAPPED:
-                               error("child pid %d trapped by signal %d.", info.si_pid, info.si_status);
-                               return(-4);
-                               break;
-
-                       case CLD_CONTINUED:
-                               error("child pid %d continued by signal %d.", info.si_pid, info.si_status);
-                               return(0);
-                               break;
-
-                       default:
-                               error("child pid %d gave us a SIGCHLD with code %d and status %d.", info.si_pid, info.si_code, info.si_status);
-                               return(-5);
-                               break;
-               }
-       }
-       else
-               error("Cannot waitid() for pid %d", pid);
-       
-       return 0;
+    debug(D_EXIT, "Request to mypclose() on pid %d", pid);
+
+    /*mypopen_del(fp);*/
+    fclose(fp);
+
+    siginfo_t info;
+    if(waitid(P_PID, (id_t) pid, &info, WEXITED) != -1) {
+        switch(info.si_code) {
+            case CLD_EXITED:
+                if(info.si_status)
+                    error("child pid %d exited with code %d.", info.si_pid, info.si_status);
+                return(info.si_status);
+                break;
+
+            case CLD_KILLED:
+                error("child pid %d killed by signal %d.", info.si_pid, info.si_status);
+                return(-1);
+                break;
+
+            case CLD_DUMPED:
+                error("child pid %d core dumped by signal %d.", info.si_pid, info.si_status);
+                return(-2);
+                break;
+
+            case CLD_STOPPED:
+                error("child pid %d stopped by signal %d.", info.si_pid, info.si_status);
+                return(0);
+                break;
+
+            case CLD_TRAPPED:
+                error("child pid %d trapped by signal %d.", info.si_pid, info.si_status);
+                return(-4);
+                break;
+
+            case CLD_CONTINUED:
+                error("child pid %d continued by signal %d.", info.si_pid, info.si_status);
+                return(0);
+                break;
+
+            default:
+                error("child pid %d gave us a SIGCHLD with code %d and status %d.", info.si_pid, info.si_code, info.si_status);
+                return(-5);
+                break;
+        }
+    }
+    else
+        error("Cannot waitid() for pid %d", pid);
+    
+    return 0;
 }
index 10680f0c8295903104a195f9576e8b9ba5958e9b..90845e1fb524e797632e0a8594d14c5bc088c6b9 100644 (file)
@@ -1,7 +1,3 @@
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-
 #ifndef NETDATA_POPEN_H
 #define NETDATA_POPEN_H 1
 
index 3e3df2465a7b517a40fd18902072a8ac3da62c44..459d5a133b29fd2e5024e20b7b37ab92b1c6f2df 100644 (file)
@@ -1,25 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-
-#include "proc_self_mountinfo.h"
 
 #define RRD_TYPE_DISK "disk"
 
 #define DISK_TYPE_CONTAINER 3
 
 static struct disk {
-       char *disk;                             // the name of the disk (sda, sdb, etc)
-       unsigned long major;
-       unsigned long minor;
-       int sector_size;
-       int type;
-       char *mount_point;
-
-       // disk options caching
-       int configured;
-       int do_io;
-       int do_ops;
-       int do_mops;
-       int do_iotime;
-       int do_qops;
-       int do_util;
-       int do_backlog;
-       int do_space;
-       int do_inodes;
-
-       struct disk *next;
+    char *disk;             // the name of the disk (sda, sdb, etc)
+    unsigned long major;
+    unsigned long minor;
+    int sector_size;
+    int type;
+    char *mount_point;
+
+    // disk options caching
+    int configured;
+    int do_io;
+    int do_ops;
+    int do_mops;
+    int do_iotime;
+    int do_qops;
+    int do_util;
+    int do_backlog;
+    int do_space;
+    int do_inodes;
+
+    struct disk *next;
 } *disk_root = NULL;
 
 static struct mountinfo *disk_mountinfo_root = NULL;
 
 static struct disk *get_disk(unsigned long major, unsigned long minor, char *disk) {
-       static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
-       static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = "";
-       static char path_find_block_device[FILENAME_MAX + 1] = "";
-       struct disk *d;
-
-       // search for it in our RAM list.
-       // this is sequential, but since we just walk through
-       // and the number of disks / partitions in a system
-       // should not be that many, it should be acceptable
-       for(d = disk_root; d ; d = d->next)
-               if(unlikely(d->major == major && d->minor == minor))
-                       break;
-
-       // if we found it, return it
-       if(likely(d))
-               return d;
-
-       // not found
-       // create a new disk structure
-       d = (struct disk *)malloc(sizeof(struct disk));
-       if(!d) fatal("Cannot allocate memory for struct disk in proc_diskstats.");
-
-       d->disk = strdup(disk);
-       d->major = major;
-       d->minor = minor;
-       d->type = DISK_TYPE_PHYSICAL; // Default type. Changed later if not correct.
-       d->configured = 0;
-       d->sector_size = 512; // the default, will be changed below
-       d->next = NULL;
-
-       // append it to the list
-       if(!disk_root)
-               disk_root = d;
-       else {
-               struct disk *last;
-               for(last = disk_root; last->next ;last = last->next);
-               last->next = d;
-       }
-
-       // ------------------------------------------------------------------------
-       // find the type of the device
-
-       char buffer[FILENAME_MAX + 1];
-
-       // get the default path for finding info about the block device
-       if(unlikely(!path_find_block_device[0])) {
-               snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/%s");
-               snprintfz(path_find_block_device, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device infos", buffer));
-       }
-
-       // find if it is a partition
-       // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable.
-       snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "partition");
-       if(access(buffer, R_OK) == 0) {
-               d->type = DISK_TYPE_PARTITION;
-       } else {
-               // find if it is a container
-               // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries
-               snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "slaves/");
-               DIR *dirp = opendir(buffer);    
-               if (dirp != NULL) {
-                       struct dirent *dp;
-                       while( (dp = readdir(dirp)) ) {
-                               // . and .. are also files in empty folders.
-                               if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
-                                       continue;
-                               }
-
-                               d->type = DISK_TYPE_CONTAINER;
-
-                               // Stop the loop after we found one file.
-                               break;
-                       }
-                       if(closedir(dirp) == -1)
-                               error("Unable to close dir %s", buffer);
-               }
-       }
-
-       // ------------------------------------------------------------------------
-       // check if we can find its mount point
-
-       // mountinfo_find() can be called with NULL disk_mountinfo_root
-       struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
-       if(unlikely(!mi)) {
-               // mountinfo_free() can be called with NULL disk_mountinfo_root
-               mountinfo_free(disk_mountinfo_root);
-
-               // re-read mountinfo in case something changed
-               disk_mountinfo_root = mountinfo_read();
-
-               // search again for this disk
-               mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
-       }
-
-       if(mi)
-               d->mount_point = strdup(mi->mount_point);
-               // no need to check for NULL
-       else
-               d->mount_point = NULL;
-
-       // ------------------------------------------------------------------------
-       // find the disk sector size
-
-       if(!path_to_get_hw_sector_size[0]) {
-               snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/block/%s/queue/hw_sector_size");
-               snprintfz(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", buffer));
-       }
-       if(!path_to_get_hw_sector_size_partitions[0]) {
-               snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/subsystem/%s/../queue/hw_sector_size");
-               snprintfz(path_to_get_hw_sector_size_partitions, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size for partitions", buffer));
-       }
-
-       {
-               char tf[FILENAME_MAX + 1], *t;
-               strncpyz(tf, d->disk, FILENAME_MAX);
-
-               // replace all / with !
-               for(t = tf; *t ;t++)
-                       if(*t == '/') *t = '!';
-
-               if(d->type == DISK_TYPE_PARTITION)
-                       snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size_partitions, d->major, d->minor, tf);
-               else
-                       snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size, tf);
-
-               FILE *fpss = fopen(buffer, "r");
-               if(fpss) {
-                       char buffer2[1024 + 1];
-                       char *tmp = fgets(buffer2, 1024, fpss);
-
-                       if(tmp) {
-                               d->sector_size = atoi(tmp);
-                               if(d->sector_size <= 0) {
-                                       error("Invalid sector size %d for device %s in %s. Assuming 512.", d->sector_size, d->disk, buffer);
-                                       d->sector_size = 512;
-                               }
-                       }
-                       else error("Cannot read data for sector size for device %s from %s. Assuming 512.", d->disk, buffer);
-
-                       fclose(fpss);
-               }
-               else error("Cannot read sector size for device %s from %s. Assuming 512.", d->disk, buffer);
-       }
-
-       return d;
+    static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
+    static char path_to_get_hw_sector_size_partitions[FILENAME_MAX + 1] = "";
+    static char path_find_block_device[FILENAME_MAX + 1] = "";
+    struct disk *d;
+
+    // search for it in our RAM list.
+    // this is sequential, but since we just walk through
+    // and the number of disks / partitions in a system
+    // should not be that many, it should be acceptable
+    for(d = disk_root; d ; d = d->next)
+        if(unlikely(d->major == major && d->minor == minor))
+            break;
+
+    // if we found it, return it
+    if(likely(d))
+        return d;
+
+    // not found
+    // create a new disk structure
+    d = (struct disk *)mallocz(sizeof(struct disk));
+
+    d->disk = strdupz(disk);
+    d->major = major;
+    d->minor = minor;
+    d->type = DISK_TYPE_PHYSICAL; // Default type. Changed later if not correct.
+    d->configured = 0;
+    d->sector_size = 512; // the default, will be changed below
+    d->next = NULL;
+
+    // append it to the list
+    if(!disk_root)
+        disk_root = d;
+    else {
+        struct disk *last;
+        for(last = disk_root; last->next ;last = last->next);
+        last->next = d;
+    }
+
+    // ------------------------------------------------------------------------
+    // find the type of the device
+
+    char buffer[FILENAME_MAX + 1];
+
+    // get the default path for finding info about the block device
+    if(unlikely(!path_find_block_device[0])) {
+        snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/%s");
+        snprintfz(path_find_block_device, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device infos", buffer));
+    }
+
+    // find if it is a partition
+    // by checking if /sys/dev/block/MAJOR:MINOR/partition is readable.
+    snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "partition");
+    if(access(buffer, R_OK) == 0) {
+        d->type = DISK_TYPE_PARTITION;
+    } else {
+        // find if it is a container
+        // by checking if /sys/dev/block/MAJOR:MINOR/slaves has entries
+        snprintfz(buffer, FILENAME_MAX, path_find_block_device, major, minor, "slaves/");
+        DIR *dirp = opendir(buffer);    
+        if (dirp != NULL) {
+            struct dirent *dp;
+            while( (dp = readdir(dirp)) ) {
+                // . and .. are also files in empty folders.
+                if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
+                    continue;
+                }
+
+                d->type = DISK_TYPE_CONTAINER;
+
+                // Stop the loop after we found one file.
+                break;
+            }
+            if(closedir(dirp) == -1)
+                error("Unable to close dir %s", buffer);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // check if we can find its mount point
+
+    // mountinfo_find() can be called with NULL disk_mountinfo_root
+    struct mountinfo *mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
+    if(unlikely(!mi)) {
+        // mountinfo_free() can be called with NULL disk_mountinfo_root
+        mountinfo_free(disk_mountinfo_root);
+
+        // re-read mountinfo in case something changed
+        disk_mountinfo_root = mountinfo_read();
+
+        // search again for this disk
+        mi = mountinfo_find(disk_mountinfo_root, d->major, d->minor);
+    }
+
+    if(mi)
+        d->mount_point = strdupz(mi->mount_point);
+        // no need to check for NULL
+    else
+        d->mount_point = NULL;
+
+    // ------------------------------------------------------------------------
+    // find the disk sector size
+
+    if(!path_to_get_hw_sector_size[0]) {
+        snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/block/%s/queue/hw_sector_size");
+        snprintfz(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", buffer));
+    }
+    if(!path_to_get_hw_sector_size_partitions[0]) {
+        snprintfz(buffer, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/subsystem/%s/../queue/hw_sector_size");
+        snprintfz(path_to_get_hw_sector_size_partitions, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size for partitions", buffer));
+    }
+
+    {
+        char tf[FILENAME_MAX + 1], *t;
+        strncpyz(tf, d->disk, FILENAME_MAX);
+
+        // replace all / with !
+        for(t = tf; *t ;t++)
+            if(*t == '/') *t = '!';
+
+        if(d->type == DISK_TYPE_PARTITION)
+            snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size_partitions, d->major, d->minor, tf);
+        else
+            snprintfz(buffer, FILENAME_MAX, path_to_get_hw_sector_size, tf);
+
+        FILE *fpss = fopen(buffer, "r");
+        if(fpss) {
+            char buffer2[1024 + 1];
+            char *tmp = fgets(buffer2, 1024, fpss);
+
+            if(tmp) {
+                d->sector_size = atoi(tmp);
+                if(d->sector_size <= 0) {
+                    error("Invalid sector size %d for device %s in %s. Assuming 512.", d->sector_size, d->disk, buffer);
+                    d->sector_size = 512;
+                }
+            }
+            else error("Cannot read data for sector size for device %s from %s. Assuming 512.", d->disk, buffer);
+
+            fclose(fpss);
+        }
+        else error("Cannot read sector size for device %s from %s. Assuming 512.", d->disk, buffer);
+    }
+
+    return d;
 }
 
 static inline int select_positive_option(int option1, int option2) {
-       if(option1 == CONFIG_ONDEMAND_YES || option2 == CONFIG_ONDEMAND_YES)
-               return CONFIG_ONDEMAND_YES;
-       else if(option1 == CONFIG_ONDEMAND_ONDEMAND || option2 == CONFIG_ONDEMAND_ONDEMAND)
-               return CONFIG_ONDEMAND_ONDEMAND;
+    if(option1 == CONFIG_ONDEMAND_YES || option2 == CONFIG_ONDEMAND_YES)
+        return CONFIG_ONDEMAND_YES;
+    else if(option1 == CONFIG_ONDEMAND_ONDEMAND || option2 == CONFIG_ONDEMAND_ONDEMAND)
+        return CONFIG_ONDEMAND_ONDEMAND;
 
-       return CONFIG_ONDEMAND_NO;
+    return CONFIG_ONDEMAND_NO;
 }
 
 int do_proc_diskstats(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static struct statvfs buff_statvfs;
-       static struct stat buff_stat;
-       static int      global_enable_new_disks_detected_at_runtime = CONFIG_ONDEMAND_YES,
-                               global_enable_performance_for_physical_disks = CONFIG_ONDEMAND_ONDEMAND,
-                               global_enable_performance_for_virtual_disks = CONFIG_ONDEMAND_NO,
-                               global_enable_performance_for_partitions = CONFIG_ONDEMAND_NO,
-                               global_enable_performance_for_mountpoints = CONFIG_ONDEMAND_NO,
-                               global_enable_performance_for_virtual_mountpoints = CONFIG_ONDEMAND_ONDEMAND,
-                               global_enable_space_for_mountpoints = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_io = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_ops = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_mops = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_iotime = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_qops = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_util = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_backlog = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_space = CONFIG_ONDEMAND_ONDEMAND,
-                               global_do_inodes = CONFIG_ONDEMAND_ONDEMAND,
-                               globals_initialized = 0;
-
-       if(unlikely(!globals_initialized)) {
-               global_enable_new_disks_detected_at_runtime = config_get_boolean("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", global_enable_new_disks_detected_at_runtime);
-
-               global_enable_performance_for_physical_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for physical disks", global_enable_performance_for_physical_disks);
-               global_enable_performance_for_virtual_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for virtual disks", global_enable_performance_for_virtual_disks);
-               global_enable_performance_for_partitions = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for partitions", global_enable_performance_for_partitions);
-               global_enable_performance_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted filesystems", global_enable_performance_for_mountpoints);
-               global_enable_performance_for_virtual_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted virtual disks", global_enable_performance_for_virtual_mountpoints);
-               global_enable_space_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space metrics for mounted filesystems", global_enable_space_for_mountpoints);
-
-               global_do_io      = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", global_do_io);
-               global_do_ops     = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", global_do_ops);
-               global_do_mops    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "merged operations for all disks", global_do_mops);
-               global_do_iotime  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "i/o time for all disks", global_do_iotime);
-               global_do_qops    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", global_do_qops);
-               global_do_util    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", global_do_util);
-               global_do_backlog = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", global_do_backlog);
-               global_do_space   = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space usage for all disks", global_do_space);
-               global_do_inodes  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "inodes usage for all disks", global_do_inodes);
-
-               globals_initialized = 1;
-       }
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/diskstats");
-               ff = procfile_open(config_get("plugin:proc:/proc/diskstats", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       for(l = 0; l < lines ;l++) {
-               // --------------------------------------------------------------------------
-               // Read parameters
-
-               char *disk;
-               unsigned long long      major = 0, minor = 0,
-                                                       reads = 0,  mreads = 0,  readsectors = 0,  readms = 0,
-                                                       writes = 0, mwrites = 0, writesectors = 0, writems = 0,
-                                                       queued_ios = 0, busy_ms = 0, backlog_ms = 0,
-                                                       space_avail = 0, space_avail_root = 0, space_used = 0,
-                                                       inodes_avail = 0, inodes_avail_root = 0, inodes_used = 0;
-
-               unsigned long long      last_reads = 0,  last_readsectors = 0,  last_readms = 0,
-                                                       last_writes = 0, last_writesectors = 0, last_writems = 0,
-                                                       last_busy_ms = 0;
-
-               words = procfile_linewords(ff, l);
-               if(words < 14) continue;
-
-               major                   = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
-               minor                   = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-               disk                    = procfile_lineword(ff, l, 2);
-
-               // # of reads completed # of writes completed
-               // This is the total number of reads or writes completed successfully.
-               reads                   = strtoull(procfile_lineword(ff, l, 3), NULL, 10);      // rd_ios
-               writes                  = strtoull(procfile_lineword(ff, l, 7), NULL, 10);      // wr_ios
-
-               // # of reads merged # of writes merged
-               // Reads and writes which are adjacent to each other may be merged for
-           // efficiency.  Thus two 4K reads may become one 8K read before it is
-           // ultimately handed to the disk, and so it will be counted (and queued)
-               mreads                  = strtoull(procfile_lineword(ff, l, 4), NULL, 10);      // rd_merges_or_rd_sec
-               mwrites                 = strtoull(procfile_lineword(ff, l, 8), NULL, 10);      // wr_merges
-
-               // # of sectors read # of sectors written
-               // This is the total number of sectors read or written successfully.
-               readsectors     = strtoull(procfile_lineword(ff, l, 5), NULL, 10);      // rd_sec_or_wr_ios
-               writesectors    = strtoull(procfile_lineword(ff, l, 9), NULL, 10);      // wr_sec
-
-               // # of milliseconds spent reading # of milliseconds spent writing
-               // This is the total number of milliseconds spent by all reads or writes (as
-               // measured from __make_request() to end_that_request_last()).
-               readms                  = strtoull(procfile_lineword(ff, l, 6), NULL, 10);      // rd_ticks_or_wr_sec
-               writems                 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);     // wr_ticks
-
-               // # of I/Os currently in progress
-               // The only field that should go to zero. Incremented as requests are
-               // given to appropriate struct request_queue and decremented as they finish.
-               queued_ios              = strtoull(procfile_lineword(ff, l, 11), NULL, 10);     // ios_pgr
-
-               // # of milliseconds spent doing I/Os
-               // This field increases so long as field queued_ios is nonzero.
-               busy_ms                 = strtoull(procfile_lineword(ff, l, 12), NULL, 10);     // tot_ticks
-
-               // weighted # of milliseconds spent doing I/Os
-               // This field is incremented at each I/O start, I/O completion, I/O
-               // merge, or read of these stats by the number of I/Os in progress
-               // (field queued_ios) times the number of milliseconds spent doing I/O since the
-               // last update of this field.  This can provide an easy measure of both
-               // I/O completion time and the backlog that may be accumulating.
-               backlog_ms              = strtoull(procfile_lineword(ff, l, 13), NULL, 10);     // rq_ticks
-
-
-               // --------------------------------------------------------------------------
-               // remove slashes from disk names
-               char *s;
-               for(s = disk; *s ;s++)
-                       if(*s == '/') *s = '_';
-
-               // --------------------------------------------------------------------------
-               // get a disk structure for the disk
-
-               struct disk *d = get_disk(major, minor, disk);
-
-
-               // --------------------------------------------------------------------------
-               // Set its family based on mount point
-
-               char *family = d->mount_point;
-               if(!family) family = disk;
-
-
-               // --------------------------------------------------------------------------
-               // Check the configuration for the device
-
-               if(unlikely(!d->configured)) {
-                       char var_name[4096 + 1];
-                       snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
-
-                       int def_enable = config_get_boolean_ondemand(var_name, "enable", global_enable_new_disks_detected_at_runtime);
-                       if(def_enable == CONFIG_ONDEMAND_NO) {
-                               // the user does not want any metrics for this disk
-                               d->do_io = CONFIG_ONDEMAND_NO;
-                               d->do_ops = CONFIG_ONDEMAND_NO;
-                               d->do_mops = CONFIG_ONDEMAND_NO;
-                               d->do_iotime = CONFIG_ONDEMAND_NO;
-                               d->do_qops = CONFIG_ONDEMAND_NO;
-                               d->do_util = CONFIG_ONDEMAND_NO;
-                               d->do_backlog = CONFIG_ONDEMAND_NO;
-                               d->do_space = CONFIG_ONDEMAND_NO;
-                               d->do_inodes = CONFIG_ONDEMAND_NO;
-                       }
-                       else {
-                               // this disk is enabled
-                               // check its direct settings
-
-                               int def_performance = CONFIG_ONDEMAND_ONDEMAND;
-                               int def_space = (d->mount_point)?CONFIG_ONDEMAND_ONDEMAND:CONFIG_ONDEMAND_NO;
-
-                               // since this is 'on demand' we can figure the performance settings
-                               // based on the type of disk
-
-                               switch(d->type) {
-                                       case DISK_TYPE_PHYSICAL:
-                                               def_performance = global_enable_performance_for_physical_disks;
-                                               break;
-
-                                       case DISK_TYPE_PARTITION:
-                                               def_performance = global_enable_performance_for_partitions;
-                                               break;
-
-                                       case DISK_TYPE_CONTAINER:
-                                               def_performance = global_enable_performance_for_virtual_disks;
-
-                                               if(d->mount_point)
-                                                       def_performance = select_positive_option(def_performance, global_enable_performance_for_virtual_mountpoints);
-
-                                               break;
-                               }
-
-                               if(d->mount_point)
-                                       def_performance = select_positive_option(def_performance, global_enable_performance_for_mountpoints);
-
-                               // ------------------------------------------------------------
-                               // now we have def_performance and def_space
-                               // to work further
-
-                               // def_performance
-                               // check the user configuration (this will also show our 'on demand' decision)
-                               def_performance = config_get_boolean_ondemand(var_name, "enable performance metrics", def_performance);
-
-                               int ddo_io = CONFIG_ONDEMAND_NO,
-                                       ddo_ops = CONFIG_ONDEMAND_NO,
-                                       ddo_mops = CONFIG_ONDEMAND_NO,
-                                       ddo_iotime = CONFIG_ONDEMAND_NO,
-                                       ddo_qops = CONFIG_ONDEMAND_NO,
-                                       ddo_util = CONFIG_ONDEMAND_NO,
-                                       ddo_backlog = CONFIG_ONDEMAND_NO;
-
-                               // we enable individual performance charts only when def_performance is not disabled
-                               if(def_performance != CONFIG_ONDEMAND_NO) {
-                                       ddo_io = global_do_io,
-                                       ddo_ops = global_do_ops,
-                                       ddo_mops = global_do_mops,
-                                       ddo_iotime = global_do_iotime,
-                                       ddo_qops = global_do_qops,
-                                       ddo_util = global_do_util,
-                                       ddo_backlog = global_do_backlog;
-                               }
-
-                               d->do_io      = config_get_boolean_ondemand(var_name, "bandwidth", ddo_io);
-                               d->do_ops     = config_get_boolean_ondemand(var_name, "operations", ddo_ops);
-                               d->do_mops    = config_get_boolean_ondemand(var_name, "merged operations", ddo_mops);
-                               d->do_iotime  = config_get_boolean_ondemand(var_name, "i/o time", ddo_iotime);
-                               d->do_qops    = config_get_boolean_ondemand(var_name, "queued operations", ddo_qops);
-                               d->do_util    = config_get_boolean_ondemand(var_name, "utilization percentage", ddo_util);
-                               d->do_backlog = config_get_boolean_ondemand(var_name, "backlog", ddo_backlog);
-
-                               // def_space
-                               if(d->mount_point) {
-                                       // check the user configuration (this will also show our 'on demand' decision)
-                                       def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space);
-
-                                       int ddo_space = def_space,
-                                               ddo_inodes = def_space;
-
-                                       d->do_space = config_get_boolean_ondemand(var_name, "space usage", ddo_space);
-                                       d->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", ddo_inodes);
-                               }
-                               else {
-                                       // don't show settings for this disk
-                                       d->do_space = CONFIG_ONDEMAND_NO;
-                                       d->do_inodes = CONFIG_ONDEMAND_NO;
-                               }
-                       }
-
-                       d->configured = 1;
-               }
-
-               RRDSET *st;
-
-               // --------------------------------------------------------------------------
-               // Do performance metrics
-
-               if(d->do_io == CONFIG_ONDEMAND_YES || (d->do_io == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) {
-                       d->do_io = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
-                       if(!st) {
-                               st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
-
-                               rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       last_readsectors  = rrddim_set(st, "reads", readsectors);
-                       last_writesectors = rrddim_set(st, "writes", writesectors);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes))) {
-                       d->do_ops = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype("disk_ops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       last_reads  = rrddim_set(st, "reads", reads);
-                       last_writes = rrddim_set(st, "writes", writes);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(d->do_qops == CONFIG_ONDEMAND_YES || (d->do_qops == CONFIG_ONDEMAND_ONDEMAND && queued_ios)) {
-                       d->do_qops = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype("disk_qops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       rrddim_set(st, "operations", queued_ios);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(d->do_backlog == CONFIG_ONDEMAND_YES || (d->do_backlog == CONFIG_ONDEMAND_ONDEMAND && backlog_ms)) {
-                       d->do_backlog = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype("disk_backlog", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       rrddim_set(st, "backlog", backlog_ms);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) {
-                       d->do_util = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype("disk_util", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       last_busy_ms = rrddim_set(st, "utilization", busy_ms);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(d->do_mops == CONFIG_ONDEMAND_YES || (d->do_mops == CONFIG_ONDEMAND_ONDEMAND && (mreads || mwrites))) {
-                       d->do_mops = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype("disk_mops", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       rrddim_set(st, "reads", mreads);
-                       rrddim_set(st, "writes", mwrites);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) {
-                       d->do_iotime = CONFIG_ONDEMAND_YES;
-
-                       st = rrdset_find_bytype("disk_iotime", disk);
-                       if(!st) {
-                               st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next_usec(st, dt);
-
-                       last_readms  = rrddim_set(st, "reads", readms);
-                       last_writems = rrddim_set(st, "writes", writems);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-               // calculate differential charts
-               // only if this is not the first time we run
-
-               if(dt) {
-                       if( (d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) &&
-                               (d->do_ops    == CONFIG_ONDEMAND_YES || (d->do_ops    == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
-                               st = rrdset_find_bytype("disk_await", disk);
-                               if(!st) {
-                                       st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
-                               }
-                               else rrdset_next_usec(st, dt);
-
-                               rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
-                               rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
-                               rrdset_done(st);
-                       }
-
-                       if( (d->do_io  == CONFIG_ONDEMAND_YES || (d->do_io  == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) &&
-                               (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
-                               st = rrdset_find_bytype("disk_avgsz", disk);
-                               if(!st) {
-                                       st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_ABSOLUTE);
-                               }
-                               else rrdset_next_usec(st, dt);
-
-                               rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
-                               rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
-                               rrdset_done(st);
-                       }
-
-                       if( (d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) &&
-                               (d->do_ops  == CONFIG_ONDEMAND_YES || (d->do_ops  == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
-                               st = rrdset_find_bytype("disk_svctm", disk);
-                               if(!st) {
-                                       st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                               }
-                               else rrdset_next_usec(st, dt);
-
-                               rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0);
-                               rrdset_done(st);
-                       }
-               }
-
-               // --------------------------------------------------------------------------
-               // space metrics
-
-               if(d->mount_point && (d->do_space || d->do_inodes) ) {
-                       // collect space metrics using statvfs
-
-                       if (statvfs(d->mount_point, &buff_statvfs) < 0)
-                               error("Failed statvfs() for '%s' (disk '%s')", d->mount_point, d->disk);
-                       else {
-                               space_avail = buff_statvfs.f_bavail * buff_statvfs.f_bsize;
-                               space_avail_root = (buff_statvfs.f_bfree - buff_statvfs.f_bavail) * buff_statvfs.f_bsize;
-                               space_used = (buff_statvfs.f_blocks - buff_statvfs.f_bfree) * buff_statvfs.f_bsize;
-
-                               inodes_avail = buff_statvfs.f_favail;
-                               inodes_avail_root = buff_statvfs.f_ffree - buff_statvfs.f_favail;
-                               inodes_used = buff_statvfs.f_files - buff_statvfs.f_ffree;
-
-                               // verify we collected the metrics for the right disk.
-                               // if not the mountpoint has changed.
-
-                               if(stat(d->mount_point, &buff_stat) == -1)
-                                       error("Failed to stat() for '%s' (disk '%s')", d->mount_point, d->disk);
-                               else {
-                                       if(major(buff_stat.st_dev) == major && minor(buff_stat.st_dev) == minor) {
-
-                                               // --------------------------------------------------------------------------
-
-                                               if(d->do_space == CONFIG_ONDEMAND_YES || (d->do_space == CONFIG_ONDEMAND_ONDEMAND && (space_avail || space_avail_root || space_used))) {
-                                                       st = rrdset_find_bytype("disk_space", disk);
-                                                       if(!st) {
-                                                               st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED);
-                                                               st->isdetail = 1;
-
-                                                               rrddim_add(st, "avail", NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
-                                                               rrddim_add(st, "used" , NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
-                                                               rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
-                                                       }
-                                                       else rrdset_next_usec(st, dt);
-
-                                                       rrddim_set(st, "avail", space_avail);
-                                                       rrddim_set(st, "used", space_used);
-                                                       rrddim_set(st, "reserved_for_root", space_avail_root);
-                                                       rrdset_done(st);
-                                               }
-
-                                               // --------------------------------------------------------------------------
-
-                                               if(d->do_inodes == CONFIG_ONDEMAND_YES || (d->do_inodes == CONFIG_ONDEMAND_ONDEMAND && (inodes_avail || inodes_avail_root || inodes_used))) {
-                                                       st = rrdset_find_bytype("disk_inodes", disk);
-                                                       if(!st) {
-                                                               st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
-                                                               st->isdetail = 1;
-
-                                                               rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                                                               rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
-                                                               rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
-                                                       }
-                                                       else rrdset_next_usec(st, dt);
-
-                                                       rrddim_set(st, "avail", inodes_avail);
-                                                       rrddim_set(st, "used", inodes_used);
-                                                       rrddim_set(st, "reserved_for_root", inodes_avail_root);
-                                                       rrdset_done(st);
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+    static struct statvfs buff_statvfs;
+    static struct stat buff_stat;
+    static int  global_enable_new_disks_detected_at_runtime = CONFIG_ONDEMAND_YES,
+                global_enable_performance_for_physical_disks = CONFIG_ONDEMAND_ONDEMAND,
+                global_enable_performance_for_virtual_disks = CONFIG_ONDEMAND_NO,
+                global_enable_performance_for_partitions = CONFIG_ONDEMAND_NO,
+                global_enable_performance_for_mountpoints = CONFIG_ONDEMAND_NO,
+                global_enable_performance_for_virtual_mountpoints = CONFIG_ONDEMAND_ONDEMAND,
+                global_enable_space_for_mountpoints = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_io = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_ops = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_mops = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_iotime = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_qops = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_util = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_backlog = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_space = CONFIG_ONDEMAND_ONDEMAND,
+                global_do_inodes = CONFIG_ONDEMAND_ONDEMAND,
+                globals_initialized = 0;
+
+    if(unlikely(!globals_initialized)) {
+        global_enable_new_disks_detected_at_runtime = config_get_boolean("plugin:proc:/proc/diskstats", "enable new disks detected at runtime", global_enable_new_disks_detected_at_runtime);
+
+        global_enable_performance_for_physical_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for physical disks", global_enable_performance_for_physical_disks);
+        global_enable_performance_for_virtual_disks = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for virtual disks", global_enable_performance_for_virtual_disks);
+        global_enable_performance_for_partitions = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for partitions", global_enable_performance_for_partitions);
+        global_enable_performance_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted filesystems", global_enable_performance_for_mountpoints);
+        global_enable_performance_for_virtual_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "performance metrics for mounted virtual disks", global_enable_performance_for_virtual_mountpoints);
+        global_enable_space_for_mountpoints = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space metrics for mounted filesystems", global_enable_space_for_mountpoints);
+
+        global_do_io      = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "bandwidth for all disks", global_do_io);
+        global_do_ops     = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "operations for all disks", global_do_ops);
+        global_do_mops    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "merged operations for all disks", global_do_mops);
+        global_do_iotime  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "i/o time for all disks", global_do_iotime);
+        global_do_qops    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "queued operations for all disks", global_do_qops);
+        global_do_util    = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "utilization percentage for all disks", global_do_util);
+        global_do_backlog = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "backlog for all disks", global_do_backlog);
+        global_do_space   = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "space usage for all disks", global_do_space);
+        global_do_inodes  = config_get_boolean_ondemand("plugin:proc:/proc/diskstats", "inodes usage for all disks", global_do_inodes);
+
+        globals_initialized = 1;
+    }
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/diskstats");
+        ff = procfile_open(config_get("plugin:proc:/proc/diskstats", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    for(l = 0; l < lines ;l++) {
+        // --------------------------------------------------------------------------
+        // Read parameters
+
+        char *disk;
+        unsigned long long  major = 0, minor = 0,
+                            reads = 0,  mreads = 0,  readsectors = 0,  readms = 0,
+                            writes = 0, mwrites = 0, writesectors = 0, writems = 0,
+                            queued_ios = 0, busy_ms = 0, backlog_ms = 0,
+                            space_avail = 0, space_avail_root = 0, space_used = 0,
+                            inodes_avail = 0, inodes_avail_root = 0, inodes_used = 0;
+
+        unsigned long long  last_reads = 0,  last_readsectors = 0,  last_readms = 0,
+                            last_writes = 0, last_writesectors = 0, last_writems = 0,
+                            last_busy_ms = 0;
+
+        words = procfile_linewords(ff, l);
+        if(words < 14) continue;
+
+        major           = strtoull(procfile_lineword(ff, l, 0), NULL, 10);
+        minor           = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+        disk            = procfile_lineword(ff, l, 2);
+
+        // # of reads completed # of writes completed
+        // This is the total number of reads or writes completed successfully.
+        reads           = strtoull(procfile_lineword(ff, l, 3), NULL, 10);  // rd_ios
+        writes          = strtoull(procfile_lineword(ff, l, 7), NULL, 10);  // wr_ios
+
+        // # of reads merged # of writes merged
+        // Reads and writes which are adjacent to each other may be merged for
+        // efficiency.  Thus two 4K reads may become one 8K read before it is
+        // ultimately handed to the disk, and so it will be counted (and queued)
+        mreads          = strtoull(procfile_lineword(ff, l, 4), NULL, 10);  // rd_merges_or_rd_sec
+        mwrites         = strtoull(procfile_lineword(ff, l, 8), NULL, 10);  // wr_merges
+
+        // # of sectors read # of sectors written
+        // This is the total number of sectors read or written successfully.
+        readsectors     = strtoull(procfile_lineword(ff, l, 5), NULL, 10);  // rd_sec_or_wr_ios
+        writesectors    = strtoull(procfile_lineword(ff, l, 9), NULL, 10);  // wr_sec
+
+        // # of milliseconds spent reading # of milliseconds spent writing
+        // This is the total number of milliseconds spent by all reads or writes (as
+        // measured from __make_request() to end_that_request_last()).
+        readms          = strtoull(procfile_lineword(ff, l, 6), NULL, 10);  // rd_ticks_or_wr_sec
+        writems         = strtoull(procfile_lineword(ff, l, 10), NULL, 10); // wr_ticks
+
+        // # of I/Os currently in progress
+        // The only field that should go to zero. Incremented as requests are
+        // given to appropriate struct request_queue and decremented as they finish.
+        queued_ios      = strtoull(procfile_lineword(ff, l, 11), NULL, 10); // ios_pgr
+
+        // # of milliseconds spent doing I/Os
+        // This field increases so long as field queued_ios is nonzero.
+        busy_ms         = strtoull(procfile_lineword(ff, l, 12), NULL, 10); // tot_ticks
+
+        // weighted # of milliseconds spent doing I/Os
+        // This field is incremented at each I/O start, I/O completion, I/O
+        // merge, or read of these stats by the number of I/Os in progress
+        // (field queued_ios) times the number of milliseconds spent doing I/O since the
+        // last update of this field.  This can provide an easy measure of both
+        // I/O completion time and the backlog that may be accumulating.
+        backlog_ms      = strtoull(procfile_lineword(ff, l, 13), NULL, 10); // rq_ticks
+
+
+        // --------------------------------------------------------------------------
+        // remove slashes from disk names
+        char *s;
+        for(s = disk; *s ;s++)
+            if(*s == '/') *s = '_';
+
+        // --------------------------------------------------------------------------
+        // get a disk structure for the disk
+
+        struct disk *d = get_disk(major, minor, disk);
+
+
+        // --------------------------------------------------------------------------
+        // Set its family based on mount point
+
+        char *family = d->mount_point;
+        if(!family) family = disk;
+
+
+        // --------------------------------------------------------------------------
+        // Check the configuration for the device
+
+        if(unlikely(!d->configured)) {
+            char var_name[4096 + 1];
+            snprintfz(var_name, 4096, "plugin:proc:/proc/diskstats:%s", disk);
+
+            int def_enable = config_get_boolean_ondemand(var_name, "enable", global_enable_new_disks_detected_at_runtime);
+            if(def_enable == CONFIG_ONDEMAND_NO) {
+                // the user does not want any metrics for this disk
+                d->do_io = CONFIG_ONDEMAND_NO;
+                d->do_ops = CONFIG_ONDEMAND_NO;
+                d->do_mops = CONFIG_ONDEMAND_NO;
+                d->do_iotime = CONFIG_ONDEMAND_NO;
+                d->do_qops = CONFIG_ONDEMAND_NO;
+                d->do_util = CONFIG_ONDEMAND_NO;
+                d->do_backlog = CONFIG_ONDEMAND_NO;
+                d->do_space = CONFIG_ONDEMAND_NO;
+                d->do_inodes = CONFIG_ONDEMAND_NO;
+            }
+            else {
+                // this disk is enabled
+                // check its direct settings
+
+                int def_performance = CONFIG_ONDEMAND_ONDEMAND;
+                int def_space = (d->mount_point)?CONFIG_ONDEMAND_ONDEMAND:CONFIG_ONDEMAND_NO;
+
+                // since this is 'on demand' we can figure the performance settings
+                // based on the type of disk
+
+                switch(d->type) {
+                    case DISK_TYPE_PHYSICAL:
+                        def_performance = global_enable_performance_for_physical_disks;
+                        break;
+
+                    case DISK_TYPE_PARTITION:
+                        def_performance = global_enable_performance_for_partitions;
+                        break;
+
+                    case DISK_TYPE_CONTAINER:
+                        def_performance = global_enable_performance_for_virtual_disks;
+
+                        if(d->mount_point)
+                            def_performance = select_positive_option(def_performance, global_enable_performance_for_virtual_mountpoints);
+
+                        break;
+                }
+
+                if(d->mount_point)
+                    def_performance = select_positive_option(def_performance, global_enable_performance_for_mountpoints);
+
+                // ------------------------------------------------------------
+                // now we have def_performance and def_space
+                // to work further
+
+                // def_performance
+                // check the user configuration (this will also show our 'on demand' decision)
+                def_performance = config_get_boolean_ondemand(var_name, "enable performance metrics", def_performance);
+
+                int ddo_io = CONFIG_ONDEMAND_NO,
+                    ddo_ops = CONFIG_ONDEMAND_NO,
+                    ddo_mops = CONFIG_ONDEMAND_NO,
+                    ddo_iotime = CONFIG_ONDEMAND_NO,
+                    ddo_qops = CONFIG_ONDEMAND_NO,
+                    ddo_util = CONFIG_ONDEMAND_NO,
+                    ddo_backlog = CONFIG_ONDEMAND_NO;
+
+                // we enable individual performance charts only when def_performance is not disabled
+                if(def_performance != CONFIG_ONDEMAND_NO) {
+                    ddo_io = global_do_io,
+                    ddo_ops = global_do_ops,
+                    ddo_mops = global_do_mops,
+                    ddo_iotime = global_do_iotime,
+                    ddo_qops = global_do_qops,
+                    ddo_util = global_do_util,
+                    ddo_backlog = global_do_backlog;
+                }
+
+                d->do_io      = config_get_boolean_ondemand(var_name, "bandwidth", ddo_io);
+                d->do_ops     = config_get_boolean_ondemand(var_name, "operations", ddo_ops);
+                d->do_mops    = config_get_boolean_ondemand(var_name, "merged operations", ddo_mops);
+                d->do_iotime  = config_get_boolean_ondemand(var_name, "i/o time", ddo_iotime);
+                d->do_qops    = config_get_boolean_ondemand(var_name, "queued operations", ddo_qops);
+                d->do_util    = config_get_boolean_ondemand(var_name, "utilization percentage", ddo_util);
+                d->do_backlog = config_get_boolean_ondemand(var_name, "backlog", ddo_backlog);
+
+                // def_space
+                if(d->mount_point) {
+                    // check the user configuration (this will also show our 'on demand' decision)
+                    def_space = config_get_boolean_ondemand(var_name, "enable space metrics", def_space);
+
+                    int ddo_space = def_space,
+                        ddo_inodes = def_space;
+
+                    d->do_space = config_get_boolean_ondemand(var_name, "space usage", ddo_space);
+                    d->do_inodes = config_get_boolean_ondemand(var_name, "inodes usage", ddo_inodes);
+                }
+                else {
+                    // don't show settings for this disk
+                    d->do_space = CONFIG_ONDEMAND_NO;
+                    d->do_inodes = CONFIG_ONDEMAND_NO;
+                }
+            }
+
+            d->configured = 1;
+        }
+
+        RRDSET *st;
+
+        // --------------------------------------------------------------------------
+        // Do performance metrics
+
+        if(d->do_io == CONFIG_ONDEMAND_YES || (d->do_io == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) {
+            d->do_io = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype(RRD_TYPE_DISK, disk);
+            if(!st) {
+                st = rrdset_create(RRD_TYPE_DISK, disk, NULL, family, "disk.io", "Disk I/O Bandwidth", "kilobytes/s", 2000, update_every, RRDSET_TYPE_AREA);
+
+                rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next_usec(st, dt);
+
+            last_readsectors  = rrddim_set(st, "reads", readsectors);
+            last_writesectors = rrddim_set(st, "writes", writesectors);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes))) {
+            d->do_ops = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype("disk_ops", disk);
+            if(!st) {
+                st = rrdset_create("disk_ops", disk, NULL, family, "disk.ops", "Disk Completed I/O Operations", "operations/s", 2001, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next_usec(st, dt);
+
+            last_reads  = rrddim_set(st, "reads", reads);
+            last_writes = rrddim_set(st, "writes", writes);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(d->do_qops == CONFIG_ONDEMAND_YES || (d->do_qops == CONFIG_ONDEMAND_ONDEMAND && queued_ios)) {
+            d->do_qops = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype("disk_qops", disk);
+            if(!st) {
+                st = rrdset_create("disk_qops", disk, NULL, family, "disk.qops", "Disk Current I/O Operations", "operations", 2002, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "operations", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            }
+            else rrdset_next_usec(st, dt);
+
+            rrddim_set(st, "operations", queued_ios);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(d->do_backlog == CONFIG_ONDEMAND_YES || (d->do_backlog == CONFIG_ONDEMAND_ONDEMAND && backlog_ms)) {
+            d->do_backlog = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype("disk_backlog", disk);
+            if(!st) {
+                st = rrdset_create("disk_backlog", disk, NULL, family, "disk.backlog", "Disk Backlog", "backlog (ms)", 2003, update_every, RRDSET_TYPE_AREA);
+                st->isdetail = 1;
+
+                rrddim_add(st, "backlog", NULL, 1, 10, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next_usec(st, dt);
+
+            rrddim_set(st, "backlog", backlog_ms);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) {
+            d->do_util = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype("disk_util", disk);
+            if(!st) {
+                st = rrdset_create("disk_util", disk, NULL, family, "disk.util", "Disk Utilization Time", "% of time working", 2004, update_every, RRDSET_TYPE_AREA);
+                st->isdetail = 1;
+
+                rrddim_add(st, "utilization", NULL, 1, 10, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next_usec(st, dt);
+
+            last_busy_ms = rrddim_set(st, "utilization", busy_ms);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(d->do_mops == CONFIG_ONDEMAND_YES || (d->do_mops == CONFIG_ONDEMAND_ONDEMAND && (mreads || mwrites))) {
+            d->do_mops = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype("disk_mops", disk);
+            if(!st) {
+                st = rrdset_create("disk_mops", disk, NULL, family, "disk.mops", "Disk Merged Operations", "merged operations/s", 2021, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next_usec(st, dt);
+
+            rrddim_set(st, "reads", mreads);
+            rrddim_set(st, "writes", mwrites);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) {
+            d->do_iotime = CONFIG_ONDEMAND_YES;
+
+            st = rrdset_find_bytype("disk_iotime", disk);
+            if(!st) {
+                st = rrdset_create("disk_iotime", disk, NULL, family, "disk.iotime", "Disk Total I/O Time", "milliseconds/s", 2022, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next_usec(st, dt);
+
+            last_readms  = rrddim_set(st, "reads", readms);
+            last_writems = rrddim_set(st, "writes", writems);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+        // calculate differential charts
+        // only if this is not the first time we run
+
+        if(dt) {
+            if( (d->do_iotime == CONFIG_ONDEMAND_YES || (d->do_iotime == CONFIG_ONDEMAND_ONDEMAND && (readms || writems))) &&
+                (d->do_ops    == CONFIG_ONDEMAND_YES || (d->do_ops    == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
+                st = rrdset_find_bytype("disk_await", disk);
+                if(!st) {
+                    st = rrdset_create("disk_await", disk, NULL, family, "disk.await", "Average Completed I/O Operation Time", "ms per operation", 2005, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "reads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "writes", NULL, -1, 1, RRDDIM_ABSOLUTE);
+                }
+                else rrdset_next_usec(st, dt);
+
+                rrddim_set(st, "reads", (reads - last_reads) ? (readms - last_readms) / (reads - last_reads) : 0);
+                rrddim_set(st, "writes", (writes - last_writes) ? (writems - last_writems) / (writes - last_writes) : 0);
+                rrdset_done(st);
+            }
+
+            if( (d->do_io  == CONFIG_ONDEMAND_YES || (d->do_io  == CONFIG_ONDEMAND_ONDEMAND && (readsectors || writesectors))) &&
+                (d->do_ops == CONFIG_ONDEMAND_YES || (d->do_ops == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
+                st = rrdset_find_bytype("disk_avgsz", disk);
+                if(!st) {
+                    st = rrdset_create("disk_avgsz", disk, NULL, family, "disk.avgsz", "Average Completed I/O Operation Bandwidth", "kilobytes per operation", 2006, update_every, RRDSET_TYPE_AREA);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "reads", NULL, d->sector_size, 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "writes", NULL, d->sector_size * -1, 1024, RRDDIM_ABSOLUTE);
+                }
+                else rrdset_next_usec(st, dt);
+
+                rrddim_set(st, "reads", (reads - last_reads) ? (readsectors - last_readsectors) / (reads - last_reads) : 0);
+                rrddim_set(st, "writes", (writes - last_writes) ? (writesectors - last_writesectors) / (writes - last_writes) : 0);
+                rrdset_done(st);
+            }
+
+            if( (d->do_util == CONFIG_ONDEMAND_YES || (d->do_util == CONFIG_ONDEMAND_ONDEMAND && busy_ms)) &&
+                (d->do_ops  == CONFIG_ONDEMAND_YES || (d->do_ops  == CONFIG_ONDEMAND_ONDEMAND && (reads || writes)))) {
+                st = rrdset_find_bytype("disk_svctm", disk);
+                if(!st) {
+                    st = rrdset_create("disk_svctm", disk, NULL, family, "disk.svctm", "Average Service Time", "ms per operation", 2007, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "svctm", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                }
+                else rrdset_next_usec(st, dt);
+
+                rrddim_set(st, "svctm", ((reads - last_reads) + (writes - last_writes)) ? (busy_ms - last_busy_ms) / ((reads - last_reads) + (writes - last_writes)) : 0);
+                rrdset_done(st);
+            }
+        }
+
+        // --------------------------------------------------------------------------
+        // space metrics
+
+        if(d->mount_point && (d->do_space || d->do_inodes) ) {
+            // collect space metrics using statvfs
+
+            if (statvfs(d->mount_point, &buff_statvfs) < 0)
+                error("Failed statvfs() for '%s' (disk '%s')", d->mount_point, d->disk);
+            else {
+                space_avail = buff_statvfs.f_bavail * buff_statvfs.f_bsize;
+                space_avail_root = (buff_statvfs.f_bfree - buff_statvfs.f_bavail) * buff_statvfs.f_bsize;
+                space_used = (buff_statvfs.f_blocks - buff_statvfs.f_bfree) * buff_statvfs.f_bsize;
+
+                inodes_avail = buff_statvfs.f_favail;
+                inodes_avail_root = buff_statvfs.f_ffree - buff_statvfs.f_favail;
+                inodes_used = buff_statvfs.f_files - buff_statvfs.f_ffree;
+
+                // verify we collected the metrics for the right disk.
+                // if not the mountpoint has changed.
+
+                if(stat(d->mount_point, &buff_stat) == -1)
+                    error("Failed to stat() for '%s' (disk '%s')", d->mount_point, d->disk);
+                else {
+                    if(major(buff_stat.st_dev) == major && minor(buff_stat.st_dev) == minor) {
+
+                        // --------------------------------------------------------------------------
+
+                        if(d->do_space == CONFIG_ONDEMAND_YES || (d->do_space == CONFIG_ONDEMAND_ONDEMAND && (space_avail || space_avail_root || space_used))) {
+                            st = rrdset_find_bytype("disk_space", disk);
+                            if(!st) {
+                                st = rrdset_create("disk_space", disk, NULL, family, "disk.space", "Disk Space Usage", "GB", 2023, update_every, RRDSET_TYPE_STACKED);
+                                st->isdetail = 1;
+
+                                rrddim_add(st, "avail", NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "used" , NULL, 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1024*1024*1024, RRDDIM_ABSOLUTE);
+                            }
+                            else rrdset_next_usec(st, dt);
+
+                            rrddim_set(st, "avail", space_avail);
+                            rrddim_set(st, "used", space_used);
+                            rrddim_set(st, "reserved_for_root", space_avail_root);
+                            rrdset_done(st);
+                        }
+
+                        // --------------------------------------------------------------------------
+
+                        if(d->do_inodes == CONFIG_ONDEMAND_YES || (d->do_inodes == CONFIG_ONDEMAND_ONDEMAND && (inodes_avail || inodes_avail_root || inodes_used))) {
+                            st = rrdset_find_bytype("disk_inodes", disk);
+                            if(!st) {
+                                st = rrdset_create("disk_inodes", disk, NULL, family, "disk.inodes", "Disk Inodes Usage", "Inodes", 2024, update_every, RRDSET_TYPE_STACKED);
+                                st->isdetail = 1;
+
+                                rrddim_add(st, "avail", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "used" , NULL, 1, 1, RRDDIM_ABSOLUTE);
+                                rrddim_add(st, "reserved_for_root", "reserved for root", 1, 1, RRDDIM_ABSOLUTE);
+                            }
+                            else rrdset_next_usec(st, dt);
+
+                            rrddim_set(st, "avail", inodes_avail);
+                            rrddim_set(st, "used", inodes_used);
+                            rrddim_set(st, "reserved_for_root", inodes_avail_root);
+                            rrdset_done(st);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return 0;
 }
index ad00c2022af532178f03cdc1c5b6cc5fd9788fb8..fe0e9b1a57635a43bc1cd139a74e59f0e10b9ebf 100644 (file)
@@ -1,26 +1,13 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
 
 #define MAX_INTERRUPT_NAME 50
 
 struct interrupt {
-       int used;
-       char *id;
-       char name[MAX_INTERRUPT_NAME + 1];
-       unsigned long long total;
-       unsigned long long value[];
+    int used;
+    char *id;
+    char name[MAX_INTERRUPT_NAME + 1];
+    unsigned long long total;
+    unsigned long long value[];
 };
 
 // since each interrupt is variable in size
@@ -31,160 +18,157 @@ struct interrupt {
 #define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[line * recordsize(cpus)])
 
 static inline struct interrupt *get_interrupts_array(int lines, int cpus) {
-       static struct interrupt *irrs = NULL;
-       static int allocated = 0;
-
-       if(lines < allocated) return irrs;
-       else {
-               irrs = (struct interrupt *)realloc(irrs, lines * recordsize(cpus));
-               if(!irrs)
-                       fatal("Cannot allocate memory for %d interrupts", lines);
+    static struct interrupt *irrs = NULL;
+    static int allocated = 0;
 
-               allocated = lines;
-       }
+    if(lines < allocated) return irrs;
+    else {
+        irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
+        allocated = lines;
+    }
 
-       return irrs;
+    return irrs;
 }
 
 int do_proc_interrupts(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int cpus = -1, do_per_core = -1;
-       struct interrupt *irrs = NULL;
-
-       if(dt) {};
-
-       if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1);
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts");
-               ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words = procfile_linewords(ff, 0), w;
-
-       if(!lines) {
-               error("Cannot read /proc/interrupts, zero lines reported.");
-               return 1;
-       }
-
-       // find how many CPUs are there
-       if(cpus == -1) {
-               cpus = 0;
-               for(w = 0; w < words ; w++) {
-                       if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
-                               cpus++;
-               }
-       }
-
-       if(!cpus) {
-               error("PLUGIN: PROC_INTERRUPTS: Cannot find the number of CPUs in /proc/interrupts");
-               return 1;
-       }
-
-       // allocate the size we need;
-       irrs = get_interrupts_array(lines, cpus);
-       irrs[0].used = 0;
-
-       // loop through all lines
-       for(l = 1; l < lines ;l++) {
-               struct interrupt *irr = irrindex(irrs, l, cpus);
-               irr->used = 0;
-               irr->total = 0;
-
-               words = procfile_linewords(ff, l);
-               if(!words) continue;
-
-               irr->id = procfile_lineword(ff, l, 0);
-               if(!irr->id || !irr->id[0]) continue;
-
-               int idlen = strlen(irr->id);
-               if(irr->id[idlen - 1] == ':')
-                       irr->id[idlen - 1] = '\0';
-
-               int c;
-               for(c = 0; c < cpus ;c++) {
-                       if((c + 1) < (int)words)
-                               irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
-                       else
-                               irr->value[c] = 0;
-
-                       irr->total += irr->value[c];
-               }
-
-               if(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words) {
-                       strncpyz(irr->name, procfile_lineword(ff, l, words - 1), MAX_INTERRUPT_NAME);
-                       int nlen = strlen(irr->name);
-                       if(nlen < (MAX_INTERRUPT_NAME-1)) {
-                               irr->name[nlen] = '_';
-                               strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen);
-                       }
-               }
-               else {
-                       strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
-               }
-
-               irr->used = 1;
-       }
-
-       RRDSET *st;
-
-       // --------------------------------------------------------------------
-
-       st = rrdset_find_bytype("system", "interrupts");
-       if(!st) {
-               st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED);
-
-               for(l = 0; l < lines ;l++) {
-                       struct interrupt *irr = irrindex(irrs, l, cpus);
-                       if(!irr->used) continue;
-                       rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
-               }
-       }
-       else rrdset_next(st);
-
-       for(l = 0; l < lines ;l++) {
-               struct interrupt *irr = irrindex(irrs, l, cpus);
-               if(!irr->used) continue;
-               rrddim_set(st, irr->id, irr->total);
-       }
-       rrdset_done(st);
-
-       if(do_per_core) {
-               int c;
-
-               for(c = 0; c < cpus ; c++) {
-                       char id[256+1];
-                       snprintfz(id, 256, "cpu%d_interrupts", c);
-
-                       st = rrdset_find_bytype("cpu", id);
-                       if(!st) {
-                               char name[256+1], title[256+1];
-                               snprintfz(name, 256, "cpu%d_interrupts", c);
-                               snprintfz(title, 256, "CPU%d Interrupts", c);
-                               st = rrdset_create("cpu", id, name, "interrupts", "cpu.interrupts", title, "interrupts/s", 2000 + c, update_every, RRDSET_TYPE_STACKED);
-
-                               for(l = 0; l < lines ;l++) {
-                                       struct interrupt *irr = irrindex(irrs, l, cpus);
-                                       if(!irr->used) continue;
-                                       rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                       }
-                       else rrdset_next(st);
-
-                       for(l = 0; l < lines ;l++) {
-                               struct interrupt *irr = irrindex(irrs, l, cpus);
-                               if(!irr->used) continue;
-                               rrddim_set(st, irr->id, irr->value[c]);
-                       }
-                       rrdset_done(st);
-               }
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+    static int cpus = -1, do_per_core = -1;
+    struct interrupt *irrs = NULL;
+
+    if(dt) {};
+
+    if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1);
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts");
+        ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words = procfile_linewords(ff, 0), w;
+
+    if(!lines) {
+        error("Cannot read /proc/interrupts, zero lines reported.");
+        return 1;
+    }
+
+    // find how many CPUs are there
+    if(cpus == -1) {
+        cpus = 0;
+        for(w = 0; w < words ; w++) {
+            if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
+                cpus++;
+        }
+    }
+
+    if(!cpus) {
+        error("PLUGIN: PROC_INTERRUPTS: Cannot find the number of CPUs in /proc/interrupts");
+        return 1;
+    }
+
+    // allocate the size we need;
+    irrs = get_interrupts_array(lines, cpus);
+    irrs[0].used = 0;
+
+    // loop through all lines
+    for(l = 1; l < lines ;l++) {
+        struct interrupt *irr = irrindex(irrs, l, cpus);
+        irr->used = 0;
+        irr->total = 0;
+
+        words = procfile_linewords(ff, l);
+        if(!words) continue;
+
+        irr->id = procfile_lineword(ff, l, 0);
+        if(!irr->id || !irr->id[0]) continue;
+
+        int idlen = strlen(irr->id);
+        if(irr->id[idlen - 1] == ':')
+            irr->id[idlen - 1] = '\0';
+
+        int c;
+        for(c = 0; c < cpus ;c++) {
+            if((c + 1) < (int)words)
+                irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
+            else
+                irr->value[c] = 0;
+
+            irr->total += irr->value[c];
+        }
+
+        if(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words) {
+            strncpyz(irr->name, procfile_lineword(ff, l, words - 1), MAX_INTERRUPT_NAME);
+            int nlen = strlen(irr->name);
+            if(nlen < (MAX_INTERRUPT_NAME-1)) {
+                irr->name[nlen] = '_';
+                strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen);
+            }
+        }
+        else {
+            strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
+        }
+
+        irr->used = 1;
+    }
+
+    RRDSET *st;
+
+    // --------------------------------------------------------------------
+
+    st = rrdset_find_bytype("system", "interrupts");
+    if(!st) {
+        st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED);
+
+        for(l = 0; l < lines ;l++) {
+            struct interrupt *irr = irrindex(irrs, l, cpus);
+            if(!irr->used) continue;
+            rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+        }
+    }
+    else rrdset_next(st);
+
+    for(l = 0; l < lines ;l++) {
+        struct interrupt *irr = irrindex(irrs, l, cpus);
+        if(!irr->used) continue;
+        rrddim_set(st, irr->id, irr->total);
+    }
+    rrdset_done(st);
+
+    if(do_per_core) {
+        int c;
+
+        for(c = 0; c < cpus ; c++) {
+            char id[256+1];
+            snprintfz(id, 256, "cpu%d_interrupts", c);
+
+            st = rrdset_find_bytype("cpu", id);
+            if(!st) {
+                char name[256+1], title[256+1];
+                snprintfz(name, 256, "cpu%d_interrupts", c);
+                snprintfz(title, 256, "CPU%d Interrupts", c);
+                st = rrdset_create("cpu", id, name, "interrupts", "cpu.interrupts", title, "interrupts/s", 2000 + c, update_every, RRDSET_TYPE_STACKED);
+
+                for(l = 0; l < lines ;l++) {
+                    struct interrupt *irr = irrindex(irrs, l, cpus);
+                    if(!irr->used) continue;
+                    rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+                }
+            }
+            else rrdset_next(st);
+
+            for(l = 0; l < lines ;l++) {
+                struct interrupt *irr = irrindex(irrs, l, cpus);
+                if(!irr->used) continue;
+                rrddim_set(st, irr->id, irr->value[c]);
+            }
+            rrdset_done(st);
+        }
+    }
+
+    return 0;
 }
index c8e893b9990bf7f52eddfdfc9574a8feb87e7840..44ea70191ee43a2244d1155d3f5d44967637cc5e 100644 (file)
@@ -1,89 +1,80 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "log.h"
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
+
+// linux calculates this once every 5 seconds
+#define MIN_LOADAVG_UPDATE_EVERY 5
 
 int do_proc_loadavg(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int do_loadavg = -1, do_all_processes = -1;
+    static procfile *ff = NULL;
+    static int do_loadavg = -1, do_all_processes = -1;
 
-       if(dt) {};
+    if(dt) {};
 
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/loadavg");
-               ff = procfile_open(config_get("plugin:proc:/proc/loadavg", "filename to monitor", filename), " \t,:|/", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/loadavg");
+        ff = procfile_open(config_get("plugin:proc:/proc/loadavg", "filename to monitor", filename), " \t,:|/", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
 
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
 
-       if(do_loadavg == -1)            do_loadavg                      = config_get_boolean("plugin:proc:/proc/loadavg", "enable load average", 1);
-       if(do_all_processes == -1)      do_all_processes        = config_get_boolean("plugin:proc:/proc/loadavg", "enable total processes", 1);
+    if(do_loadavg == -1)        do_loadavg          = config_get_boolean("plugin:proc:/proc/loadavg", "enable load average", 1);
+    if(do_all_processes == -1)  do_all_processes    = config_get_boolean("plugin:proc:/proc/loadavg", "enable total processes", 1);
 
-       if(procfile_lines(ff) < 1) {
-               error("/proc/loadavg has no lines.");
-               return 1;
-       }
-       if(procfile_linewords(ff, 0) < 6) {
-               error("/proc/loadavg has less than 6 words in it.");
-               return 1;
-       }
+    if(procfile_lines(ff) < 1) {
+        error("/proc/loadavg has no lines.");
+        return 1;
+    }
+    if(procfile_linewords(ff, 0) < 6) {
+        error("/proc/loadavg has less than 6 words in it.");
+        return 1;
+    }
 
-       double load1 = strtod(procfile_lineword(ff, 0, 0), NULL);
-       double load5 = strtod(procfile_lineword(ff, 0, 1), NULL);
-       double load15 = strtod(procfile_lineword(ff, 0, 2), NULL);
+    double load1 = strtod(procfile_lineword(ff, 0, 0), NULL);
+    double load5 = strtod(procfile_lineword(ff, 0, 1), NULL);
+    double load15 = strtod(procfile_lineword(ff, 0, 2), NULL);
 
-       //unsigned long long running_processes  = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
-       unsigned long long active_processes             = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
-       //unsigned long long next_pid                           = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
+    //unsigned long long running_processes  = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+    unsigned long long active_processes     = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
+    //unsigned long long next_pid               = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
 
 
-       RRDSET *st;
+    RRDSET *st;
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_loadavg) {
-               st = rrdset_find_byname("system.load");
-               if(!st) {
-                       st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, update_every, RRDSET_TYPE_LINE);
+    if(do_loadavg) {
+        st = rrdset_find_byname("system.load");
+        if(!st) {
+            st = rrdset_create("system", "load", NULL, "load", NULL, "System Load Average", "load", 100, (update_every < MIN_LOADAVG_UPDATE_EVERY)?MIN_LOADAVG_UPDATE_EVERY:update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "load1", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "load5", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "load15", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "load1", load1 * 1000);
-               rrddim_set(st, "load5", load5 * 1000);
-               rrddim_set(st, "load15", load15 * 1000);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "load1", load1 * 1000);
+        rrddim_set(st, "load5", load5 * 1000);
+        rrddim_set(st, "load15", load15 * 1000);
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_all_processes) {
-               st = rrdset_find_byname("system.active_processes");
-               if(!st) {
-                       st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
+    if(do_all_processes) {
+        st = rrdset_find_byname("system.active_processes");
+        if(!st) {
+            st = rrdset_create("system", "active_processes", NULL, "processes", NULL, "System Active Processes", "processes", 750, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "active", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "active", active_processes);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "active", active_processes);
+        rrdset_done(st);
+    }
 
-       return 0;
+    return 0;
 }
index 611b4ed219ce396212409636b561406552fbc9bf..4295cd6daea3a085442c57f71362bb81de62a239 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 #define MAX_PROC_MEMINFO_LINE 4096
 #define MAX_PROC_MEMINFO_NAME 1024
 
 int do_proc_meminfo(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-
-       static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1;
-
-       if(do_ram == -1)                do_ram                  = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
-       if(do_swap == -1)               do_swap                 = config_get_boolean("plugin:proc:/proc/meminfo", "system swap", 1);
-       if(do_hwcorrupt == -1)  do_hwcorrupt    = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_committed == -1)  do_committed    = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
-       if(do_writeback == -1)  do_writeback    = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
-       if(do_kernel == -1)             do_kernel               = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
-       if(do_slab == -1)               do_slab                 = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1);
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/meminfo");
-               ff = procfile_open(config_get("plugin:proc:/proc/meminfo", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       int hwcorrupted = 0;
-
-       unsigned long long MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapCached = 0,
-               Active = 0, Inactive = 0, ActiveAnon = 0, InactiveAnon = 0, ActiveFile = 0, InactiveFile = 0,
-               Unevictable = 0, Mlocked = 0, SwapTotal = 0, SwapFree = 0, Dirty = 0, Writeback = 0, AnonPages = 0,
-               Mapped = 0, Shmem = 0, Slab = 0, SReclaimable = 0, SUnreclaim = 0, KernelStack = 0, PageTables = 0,
-               NFS_Unstable = 0, Bounce = 0, WritebackTmp = 0, CommitLimit = 0, Committed_AS = 0,
-               VmallocTotal = 0, VmallocUsed = 0, VmallocChunk = 0,
-               AnonHugePages = 0, HugePages_Total = 0, HugePages_Free = 0, HugePages_Rsvd = 0, HugePages_Surp = 0, Hugepagesize = 0,
-               DirectMap4k = 0, DirectMap2M = 0, HardwareCorrupted = 0;
-
-       for(l = 0; l < lines ;l++) {
-               words = procfile_linewords(ff, l);
-               if(words < 2) continue;
-
-               char *name = procfile_lineword(ff, l, 0);
-               unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-
-                    if(!MemTotal && strcmp(name, "MemTotal") == 0) MemTotal = value;
-               else if(!MemFree && strcmp(name, "MemFree") == 0) MemFree = value;
-               else if(!Buffers && strcmp(name, "Buffers") == 0) Buffers = value;
-               else if(!Cached && strcmp(name, "Cached") == 0) Cached = value;
-               else if(!SwapCached && strcmp(name, "SwapCached") == 0) SwapCached = value;
-               else if(!Active && strcmp(name, "Active") == 0) Active = value;
-               else if(!Inactive && strcmp(name, "Inactive") == 0) Inactive = value;
-               else if(!ActiveAnon && strcmp(name, "ActiveAnon") == 0) ActiveAnon = value;
-               else if(!InactiveAnon && strcmp(name, "InactiveAnon") == 0) InactiveAnon = value;
-               else if(!ActiveFile && strcmp(name, "ActiveFile") == 0) ActiveFile = value;
-               else if(!InactiveFile && strcmp(name, "InactiveFile") == 0) InactiveFile = value;
-               else if(!Unevictable && strcmp(name, "Unevictable") == 0) Unevictable = value;
-               else if(!Mlocked && strcmp(name, "Mlocked") == 0) Mlocked = value;
-               else if(!SwapTotal && strcmp(name, "SwapTotal") == 0) SwapTotal = value;
-               else if(!SwapFree && strcmp(name, "SwapFree") == 0) SwapFree = value;
-               else if(!Dirty && strcmp(name, "Dirty") == 0) Dirty = value;
-               else if(!Writeback && strcmp(name, "Writeback") == 0) Writeback = value;
-               else if(!AnonPages && strcmp(name, "AnonPages") == 0) AnonPages = value;
-               else if(!Mapped && strcmp(name, "Mapped") == 0) Mapped = value;
-               else if(!Shmem && strcmp(name, "Shmem") == 0) Shmem = value;
-               else if(!Slab && strcmp(name, "Slab") == 0) Slab = value;
-               else if(!SReclaimable && strcmp(name, "SReclaimable") == 0) SReclaimable = value;
-               else if(!SUnreclaim && strcmp(name, "SUnreclaim") == 0) SUnreclaim = value;
-               else if(!KernelStack && strcmp(name, "KernelStack") == 0) KernelStack = value;
-               else if(!PageTables && strcmp(name, "PageTables") == 0) PageTables = value;
-               else if(!NFS_Unstable && strcmp(name, "NFS_Unstable") == 0) NFS_Unstable = value;
-               else if(!Bounce && strcmp(name, "Bounce") == 0) Bounce = value;
-               else if(!WritebackTmp && strcmp(name, "WritebackTmp") == 0) WritebackTmp = value;
-               else if(!CommitLimit && strcmp(name, "CommitLimit") == 0) CommitLimit = value;
-               else if(!Committed_AS && strcmp(name, "Committed_AS") == 0) Committed_AS = value;
-               else if(!VmallocTotal && strcmp(name, "VmallocTotal") == 0) VmallocTotal = value;
-               else if(!VmallocUsed && strcmp(name, "VmallocUsed") == 0) VmallocUsed = value;
-               else if(!VmallocChunk && strcmp(name, "VmallocChunk") == 0) VmallocChunk = value;
-               else if(!HardwareCorrupted && strcmp(name, "HardwareCorrupted") == 0) { HardwareCorrupted = value; hwcorrupted = 1; }
-               else if(!AnonHugePages && strcmp(name, "AnonHugePages") == 0) AnonHugePages = value;
-               else if(!HugePages_Total && strcmp(name, "HugePages_Total") == 0) HugePages_Total = value;
-               else if(!HugePages_Free && strcmp(name, "HugePages_Free") == 0) HugePages_Free = value;
-               else if(!HugePages_Rsvd && strcmp(name, "HugePages_Rsvd") == 0) HugePages_Rsvd = value;
-               else if(!HugePages_Surp && strcmp(name, "HugePages_Surp") == 0) HugePages_Surp = value;
-               else if(!Hugepagesize && strcmp(name, "Hugepagesize") == 0) Hugepagesize = value;
-               else if(!DirectMap4k && strcmp(name, "DirectMap4k") == 0) DirectMap4k = value;
-               else if(!DirectMap2M && strcmp(name, "DirectMap2M") == 0) DirectMap2M = value;
-       }
-
-       RRDSET *st;
-
-       // --------------------------------------------------------------------
-
-       // http://stackoverflow.com/questions/3019748/how-to-reliably-measure-available-memory-in-linux
-       unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers;
-
-       if(do_ram) {
-               st = rrdset_find("system.ram");
-               if(!st) {
-                       st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
-
-                       rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "used",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "cached",  NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "free",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "used", MemUsed);
-               rrddim_set(st, "free", MemFree);
-               rrddim_set(st, "cached", Cached);
-               rrddim_set(st, "buffers", Buffers);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       unsigned long long SwapUsed = SwapTotal - SwapFree;
-
-       if(do_swap) {
-               st = rrdset_find("system.swap");
-               if(!st) {
-                       st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "free",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "used",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "used", SwapUsed);
-               rrddim_set(st, "free", SwapFree);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(hwcorrupted && do_hwcorrupt && HardwareCorrupted > 0) {
-               do_hwcorrupt = CONFIG_ONDEMAND_YES;
-
-               st = rrdset_find("mem.hwcorrupt");
-               if(!st) {
-                       st = rrdset_create("mem", "hwcorrupt", NULL, "errors", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "HardwareCorrupted", HardwareCorrupted);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_committed) {
-               st = rrdset_find("mem.committed");
-               if(!st) {
-                       st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "Committed_AS", Committed_AS);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_writeback) {
-               st = rrdset_find("mem.writeback");
-               if(!st) {
-                       st = rrdset_create("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "Dirty", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "Writeback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "Bounce", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "Dirty", Dirty);
-               rrddim_set(st, "Writeback", Writeback);
-               rrddim_set(st, "FuseWriteback", WritebackTmp);
-               rrddim_set(st, "NfsWriteback", NFS_Unstable);
-               rrddim_set(st, "Bounce", Bounce);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_kernel) {
-               st = rrdset_find("mem.kernel");
-               if(!st) {
-                       st = rrdset_create("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000, update_every, RRDSET_TYPE_STACKED);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "Slab", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "KernelStack", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "PageTables", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "KernelStack", KernelStack);
-               rrddim_set(st, "Slab", Slab);
-               rrddim_set(st, "PageTables", PageTables);
-               rrddim_set(st, "VmallocUsed", VmallocUsed);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_slab) {
-               st = rrdset_find("mem.slab");
-               if(!st) {
-                       st = rrdset_create("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500, update_every, RRDSET_TYPE_STACKED);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "reclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "reclaimable", SReclaimable);
-               rrddim_set(st, "unreclaimable", SUnreclaim);
-               rrdset_done(st);
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+
+    static int do_ram = -1, do_swap = -1, do_hwcorrupt = -1, do_committed = -1, do_writeback = -1, do_kernel = -1, do_slab = -1;
+
+    if(do_ram == -1)        do_ram          = config_get_boolean("plugin:proc:/proc/meminfo", "system ram", 1);
+    if(do_swap == -1)       do_swap         = config_get_boolean("plugin:proc:/proc/meminfo", "system swap", 1);
+    if(do_hwcorrupt == -1)  do_hwcorrupt    = config_get_boolean_ondemand("plugin:proc:/proc/meminfo", "hardware corrupted ECC", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_committed == -1)  do_committed    = config_get_boolean("plugin:proc:/proc/meminfo", "committed memory", 1);
+    if(do_writeback == -1)  do_writeback    = config_get_boolean("plugin:proc:/proc/meminfo", "writeback memory", 1);
+    if(do_kernel == -1)     do_kernel       = config_get_boolean("plugin:proc:/proc/meminfo", "kernel memory", 1);
+    if(do_slab == -1)       do_slab         = config_get_boolean("plugin:proc:/proc/meminfo", "slab memory", 1);
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/meminfo");
+        ff = procfile_open(config_get("plugin:proc:/proc/meminfo", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    int hwcorrupted = 0;
+
+    unsigned long long MemTotal = 0, MemFree = 0, Buffers = 0, Cached = 0, SwapCached = 0,
+        Active = 0, Inactive = 0, ActiveAnon = 0, InactiveAnon = 0, ActiveFile = 0, InactiveFile = 0,
+        Unevictable = 0, Mlocked = 0, SwapTotal = 0, SwapFree = 0, Dirty = 0, Writeback = 0, AnonPages = 0,
+        Mapped = 0, Shmem = 0, Slab = 0, SReclaimable = 0, SUnreclaim = 0, KernelStack = 0, PageTables = 0,
+        NFS_Unstable = 0, Bounce = 0, WritebackTmp = 0, CommitLimit = 0, Committed_AS = 0,
+        VmallocTotal = 0, VmallocUsed = 0, VmallocChunk = 0,
+        AnonHugePages = 0, HugePages_Total = 0, HugePages_Free = 0, HugePages_Rsvd = 0, HugePages_Surp = 0, Hugepagesize = 0,
+        DirectMap4k = 0, DirectMap2M = 0, HardwareCorrupted = 0;
+
+    for(l = 0; l < lines ;l++) {
+        words = procfile_linewords(ff, l);
+        if(words < 2) continue;
+
+        char *name = procfile_lineword(ff, l, 0);
+        unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+
+             if(!MemTotal && strcmp(name, "MemTotal") == 0) MemTotal = value;
+        else if(!MemFree && strcmp(name, "MemFree") == 0) MemFree = value;
+        else if(!Buffers && strcmp(name, "Buffers") == 0) Buffers = value;
+        else if(!Cached && strcmp(name, "Cached") == 0) Cached = value;
+        else if(!SwapCached && strcmp(name, "SwapCached") == 0) SwapCached = value;
+        else if(!Active && strcmp(name, "Active") == 0) Active = value;
+        else if(!Inactive && strcmp(name, "Inactive") == 0) Inactive = value;
+        else if(!ActiveAnon && strcmp(name, "ActiveAnon") == 0) ActiveAnon = value;
+        else if(!InactiveAnon && strcmp(name, "InactiveAnon") == 0) InactiveAnon = value;
+        else if(!ActiveFile && strcmp(name, "ActiveFile") == 0) ActiveFile = value;
+        else if(!InactiveFile && strcmp(name, "InactiveFile") == 0) InactiveFile = value;
+        else if(!Unevictable && strcmp(name, "Unevictable") == 0) Unevictable = value;
+        else if(!Mlocked && strcmp(name, "Mlocked") == 0) Mlocked = value;
+        else if(!SwapTotal && strcmp(name, "SwapTotal") == 0) SwapTotal = value;
+        else if(!SwapFree && strcmp(name, "SwapFree") == 0) SwapFree = value;
+        else if(!Dirty && strcmp(name, "Dirty") == 0) Dirty = value;
+        else if(!Writeback && strcmp(name, "Writeback") == 0) Writeback = value;
+        else if(!AnonPages && strcmp(name, "AnonPages") == 0) AnonPages = value;
+        else if(!Mapped && strcmp(name, "Mapped") == 0) Mapped = value;
+        else if(!Shmem && strcmp(name, "Shmem") == 0) Shmem = value;
+        else if(!Slab && strcmp(name, "Slab") == 0) Slab = value;
+        else if(!SReclaimable && strcmp(name, "SReclaimable") == 0) SReclaimable = value;
+        else if(!SUnreclaim && strcmp(name, "SUnreclaim") == 0) SUnreclaim = value;
+        else if(!KernelStack && strcmp(name, "KernelStack") == 0) KernelStack = value;
+        else if(!PageTables && strcmp(name, "PageTables") == 0) PageTables = value;
+        else if(!NFS_Unstable && strcmp(name, "NFS_Unstable") == 0) NFS_Unstable = value;
+        else if(!Bounce && strcmp(name, "Bounce") == 0) Bounce = value;
+        else if(!WritebackTmp && strcmp(name, "WritebackTmp") == 0) WritebackTmp = value;
+        else if(!CommitLimit && strcmp(name, "CommitLimit") == 0) CommitLimit = value;
+        else if(!Committed_AS && strcmp(name, "Committed_AS") == 0) Committed_AS = value;
+        else if(!VmallocTotal && strcmp(name, "VmallocTotal") == 0) VmallocTotal = value;
+        else if(!VmallocUsed && strcmp(name, "VmallocUsed") == 0) VmallocUsed = value;
+        else if(!VmallocChunk && strcmp(name, "VmallocChunk") == 0) VmallocChunk = value;
+        else if(!HardwareCorrupted && strcmp(name, "HardwareCorrupted") == 0) { HardwareCorrupted = value; hwcorrupted = 1; }
+        else if(!AnonHugePages && strcmp(name, "AnonHugePages") == 0) AnonHugePages = value;
+        else if(!HugePages_Total && strcmp(name, "HugePages_Total") == 0) HugePages_Total = value;
+        else if(!HugePages_Free && strcmp(name, "HugePages_Free") == 0) HugePages_Free = value;
+        else if(!HugePages_Rsvd && strcmp(name, "HugePages_Rsvd") == 0) HugePages_Rsvd = value;
+        else if(!HugePages_Surp && strcmp(name, "HugePages_Surp") == 0) HugePages_Surp = value;
+        else if(!Hugepagesize && strcmp(name, "Hugepagesize") == 0) Hugepagesize = value;
+        else if(!DirectMap4k && strcmp(name, "DirectMap4k") == 0) DirectMap4k = value;
+        else if(!DirectMap2M && strcmp(name, "DirectMap2M") == 0) DirectMap2M = value;
+    }
+
+    RRDSET *st;
+
+    // --------------------------------------------------------------------
+
+    // http://stackoverflow.com/questions/3019748/how-to-reliably-measure-available-memory-in-linux
+    unsigned long long MemUsed = MemTotal - MemFree - Cached - Buffers;
+
+    if(do_ram) {
+        st = rrdset_find("system.ram");
+        if(!st) {
+            st = rrdset_create("system", "ram", NULL, "ram", NULL, "System RAM", "MB", 200, update_every, RRDSET_TYPE_STACKED);
+
+            rrddim_add(st, "buffers", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "used",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "cached",  NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "free",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "used", MemUsed);
+        rrddim_set(st, "free", MemFree);
+        rrddim_set(st, "cached", Cached);
+        rrddim_set(st, "buffers", Buffers);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    unsigned long long SwapUsed = SwapTotal - SwapFree;
+
+    if(do_swap) {
+        st = rrdset_find("system.swap");
+        if(!st) {
+            st = rrdset_create("system", "swap", NULL, "swap", NULL, "System Swap", "MB", 201, update_every, RRDSET_TYPE_STACKED);
+            st->isdetail = 1;
+
+            rrddim_add(st, "free",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "used",    NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "used", SwapUsed);
+        rrddim_set(st, "free", SwapFree);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(hwcorrupted && do_hwcorrupt && HardwareCorrupted > 0) {
+        do_hwcorrupt = CONFIG_ONDEMAND_YES;
+
+        st = rrdset_find("mem.hwcorrupt");
+        if(!st) {
+            st = rrdset_create("mem", "hwcorrupt", NULL, "errors", NULL, "Hardware Corrupted ECC", "MB", 9000, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "HardwareCorrupted", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "HardwareCorrupted", HardwareCorrupted);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_committed) {
+        st = rrdset_find("mem.committed");
+        if(!st) {
+            st = rrdset_create("mem", "committed", NULL, "system", NULL, "Committed (Allocated) Memory", "MB", 5000, update_every, RRDSET_TYPE_AREA);
+            st->isdetail = 1;
+
+            rrddim_add(st, "Committed_AS", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "Committed_AS", Committed_AS);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_writeback) {
+        st = rrdset_find("mem.writeback");
+        if(!st) {
+            st = rrdset_create("mem", "writeback", NULL, "kernel", NULL, "Writeback Memory", "MB", 4000, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "Dirty", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "Writeback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "FuseWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "NfsWriteback", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "Bounce", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "Dirty", Dirty);
+        rrddim_set(st, "Writeback", Writeback);
+        rrddim_set(st, "FuseWriteback", WritebackTmp);
+        rrddim_set(st, "NfsWriteback", NFS_Unstable);
+        rrddim_set(st, "Bounce", Bounce);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_kernel) {
+        st = rrdset_find("mem.kernel");
+        if(!st) {
+            st = rrdset_create("mem", "kernel", NULL, "kernel", NULL, "Memory Used by Kernel", "MB", 6000, update_every, RRDSET_TYPE_STACKED);
+            st->isdetail = 1;
+
+            rrddim_add(st, "Slab", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "KernelStack", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "PageTables", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "VmallocUsed", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "KernelStack", KernelStack);
+        rrddim_set(st, "Slab", Slab);
+        rrddim_set(st, "PageTables", PageTables);
+        rrddim_set(st, "VmallocUsed", VmallocUsed);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_slab) {
+        st = rrdset_find("mem.slab");
+        if(!st) {
+            st = rrdset_create("mem", "slab", NULL, "slab", NULL, "Reclaimable Kernel Memory", "MB", 6500, update_every, RRDSET_TYPE_STACKED);
+            st->isdetail = 1;
+
+            rrddim_add(st, "reclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "unreclaimable", NULL, 1, 1024, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "reclaimable", SReclaimable);
+        rrddim_set(st, "unreclaimable", SUnreclaim);
+        rrdset_done(st);
+    }
+
+    return 0;
 }
 
index 12d8078c72178321ead1a9afd2081af1a6ef0bbe..53981182a4b22ad5158534e4f0db9cf9596fe276 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 int do_proc_net_dev(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int enable_new_interfaces = -1, enable_ifb_interfaces = -1;
-       static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/dev");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
-       if(enable_ifb_interfaces == -1) enable_ifb_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable ifb interfaces", CONFIG_ONDEMAND_NO);
-
-       if(do_bandwidth == -1)  do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_packets == -1)    do_packets              = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_errors == -1)             do_errors               = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_drops == -1)              do_drops                = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_fifo == -1)               do_fifo                 = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_compressed == -1) do_compressed   = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_events == -1)             do_events               = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       char *iface;
-       unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
-       unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
-
-       for(l = 2; l < lines ;l++) {
-               words = procfile_linewords(ff, l);
-               if(words < 17) continue;
-
-               iface           = procfile_lineword(ff, l, 0);
-
-               rbytes          = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-               rpackets        = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-               rerrors         = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-               rdrops          = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-               rfifo           = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-               rframe          = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-               rcompressed     = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-               rmulticast      = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-
-               tbytes          = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-               tpackets        = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-               terrors         = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
-               tdrops          = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-               tfifo           = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
-               tcollisions     = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
-               tcarrier        = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
-               tcompressed     = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
-
-               int ddo_bandwidth = do_bandwidth, ddo_packets = do_packets, ddo_errors = do_errors, ddo_drops = do_drops, ddo_fifo = do_fifo, ddo_compressed = do_compressed, ddo_events = do_events;
-
-               int default_enable = enable_new_interfaces;
-
-               // prevent unused interfaces from creating charts
-               if(strcmp(iface, "lo") == 0)
-                       default_enable = 0;
-               else {
-                       int len = strlen(iface);
-                       if(len >= 4 && strcmp(&iface[len-4], "-ifb") == 0)
-                               default_enable = enable_ifb_interfaces;
-               }
-
-               // check if the user wants it
-               {
-                       char var_name[512 + 1];
-                       snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", iface);
-                       default_enable = config_get_boolean_ondemand(var_name, "enabled", default_enable);
-                       if(default_enable == CONFIG_ONDEMAND_NO) continue;
-                       if(default_enable == CONFIG_ONDEMAND_ONDEMAND && !rbytes && !tbytes) continue;
-
-                       ddo_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", ddo_bandwidth);
-                       ddo_packets = config_get_boolean_ondemand(var_name, "packets", ddo_packets);
-                       ddo_errors = config_get_boolean_ondemand(var_name, "errors", ddo_errors);
-                       ddo_drops = config_get_boolean_ondemand(var_name, "drops", ddo_drops);
-                       ddo_fifo = config_get_boolean_ondemand(var_name, "fifo", ddo_fifo);
-                       ddo_compressed = config_get_boolean_ondemand(var_name, "compressed", ddo_compressed);
-                       ddo_events = config_get_boolean_ondemand(var_name, "events", ddo_events);
-
-                       if(ddo_bandwidth == CONFIG_ONDEMAND_ONDEMAND && rbytes == 0 && tbytes == 0) ddo_bandwidth = 0;
-                       if(ddo_errors == CONFIG_ONDEMAND_ONDEMAND && rerrors == 0 && terrors == 0) ddo_errors = 0;
-                       if(ddo_drops == CONFIG_ONDEMAND_ONDEMAND && rdrops == 0 && tdrops == 0) ddo_drops = 0;
-                       if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND && rfifo == 0 && tfifo == 0) ddo_fifo = 0;
-                       if(ddo_compressed == CONFIG_ONDEMAND_ONDEMAND && rcompressed == 0 && tcompressed == 0) ddo_compressed = 0;
-                       if(ddo_events == CONFIG_ONDEMAND_ONDEMAND && rframe == 0 && tcollisions == 0 && tcarrier == 0) ddo_events = 0;
-
-                       // for absolute values, we need to switch the setting to 'yes'
-                       // to allow it refresh from now on
-                       // if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "fifo", "yes");
-               }
-
-               RRDSET *st;
-
-               // --------------------------------------------------------------------
-
-               if(ddo_bandwidth) {
-                       st = rrdset_find_bytype("net", iface);
-                       if(!st) {
-                               st = rrdset_create("net", iface, NULL, iface, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
-
-                               rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "received", rbytes);
-                       rrddim_set(st, "sent", tbytes);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_packets) {
-                       st = rrdset_find_bytype("net_packets", iface);
-                       if(!st) {
-                               st = rrdset_create("net_packets", iface, NULL, iface, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "received", rpackets);
-                       rrddim_set(st, "sent", tpackets);
-                       rrddim_set(st, "multicast", rmulticast);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_errors) {
-                       st = rrdset_find_bytype("net_errors", iface);
-                       if(!st) {
-                               st = rrdset_create("net_errors", iface, NULL, iface, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "inbound", rerrors);
-                       rrddim_set(st, "outbound", terrors);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_drops) {
-                       st = rrdset_find_bytype("net_drops", iface);
-                       if(!st) {
-                               st = rrdset_create("net_drops", iface, NULL, iface, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "inbound", rdrops);
-                       rrddim_set(st, "outbound", tdrops);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_fifo) {
-                       st = rrdset_find_bytype("net_fifo", iface);
-                       if(!st) {
-                               st = rrdset_create("net_fifo", iface, NULL, iface, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "receive", rfifo);
-                       rrddim_set(st, "transmit", tfifo);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_compressed) {
-                       st = rrdset_find_bytype("net_compressed", iface);
-                       if(!st) {
-                               st = rrdset_create("net_compressed", iface, NULL, iface, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "received", rcompressed);
-                       rrddim_set(st, "sent", tcompressed);
-                       rrdset_done(st);
-               }
-
-               // --------------------------------------------------------------------
-
-               if(ddo_events) {
-                       st = rrdset_find_bytype("net_events", iface);
-                       if(!st) {
-                               st = rrdset_create("net_events", iface, NULL, iface, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
-                               st->isdetail = 1;
-
-                               rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "frames", rframe);
-                       rrddim_set(st, "collisions", tcollisions);
-                       rrddim_set(st, "carrier", tcarrier);
-                       rrdset_done(st);
-               }
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+    static int enable_new_interfaces = -1, enable_ifb_interfaces = -1;
+    static int do_bandwidth = -1, do_packets = -1, do_errors = -1, do_drops = -1, do_fifo = -1, do_compressed = -1, do_events = -1;
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/dev");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/dev", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_ONDEMAND);
+    if(enable_ifb_interfaces == -1) enable_ifb_interfaces = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "enable ifb interfaces", CONFIG_ONDEMAND_NO);
+
+    if(do_bandwidth == -1)  do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "bandwidth for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_packets == -1)    do_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_errors == -1)     do_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "errors for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_drops == -1)      do_drops        = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "drops for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_fifo == -1)       do_fifo         = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "fifo for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_compressed == -1) do_compressed   = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "compressed packets for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_events == -1)     do_events       = config_get_boolean_ondemand("plugin:proc:/proc/net/dev", "frames, collisions, carrier counters for all interfaces", CONFIG_ONDEMAND_ONDEMAND);
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    char *iface;
+    unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
+    unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
+
+    for(l = 2; l < lines ;l++) {
+        words = procfile_linewords(ff, l);
+        if(words < 17) continue;
+
+        iface       = procfile_lineword(ff, l, 0);
+
+        rbytes      = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+        rpackets    = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+        rerrors     = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+        rdrops      = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+        rfifo       = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+        rframe      = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+        rcompressed = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+        rmulticast  = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+
+        tbytes      = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+        tpackets    = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+        terrors     = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+        tdrops      = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+        tfifo       = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+        tcollisions = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
+        tcarrier    = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
+        tcompressed = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
+
+        int ddo_bandwidth = do_bandwidth, ddo_packets = do_packets, ddo_errors = do_errors, ddo_drops = do_drops, ddo_fifo = do_fifo, ddo_compressed = do_compressed, ddo_events = do_events;
+
+        int default_enable = enable_new_interfaces;
+
+        // prevent unused interfaces from creating charts
+        if(strcmp(iface, "lo") == 0)
+            default_enable = 0;
+        else {
+            int len = strlen(iface);
+            if(len >= 4 && strcmp(&iface[len-4], "-ifb") == 0)
+                default_enable = enable_ifb_interfaces;
+        }
+
+        // check if the user wants it
+        {
+            char var_name[512 + 1];
+            snprintfz(var_name, 512, "plugin:proc:/proc/net/dev:%s", iface);
+            default_enable = config_get_boolean_ondemand(var_name, "enabled", default_enable);
+            if(default_enable == CONFIG_ONDEMAND_NO) continue;
+            if(default_enable == CONFIG_ONDEMAND_ONDEMAND && !rbytes && !tbytes) continue;
+
+            ddo_bandwidth = config_get_boolean_ondemand(var_name, "bandwidth", ddo_bandwidth);
+            ddo_packets = config_get_boolean_ondemand(var_name, "packets", ddo_packets);
+            ddo_errors = config_get_boolean_ondemand(var_name, "errors", ddo_errors);
+            ddo_drops = config_get_boolean_ondemand(var_name, "drops", ddo_drops);
+            ddo_fifo = config_get_boolean_ondemand(var_name, "fifo", ddo_fifo);
+            ddo_compressed = config_get_boolean_ondemand(var_name, "compressed", ddo_compressed);
+            ddo_events = config_get_boolean_ondemand(var_name, "events", ddo_events);
+
+            if(ddo_bandwidth == CONFIG_ONDEMAND_ONDEMAND && rbytes == 0 && tbytes == 0) ddo_bandwidth = 0;
+            if(ddo_errors == CONFIG_ONDEMAND_ONDEMAND && rerrors == 0 && terrors == 0) ddo_errors = 0;
+            if(ddo_drops == CONFIG_ONDEMAND_ONDEMAND && rdrops == 0 && tdrops == 0) ddo_drops = 0;
+            if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND && rfifo == 0 && tfifo == 0) ddo_fifo = 0;
+            if(ddo_compressed == CONFIG_ONDEMAND_ONDEMAND && rcompressed == 0 && tcompressed == 0) ddo_compressed = 0;
+            if(ddo_events == CONFIG_ONDEMAND_ONDEMAND && rframe == 0 && tcollisions == 0 && tcarrier == 0) ddo_events = 0;
+
+            // for absolute values, we need to switch the setting to 'yes'
+            // to allow it refresh from now on
+            // if(ddo_fifo == CONFIG_ONDEMAND_ONDEMAND) config_set(var_name, "fifo", "yes");
+        }
+
+        RRDSET *st;
+
+        // --------------------------------------------------------------------
+
+        if(ddo_bandwidth) {
+            st = rrdset_find_bytype("net", iface);
+            if(!st) {
+                st = rrdset_create("net", iface, NULL, iface, "net.net", "Bandwidth", "kilobits/s", 7000, update_every, RRDSET_TYPE_AREA);
+
+                rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "received", rbytes);
+            rrddim_set(st, "sent", tbytes);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(ddo_packets) {
+            st = rrdset_find_bytype("net_packets", iface);
+            if(!st) {
+                st = rrdset_create("net_packets", iface, NULL, iface, "net.packets", "Packets", "packets/s", 7001, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "multicast", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "received", rpackets);
+            rrddim_set(st, "sent", tpackets);
+            rrddim_set(st, "multicast", rmulticast);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(ddo_errors) {
+            st = rrdset_find_bytype("net_errors", iface);
+            if(!st) {
+                st = rrdset_create("net_errors", iface, NULL, iface, "net.errors", "Interface Errors", "errors/s", 7002, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "inbound", rerrors);
+            rrddim_set(st, "outbound", terrors);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(ddo_drops) {
+            st = rrdset_find_bytype("net_drops", iface);
+            if(!st) {
+                st = rrdset_create("net_drops", iface, NULL, iface, "net.drops", "Interface Drops", "drops/s", 7003, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "inbound", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "outbound", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "inbound", rdrops);
+            rrddim_set(st, "outbound", tdrops);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(ddo_fifo) {
+            st = rrdset_find_bytype("net_fifo", iface);
+            if(!st) {
+                st = rrdset_create("net_fifo", iface, NULL, iface, "net.fifo", "Interface FIFO Buffer Errors", "errors", 7004, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "receive", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "transmit", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "receive", rfifo);
+            rrddim_set(st, "transmit", tfifo);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(ddo_compressed) {
+            st = rrdset_find_bytype("net_compressed", iface);
+            if(!st) {
+                st = rrdset_create("net_compressed", iface, NULL, iface, "net.compressed", "Compressed Packets", "packets/s", 7005, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "received", rcompressed);
+            rrddim_set(st, "sent", tcompressed);
+            rrdset_done(st);
+        }
+
+        // --------------------------------------------------------------------
+
+        if(ddo_events) {
+            st = rrdset_find_bytype("net_events", iface);
+            if(!st) {
+                st = rrdset_create("net_events", iface, NULL, iface, "net.events", "Network Interface Events", "events/s", 7006, update_every, RRDSET_TYPE_LINE);
+                st->isdetail = 1;
+
+                rrddim_add(st, "frames", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "collisions", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "carrier", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "frames", rframe);
+            rrddim_set(st, "collisions", tcollisions);
+            rrddim_set(st, "carrier", tcarrier);
+            rrdset_done(st);
+        }
+    }
+
+    return 0;
 }
index ffb5da7b56702f781d64abcc8bc12f18c60b9354..96efd7925a6ebf5ecc1cd2725332c651e96bc686 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
-#define RRD_TYPE_NET_IPVS                      "ipvs"
-#define RRD_TYPE_NET_IPVS_LEN          strlen(RRD_TYPE_NET_IPVS)
+#define RRD_TYPE_NET_IPVS           "ipvs"
+#define RRD_TYPE_NET_IPVS_LEN       strlen(RRD_TYPE_NET_IPVS)
 
 int do_proc_net_ip_vs_stats(int update_every, unsigned long long dt) {
-       static int do_bandwidth = -1, do_sockets = -1, do_packets = -1;
-       static procfile *ff = NULL;
+    static int do_bandwidth = -1, do_sockets = -1, do_packets = -1;
+    static procfile *ff = NULL;
 
-       if(do_bandwidth == -1)  do_bandwidth    = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS bandwidth", 1);
-       if(do_sockets == -1)    do_sockets              = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS connections", 1);
-       if(do_packets == -1)    do_packets              = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS packets", 1);
+    if(do_bandwidth == -1)  do_bandwidth    = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS bandwidth", 1);
+    if(do_sockets == -1)    do_sockets      = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS connections", 1);
+    if(do_packets == -1)    do_packets      = config_get_boolean("plugin:proc:/proc/net/ip_vs_stats", "IPVS packets", 1);
 
-       if(dt) {};
+    if(dt) {};
 
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/ip_vs_stats");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/ip_vs_stats", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/ip_vs_stats");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/ip_vs_stats", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
 
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
 
-       // make sure we have 3 lines
-       if(procfile_lines(ff) < 3) return 1;
+    // make sure we have 3 lines
+    if(procfile_lines(ff) < 3) return 1;
 
-       // make sure we have 5 words on the 3rd line
-       if(procfile_linewords(ff, 2) < 5) return 1;
+    // make sure we have 5 words on the 3rd line
+    if(procfile_linewords(ff, 2) < 5) return 1;
 
-       unsigned long long entries, InPackets, OutPackets, InBytes, OutBytes;
+    unsigned long long entries, InPackets, OutPackets, InBytes, OutBytes;
 
-       entries         = strtoull(procfile_lineword(ff, 2, 0), NULL, 16);
-       InPackets       = strtoull(procfile_lineword(ff, 2, 1), NULL, 16);
-       OutPackets      = strtoull(procfile_lineword(ff, 2, 2), NULL, 16);
-       InBytes         = strtoull(procfile_lineword(ff, 2, 3), NULL, 16);
-       OutBytes        = strtoull(procfile_lineword(ff, 2, 4), NULL, 16);
+    entries     = strtoull(procfile_lineword(ff, 2, 0), NULL, 16);
+    InPackets   = strtoull(procfile_lineword(ff, 2, 1), NULL, 16);
+    OutPackets  = strtoull(procfile_lineword(ff, 2, 2), NULL, 16);
+    InBytes     = strtoull(procfile_lineword(ff, 2, 3), NULL, 16);
+    OutBytes    = strtoull(procfile_lineword(ff, 2, 4), NULL, 16);
 
-       RRDSET *st;
+    RRDSET *st;
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_sockets) {
-               st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
+    if(do_sockets) {
+        st = rrdset_find(RRD_TYPE_NET_IPVS ".sockets");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_IPVS, "sockets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "connections", entries);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "connections", entries);
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_packets) {
-               st = rrdset_find(RRD_TYPE_NET_IPVS ".packets");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 1002, update_every, RRDSET_TYPE_LINE);
+    if(do_packets) {
+        st = rrdset_find(RRD_TYPE_NET_IPVS ".packets");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_IPVS, "packets", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Packets", "packets/s", 1002, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "received", InPackets);
-               rrddim_set(st, "sent", OutPackets);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "received", InPackets);
+        rrddim_set(st, "sent", OutPackets);
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_bandwidth) {
-               st = rrdset_find(RRD_TYPE_NET_IPVS ".net");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 1000, update_every, RRDSET_TYPE_AREA);
+    if(do_bandwidth) {
+        st = rrdset_find(RRD_TYPE_NET_IPVS ".net");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_IPVS, "net", NULL, RRD_TYPE_NET_IPVS, NULL, "IPVS Bandwidth", "kilobits/s", 1000, update_every, RRDSET_TYPE_AREA);
 
-                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "received", InBytes);
-               rrddim_set(st, "sent", OutBytes);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "received", InBytes);
+        rrddim_set(st, "sent", OutBytes);
+        rrdset_done(st);
+    }
 
-       return 0;
+    return 0;
 }
index 70d8cd68d0e6f018b648a06ce2bbdd00739ef7fd..fe1c4d9793aa4096297f9ba36e7a946d7519e779 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 int do_proc_net_netstat(int update_every, unsigned long long dt) {
-       static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
-       static procfile *ff = NULL;
-
-       if(do_bandwidth == -1)  do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_inerrors == -1)   do_inerrors             = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_mcast == -1)              do_mcast                = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_bcast == -1)              do_bcast                = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_mcast_p == -1)    do_mcast_p              = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_bcast_p == -1)    do_bcast_p              = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       for(l = 0; l < lines ;l++) {
-               if(strcmp(procfile_lineword(ff, l, 0), "IpExt") == 0) {
-                       l++; // we need the next line
-
-                       if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) {
-                               error("Cannot read IpExt line from /proc/net/netstat.");
-                               break;
-                       }
-                       words = procfile_linewords(ff, l);
-                       if(words < 12) {
-                               error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %u.", words);
-                               continue;
-                       }
-
-                       unsigned long long
-                               InNoRoutes = 0, InTruncatedPkts = 0,
-                               InOctets = 0,  InMcastPkts = 0,  InBcastPkts = 0,  InMcastOctets = 0,  InBcastOctets = 0,
-                               OutOctets = 0, OutMcastPkts = 0, OutBcastPkts = 0, OutMcastOctets = 0, OutBcastOctets = 0;
-
-                       InNoRoutes                      = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       InTruncatedPkts         = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       InMcastPkts             = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       OutMcastPkts            = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       InBcastPkts             = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-                       OutBcastPkts            = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-                       InOctets                        = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-                       OutOctets                       = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-                       InMcastOctets           = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-                       OutMcastOctets          = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-                       InBcastOctets           = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
-                       OutBcastOctets          = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-
-                       RRDSET *st;
-
-                       // --------------------------------------------------------------------
-
-                       if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (InOctets || OutOctets))) {
-                               do_bandwidth = CONFIG_ONDEMAND_YES;
-                               st = rrdset_find("system.ipv4");
-                               if(!st) {
-                                       st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
-
-                                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "sent", OutOctets);
-                               rrddim_set(st, "received", InOctets);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (InNoRoutes || InTruncatedPkts))) {
-                               do_inerrors = CONFIG_ONDEMAND_YES;
-                               st = rrdset_find("ipv4.inerrors");
-                               if(!st) {
-                                       st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "noroutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "truncated", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "noroutes", InNoRoutes);
-                               rrddim_set(st, "truncated", InTruncatedPkts);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (InMcastOctets || OutMcastOctets))) {
-                               do_mcast = CONFIG_ONDEMAND_YES;
-                               st = rrdset_find("ipv4.mcast");
-                               if(!st) {
-                                       st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "sent", OutMcastOctets);
-                               rrddim_set(st, "received", InMcastOctets);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (InBcastOctets || OutBcastOctets))) {
-                               do_bcast = CONFIG_ONDEMAND_YES;
-                               st = rrdset_find("ipv4.bcast");
-                               if(!st) {
-                                       st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "sent", OutBcastOctets);
-                               rrddim_set(st, "received", InBcastOctets);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (InMcastPkts || OutMcastPkts))) {
-                               do_mcast_p = CONFIG_ONDEMAND_YES;
-                               st = rrdset_find("ipv4.mcastpkts");
-                               if(!st) {
-                                       st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "sent", OutMcastPkts);
-                               rrddim_set(st, "received", InMcastPkts);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (InBcastPkts || OutBcastPkts))) {
-                               do_bcast_p = CONFIG_ONDEMAND_YES;
-                               st = rrdset_find("ipv4.bcastpkts");
-                               if(!st) {
-                                       st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "sent", OutBcastPkts);
-                               rrddim_set(st, "received", InBcastPkts);
-                               rrdset_done(st);
-                       }
-               }
-       }
-
-       return 0;
+    static int do_bandwidth = -1, do_inerrors = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1, do_bcast_p = -1;
+    static procfile *ff = NULL;
+
+    if(do_bandwidth == -1)  do_bandwidth    = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_inerrors == -1)   do_inerrors     = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "input errors", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_mcast == -1)      do_mcast        = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_bcast == -1)      do_bcast        = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_mcast_p == -1)    do_mcast_p      = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_bcast_p == -1)    do_bcast_p      = config_get_boolean_ondemand("plugin:proc:/proc/net/netstat", "broadcast packets", CONFIG_ONDEMAND_ONDEMAND);
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/netstat");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/netstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    for(l = 0; l < lines ;l++) {
+        if(strcmp(procfile_lineword(ff, l, 0), "IpExt") == 0) {
+            l++; // we need the next line
+
+            if(strcmp(procfile_lineword(ff, l, 0), "IpExt") != 0) {
+                error("Cannot read IpExt line from /proc/net/netstat.");
+                break;
+            }
+            words = procfile_linewords(ff, l);
+            if(words < 12) {
+                error("Cannot read /proc/net/netstat IpExt line. Expected 12 params, read %u.", words);
+                continue;
+            }
+
+            unsigned long long
+                InNoRoutes = 0, InTruncatedPkts = 0,
+                InOctets = 0,  InMcastPkts = 0,  InBcastPkts = 0,  InMcastOctets = 0,  InBcastOctets = 0,
+                OutOctets = 0, OutMcastPkts = 0, OutBcastPkts = 0, OutMcastOctets = 0, OutBcastOctets = 0;
+
+            InNoRoutes          = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            InTruncatedPkts     = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            InMcastPkts         = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            OutMcastPkts        = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            InBcastPkts         = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+            OutBcastPkts        = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+            InOctets            = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+            OutOctets           = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+            InMcastOctets       = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+            OutMcastOctets      = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+            InBcastOctets       = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+            OutBcastOctets      = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+
+            RRDSET *st;
+
+            // --------------------------------------------------------------------
+
+            if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (InOctets || OutOctets))) {
+                do_bandwidth = CONFIG_ONDEMAND_YES;
+                st = rrdset_find("system.ipv4");
+                if(!st) {
+                    st = rrdset_create("system", "ipv4", NULL, "network", NULL, "IPv4 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+
+                    rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "sent", OutOctets);
+                rrddim_set(st, "received", InOctets);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_inerrors == CONFIG_ONDEMAND_YES || (do_inerrors == CONFIG_ONDEMAND_ONDEMAND && (InNoRoutes || InTruncatedPkts))) {
+                do_inerrors = CONFIG_ONDEMAND_YES;
+                st = rrdset_find("ipv4.inerrors");
+                if(!st) {
+                    st = rrdset_create("ipv4", "inerrors", NULL, "errors", NULL, "IPv4 Input Errors", "packets/s", 4000, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "noroutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "truncated", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "noroutes", InNoRoutes);
+                rrddim_set(st, "truncated", InTruncatedPkts);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (InMcastOctets || OutMcastOctets))) {
+                do_mcast = CONFIG_ONDEMAND_YES;
+                st = rrdset_find("ipv4.mcast");
+                if(!st) {
+                    st = rrdset_create("ipv4", "mcast", NULL, "multicast", NULL, "IPv4 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "sent", OutMcastOctets);
+                rrddim_set(st, "received", InMcastOctets);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (InBcastOctets || OutBcastOctets))) {
+                do_bcast = CONFIG_ONDEMAND_YES;
+                st = rrdset_find("ipv4.bcast");
+                if(!st) {
+                    st = rrdset_create("ipv4", "bcast", NULL, "broadcast", NULL, "IPv4 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "sent", OutBcastOctets);
+                rrddim_set(st, "received", InBcastOctets);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (InMcastPkts || OutMcastPkts))) {
+                do_mcast_p = CONFIG_ONDEMAND_YES;
+                st = rrdset_find("ipv4.mcastpkts");
+                if(!st) {
+                    st = rrdset_create("ipv4", "mcastpkts", NULL, "multicast", NULL, "IPv4 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "sent", OutMcastPkts);
+                rrddim_set(st, "received", InMcastPkts);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_bcast_p == CONFIG_ONDEMAND_YES || (do_bcast_p == CONFIG_ONDEMAND_ONDEMAND && (InBcastPkts || OutBcastPkts))) {
+                do_bcast_p = CONFIG_ONDEMAND_YES;
+                st = rrdset_find("ipv4.bcastpkts");
+                if(!st) {
+                    st = rrdset_create("ipv4", "bcastpkts", NULL, "broadcast", NULL, "IPv4 Broadcast Packets", "packets/s", 8500, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "sent", OutBcastPkts);
+                rrddim_set(st, "received", InBcastPkts);
+                rrdset_done(st);
+            }
+        }
+    }
+
+    return 0;
 }
index b5d766e057adcc04193c5dcad98330f5c104e02f..0323b4dfb190002872084d8716d25225eaeb403d 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 struct nfsd_procs {
-       char name[30];
-       unsigned long long proc2;
-       unsigned long long proc3;
-       unsigned long long proc4;
-       int present2;
-       int present3;
-       int present4;
+    char name[30];
+    unsigned long long proc2;
+    unsigned long long proc3;
+    unsigned long long proc4;
+    int present2;
+    int present3;
+    int present4;
 };
 
 struct nfsd_procs nfsd_proc_values[] = {
-       { "null", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "getattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "setattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "lookup", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "access", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "readlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "read", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "write", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "create", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "mkdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "symlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "mknod", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "remove", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "rmdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "rename", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "link", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "readdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "readdirplus", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "fsstat", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "fsinfo", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "pathconf", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "commit", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
-       { "", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "null", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "getattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "setattr", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "lookup", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "access", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "readlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "read", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "write", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "create", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "mkdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "symlink", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "mknod", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "remove", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "rmdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "rename", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "link", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "readdir", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "readdirplus", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "fsstat", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "fsinfo", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "pathconf", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "commit", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
+    { "", 0ULL, 0ULL, 0ULL, 0, 0, 0 },
 };
 
 struct nfsd4_ops {
-       char name[30];
-       unsigned long long value;
-       int present;
+    char name[30];
+    unsigned long long value;
+    int present;
 };
 
 struct nfsd4_ops nfsd4_ops_values[] = {
-       { "access", 0ULL, 0},
-       { "close", 0ULL, 0},
-       { "commit", 0ULL, 0},
-       { "create", 0ULL, 0},
-       { "delegpurge", 0ULL, 0},
-       { "delegreturn", 0ULL, 0},
-       { "getattr", 0ULL, 0},
-       { "getfh", 0ULL, 0},
-       { "link", 0ULL, 0},
-       { "lock", 0ULL, 0},
-       { "lockt", 0ULL, 0},
-       { "locku", 0ULL, 0},
-       { "lookup", 0ULL, 0},
-       { "lookupp", 0ULL, 0},
-       { "nverify", 0ULL, 0},
-       { "open", 0ULL, 0},
-       { "openattr", 0ULL, 0},
-       { "open_confirm", 0ULL, 0},
-       { "open_downgrade", 0ULL, 0},
-       { "putfh", 0ULL, 0},
-       { "putpubfh", 0ULL, 0},
-       { "putrootfh", 0ULL, 0},
-       { "read", 0ULL, 0},
-       { "readdir", 0ULL, 0},
-       { "readlink", 0ULL, 0},
-       { "remove", 0ULL, 0},
-       { "rename", 0ULL, 0},
-       { "renew", 0ULL, 0},
-       { "restorefh", 0ULL, 0},
-       { "savefh", 0ULL, 0},
-       { "secinfo", 0ULL, 0},
-       { "setattr", 0ULL, 0},
-       { "setclientid", 0ULL, 0},
-       { "setclientid_confirm", 0ULL, 0},
-       { "verify", 0ULL, 0},
-       { "write", 0ULL, 0},
-       { "release_lockowner", 0ULL, 0},
-
-       /* nfs41 */
-       { "backchannel_ctl", 0ULL, 0},
-       { "bind_conn_to_session", 0ULL, 0},
-       { "exchange_id", 0ULL, 0},
-       { "create_session", 0ULL, 0},
-       { "destroy_session", 0ULL, 0},
-       { "free_stateid", 0ULL, 0},
-       { "get_dir_delegation", 0ULL, 0},
-       { "getdeviceinfo", 0ULL, 0},
-       { "getdevicelist", 0ULL, 0},
-       { "layoutcommit", 0ULL, 0},
-       { "layoutget", 0ULL, 0},
-       { "layoutreturn", 0ULL, 0},
-       { "secinfo_no_name", 0ULL, 0},
-       { "sequence", 0ULL, 0},
-       { "set_ssv", 0ULL, 0},
-       { "test_stateid", 0ULL, 0},
-       { "want_delegation", 0ULL, 0},
-       { "destroy_clientid", 0ULL, 0},
-       { "reclaim_complete", 0ULL, 0},
-
-       /* nfs42 */
-       { "allocate", 0ULL, 0},
-       { "copy", 0ULL, 0},
-       { "copy_notify", 0ULL, 0},
-       { "deallocate", 0ULL, 0},
-       { "io_advise", 0ULL, 0},
-       { "layouterror", 0ULL, 0},
-       { "layoutstats", 0ULL, 0},
-       { "offload_cancel", 0ULL, 0},
-       { "offload_status", 0ULL, 0},
-       { "read_plus", 0ULL, 0},
-       { "seek", 0ULL, 0},
-       { "write_same", 0ULL, 0},
-
-       /* termination */
-       { "", 0ULL, 0 }
+    { "access", 0ULL, 0},
+    { "close", 0ULL, 0},
+    { "commit", 0ULL, 0},
+    { "create", 0ULL, 0},
+    { "delegpurge", 0ULL, 0},
+    { "delegreturn", 0ULL, 0},
+    { "getattr", 0ULL, 0},
+    { "getfh", 0ULL, 0},
+    { "link", 0ULL, 0},
+    { "lock", 0ULL, 0},
+    { "lockt", 0ULL, 0},
+    { "locku", 0ULL, 0},
+    { "lookup", 0ULL, 0},
+    { "lookupp", 0ULL, 0},
+    { "nverify", 0ULL, 0},
+    { "open", 0ULL, 0},
+    { "openattr", 0ULL, 0},
+    { "open_confirm", 0ULL, 0},
+    { "open_downgrade", 0ULL, 0},
+    { "putfh", 0ULL, 0},
+    { "putpubfh", 0ULL, 0},
+    { "putrootfh", 0ULL, 0},
+    { "read", 0ULL, 0},
+    { "readdir", 0ULL, 0},
+    { "readlink", 0ULL, 0},
+    { "remove", 0ULL, 0},
+    { "rename", 0ULL, 0},
+    { "renew", 0ULL, 0},
+    { "restorefh", 0ULL, 0},
+    { "savefh", 0ULL, 0},
+    { "secinfo", 0ULL, 0},
+    { "setattr", 0ULL, 0},
+    { "setclientid", 0ULL, 0},
+    { "setclientid_confirm", 0ULL, 0},
+    { "verify", 0ULL, 0},
+    { "write", 0ULL, 0},
+    { "release_lockowner", 0ULL, 0},
+
+    /* nfs41 */
+    { "backchannel_ctl", 0ULL, 0},
+    { "bind_conn_to_session", 0ULL, 0},
+    { "exchange_id", 0ULL, 0},
+    { "create_session", 0ULL, 0},
+    { "destroy_session", 0ULL, 0},
+    { "free_stateid", 0ULL, 0},
+    { "get_dir_delegation", 0ULL, 0},
+    { "getdeviceinfo", 0ULL, 0},
+    { "getdevicelist", 0ULL, 0},
+    { "layoutcommit", 0ULL, 0},
+    { "layoutget", 0ULL, 0},
+    { "layoutreturn", 0ULL, 0},
+    { "secinfo_no_name", 0ULL, 0},
+    { "sequence", 0ULL, 0},
+    { "set_ssv", 0ULL, 0},
+    { "test_stateid", 0ULL, 0},
+    { "want_delegation", 0ULL, 0},
+    { "destroy_clientid", 0ULL, 0},
+    { "reclaim_complete", 0ULL, 0},
+
+    /* nfs42 */
+    { "allocate", 0ULL, 0},
+    { "copy", 0ULL, 0},
+    { "copy_notify", 0ULL, 0},
+    { "deallocate", 0ULL, 0},
+    { "io_advise", 0ULL, 0},
+    { "layouterror", 0ULL, 0},
+    { "layoutstats", 0ULL, 0},
+    { "offload_cancel", 0ULL, 0},
+    { "offload_status", 0ULL, 0},
+    { "read_plus", 0ULL, 0},
+    { "seek", 0ULL, 0},
+    { "write_same", 0ULL, 0},
+
+    /* termination */
+    { "", 0ULL, 0 }
 };
 
 
 int do_proc_net_rpc_nfsd(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_ra = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1;
-       static int ra_warning = 0, th_warning = 0, proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0;
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/rpc/nfsd");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/rpc/nfsd", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       if(do_rc == -1) do_rc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read cache", 1);
-       if(do_fh == -1) do_fh = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "file handles", 1);
-       if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "I/O", 1);
-       if(do_th == -1) do_th = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "threads", 1);
-       if(do_ra == -1) do_ra = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read ahead", 1);
-       if(do_net == -1) do_net = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "network", 1);
-       if(do_rpc == -1) do_rpc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "rpc", 1);
-       if(do_proc2 == -1) do_proc2 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v2 procedures", 1);
-       if(do_proc3 == -1) do_proc3 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v3 procedures", 1);
-       if(do_proc4 == -1) do_proc4 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 procedures", 1);
-       if(do_proc4ops == -1) do_proc4ops = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 operations", 1);
-
-       // if they are enabled, reset them to 1
-       // later we do them =2 to avoid doing strcmp for all lines
-       if(do_rc) do_rc = 1;
-       if(do_fh) do_fh = 1;
-       if(do_io) do_io = 1;
-       if(do_th) do_th = 1;
-       if(do_ra) do_ra = 1;
-       if(do_net) do_net = 1;
-       if(do_rpc) do_rpc = 1;
-       if(do_proc2) do_proc2 = 1;
-       if(do_proc3) do_proc3 = 1;
-       if(do_proc4) do_proc4 = 1;
-       if(do_proc4ops) do_proc4ops = 1;
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       char *type;
-       unsigned long long rc_hits = 0, rc_misses = 0, rc_nocache = 0;
-       unsigned long long fh_stale = 0, fh_total_lookups = 0, fh_anonymous_lookups = 0, fh_dir_not_in_dcache = 0, fh_non_dir_not_in_dcache = 0;
-       unsigned long long io_read = 0, io_write = 0;
-       unsigned long long th_threads = 0, th_fullcnt = 0, th_hist10 = 0, th_hist20 = 0, th_hist30 = 0, th_hist40 = 0, th_hist50 = 0, th_hist60 = 0, th_hist70 = 0, th_hist80 = 0, th_hist90 = 0, th_hist100 = 0;
-       unsigned long long ra_size = 0, ra_hist10 = 0, ra_hist20 = 0, ra_hist30 = 0, ra_hist40 = 0, ra_hist50 = 0, ra_hist60 = 0, ra_hist70 = 0, ra_hist80 = 0, ra_hist90 = 0, ra_hist100 = 0, ra_none = 0;
-       unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0;
-       unsigned long long rpc_count = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0;
-
-       for(l = 0; l < lines ;l++) {
-               words = procfile_linewords(ff, l);
-               if(!words) continue;
-
-               type            = procfile_lineword(ff, l, 0);
-
-               if(do_rc == 1 && strcmp(type, "rc") == 0) {
-                       if(words < 4) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 4);
-                               continue;
-                       }
-
-                       rc_hits = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       rc_misses = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       rc_nocache = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-
-                       unsigned long long sum = rc_hits + rc_misses + rc_nocache;
-                       if(sum == 0ULL) do_rc = -1;
-                       else do_rc = 2;
-               }
-               else if(do_fh == 1 && strcmp(type, "fh") == 0) {
-                       if(words < 6) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6);
-                               continue;
-                       }
-
-                       fh_stale = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       fh_total_lookups = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       fh_anonymous_lookups = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       fh_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       fh_non_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-
-                       unsigned long long sum = fh_stale + fh_total_lookups + fh_anonymous_lookups + fh_dir_not_in_dcache + fh_non_dir_not_in_dcache;
-                       if(sum == 0ULL) do_fh = -1;
-                       else do_fh = 2;
-               }
-               else if(do_io == 1 && strcmp(type, "io") == 0) {
-                       if(words < 3) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 3);
-                               continue;
-                       }
-
-                       io_read = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       io_write = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-
-                       unsigned long long sum = io_read + io_write;
-                       if(sum == 0ULL) do_io = -1;
-                       else do_io = 2;
-               }
-               else if(do_th == 1 && strcmp(type, "th") == 0) {
-                       if(words < 13) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13);
-                               continue;
-                       }
-
-                       th_threads = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       th_fullcnt = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       th_hist10 = (unsigned long long)(atof(procfile_lineword(ff, l, 3)) * 1000.0);
-                       th_hist20 = (unsigned long long)(atof(procfile_lineword(ff, l, 4)) * 1000.0);
-                       th_hist30 = (unsigned long long)(atof(procfile_lineword(ff, l, 5)) * 1000.0);
-                       th_hist40 = (unsigned long long)(atof(procfile_lineword(ff, l, 6)) * 1000.0);
-                       th_hist50 = (unsigned long long)(atof(procfile_lineword(ff, l, 7)) * 1000.0);
-                       th_hist60 = (unsigned long long)(atof(procfile_lineword(ff, l, 8)) * 1000.0);
-                       th_hist70 = (unsigned long long)(atof(procfile_lineword(ff, l, 9)) * 1000.0);
-                       th_hist80 = (unsigned long long)(atof(procfile_lineword(ff, l, 10)) * 1000.0);
-                       th_hist90 = (unsigned long long)(atof(procfile_lineword(ff, l, 11)) * 1000.0);
-                       th_hist100 = (unsigned long long)(atof(procfile_lineword(ff, l, 12)) * 1000.0);
-
-                       // threads histogram has been disabled on recent kernels
-                       // http://permalink.gmane.org/gmane.linux.nfs/24528
-                       unsigned long long sum = th_hist10 + th_hist20 + th_hist30 + th_hist40 + th_hist50 + th_hist60 + th_hist70 + th_hist80 + th_hist90 + th_hist100;
-                       if(sum == 0ULL) {
-                               if(!th_warning) {
-                                       info("Disabling /proc/net/rpc/nfsd threads histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
-                                       th_warning = 1;
-                               }
-                               do_th = -1;
-                       }
-                       else do_th = 2;
-               }
-               else if(do_ra == 1 && strcmp(type, "ra") == 0) {
-                       if(words < 13) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13);
-                               continue;
-                       }
-
-                       ra_size = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       ra_hist10 = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       ra_hist20 = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       ra_hist30 = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       ra_hist40 = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-                       ra_hist50 = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-                       ra_hist60 = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-                       ra_hist70 = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-                       ra_hist80 = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-                       ra_hist90 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-                       ra_hist100 = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
-                       ra_none = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-
-                       unsigned long long sum = ra_hist10 + ra_hist20 + ra_hist30 + ra_hist40 + ra_hist50 + ra_hist60 + ra_hist70 + ra_hist80 + ra_hist90 + ra_hist100 + ra_none;
-                       if(sum == 0ULL) {
-                               if(!ra_warning) {
-                                       info("Disabling /proc/net/rpc/nfsd read ahead histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
-                                       ra_warning = 1;
-                               }
-                               do_ra = -1;
-                       }
-                       else do_ra = 2;
-               }
-               else if(do_net == 1 && strcmp(type, "net") == 0) {
-                       if(words < 5) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 5);
-                               continue;
-                       }
-
-                       net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-
-                       unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections;
-                       if(sum == 0ULL) do_net = -1;
-                       else do_net = 2;
-               }
-               else if(do_rpc == 1 && strcmp(type, "rpc") == 0) {
-                       if(words < 6) {
-                               error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6);
-                               continue;
-                       }
-
-                       rpc_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       rpc_bad_format = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       rpc_bad_auth = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       rpc_bad_client = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-
-                       unsigned long long sum = rpc_count + rpc_bad_format + rpc_bad_auth + rpc_bad_client;
-                       if(sum == 0ULL) do_rpc = -1;
-                       else do_rpc = 2;
-               }
-               else if(do_proc2 == 1 && strcmp(type, "proc2") == 0) {
-                       // the first number is the count of numbers present
-                       // so we start for word 2
-
-                       unsigned long long sum = 0;
-                       unsigned int i, j;
-                       for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
-                               nfsd_proc_values[i].proc2 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
-                               nfsd_proc_values[i].present2 = 1;
-                               sum += nfsd_proc_values[i].proc2;
-                       }
-
-                       if(sum == 0ULL) {
-                               if(!proc2_warning) {
-                                       error("Disabling /proc/net/rpc/nfsd v2 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
-                                       proc2_warning = 1;
-                               }
-                               do_proc2 = 0;
-                       }
-                       else do_proc2 = 2;
-               }
-               else if(do_proc3 == 1 && strcmp(type, "proc3") == 0) {
-                       // the first number is the count of numbers present
-                       // so we start for word 2
-
-                       unsigned long long sum = 0;
-                       unsigned int i, j;
-                       for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
-                               nfsd_proc_values[i].proc3 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
-                               nfsd_proc_values[i].present3 = 1;
-                               sum += nfsd_proc_values[i].proc3;
-                       }
-
-                       if(sum == 0ULL) {
-                               if(!proc3_warning) {
-                                       info("Disabling /proc/net/rpc/nfsd v3 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
-                                       proc3_warning = 1;
-                               }
-                               do_proc3 = 0;
-                       }
-                       else do_proc3 = 2;
-               }
-               else if(do_proc4 == 1 && strcmp(type, "proc4") == 0) {
-                       // the first number is the count of numbers present
-                       // so we start for word 2
-
-                       unsigned long long sum = 0;
-                       unsigned int i, j;
-                       for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
-                               nfsd_proc_values[i].proc4 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
-                               nfsd_proc_values[i].present4 = 1;
-                               sum += nfsd_proc_values[i].proc4;
-                       }
-
-                       if(sum == 0ULL) {
-                               if(!proc4_warning) {
-                                       info("Disabling /proc/net/rpc/nfsd v4 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
-                                       proc4_warning = 1;
-                               }
-                               do_proc4 = 0;
-                       }
-                       else do_proc4 = 2;
-               }
-               else if(do_proc4ops == 1 && strcmp(type, "proc4ops") == 0) {
-                       // the first number is the count of numbers present
-                       // so we start for word 2
-
-                       unsigned long long sum = 0;
-                       unsigned int i, j;
-                       for(i = 0, j = 2; j < words && nfsd4_ops_values[i].name[0] ; i++, j++) {
-                               nfsd4_ops_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
-                               nfsd4_ops_values[i].present = 1;
-                               sum += nfsd4_ops_values[i].value;
-                       }
-
-                       if(sum == 0ULL) {
-                               if(!proc4ops_warning) {
-                                       info("Disabling /proc/net/rpc/nfsd v4 operations chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
-                                       proc4ops_warning = 1;
-                               }
-                               do_proc4ops = 0;
-                       }
-                       else do_proc4ops = 2;
-               }
-       }
-
-       RRDSET *st;
-
-       // --------------------------------------------------------------------
-
-       if(do_rc == 2) {
-               st = rrdset_find_bytype("nfsd", "readcache");
-               if(!st) {
-                       st = rrdset_create("nfsd", "readcache", NULL, "nfsd", NULL, "Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
-
-                       rrddim_add(st, "hits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "nocache", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "hits", rc_hits);
-               rrddim_set(st, "misses", rc_misses);
-               rrddim_set(st, "nocache", rc_nocache);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_fh == 2) {
-               st = rrdset_find_bytype("nfsd", "filehandles");
-               if(!st) {
-                       st = rrdset_create("nfsd", "filehandles", NULL, "nfsd", NULL, "File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "stale", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "total_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "stale", fh_stale);
-               rrddim_set(st, "total_lookups", fh_total_lookups);
-               rrddim_set(st, "anonymous_lookups", fh_anonymous_lookups);
-               rrddim_set(st, "dir_not_in_dcache", fh_dir_not_in_dcache);
-               rrddim_set(st, "non_dir_not_in_dcache", fh_non_dir_not_in_dcache);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_io == 2) {
-               st = rrdset_find_bytype("nfsd", "io");
-               if(!st) {
-                       st = rrdset_create("nfsd", "io", NULL, "nfsd", NULL, "I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
-
-                       rrddim_add(st, "read", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "write", NULL, -1, 1000, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "read", io_read);
-               rrddim_set(st, "write", io_write);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_th == 2) {
-               st = rrdset_find_bytype("nfsd", "threads");
-               if(!st) {
-                       st = rrdset_create("nfsd", "threads", NULL, "nfsd", NULL, "Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "threads", th_threads);
-               rrdset_done(st);
-
-               st = rrdset_find_bytype("nfsd", "threads_fullcnt");
-               if(!st) {
-                       st = rrdset_create("nfsd", "threads_fullcnt", NULL, "nfsd", NULL, "Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "full_count", th_fullcnt);
-               rrdset_done(st);
-
-               st = rrdset_find_bytype("nfsd", "threads_histogram");
-               if(!st) {
-                       st = rrdset_create("nfsd", "threads_histogram", NULL, "nfsd", NULL, "Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "0%-10%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "10%-20%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "20%-30%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "30%-40%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "40%-50%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "50%-60%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "60%-70%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "70%-80%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "80%-90%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "90%-100%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "0%-10%", th_hist10);
-               rrddim_set(st, "10%-20%", th_hist20);
-               rrddim_set(st, "20%-30%", th_hist30);
-               rrddim_set(st, "30%-40%", th_hist40);
-               rrddim_set(st, "40%-50%", th_hist50);
-               rrddim_set(st, "50%-60%", th_hist60);
-               rrddim_set(st, "60%-70%", th_hist70);
-               rrddim_set(st, "70%-80%", th_hist80);
-               rrddim_set(st, "80%-90%", th_hist90);
-               rrddim_set(st, "90%-100%", th_hist100);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_ra == 2) {
-               st = rrdset_find_bytype("nfsd", "readahead");
-               if(!st) {
-                       st = rrdset_create("nfsd", "readahead", NULL, "nfsd", NULL, "Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
-
-                       rrddim_add(st, "10%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "20%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "30%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "40%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "50%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "60%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "70%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "80%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "90%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "100%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                       rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-               }
-               else rrdset_next(st);
-
-               // ignore ra_size
-               if(ra_size) {};
-
-               rrddim_set(st, "10%", ra_hist10);
-               rrddim_set(st, "20%", ra_hist20);
-               rrddim_set(st, "30%", ra_hist30);
-               rrddim_set(st, "40%", ra_hist40);
-               rrddim_set(st, "50%", ra_hist50);
-               rrddim_set(st, "60%", ra_hist60);
-               rrddim_set(st, "70%", ra_hist70);
-               rrddim_set(st, "80%", ra_hist80);
-               rrddim_set(st, "90%", ra_hist90);
-               rrddim_set(st, "100%", ra_hist100);
-               rrddim_set(st, "misses", ra_none);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_net == 2) {
-               st = rrdset_find_bytype("nfsd", "net");
-               if(!st) {
-                       st = rrdset_create("nfsd", "net", NULL, "nfsd", NULL, "Network Reads", "reads/s", 5007, update_every, RRDSET_TYPE_STACKED);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               // ignore net_count, net_tcp_connections
-               if(net_count) {};
-               if(net_tcp_connections) {};
-
-               rrddim_set(st, "udp", net_udp_count);
-               rrddim_set(st, "tcp", net_tcp_count);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_rpc == 2) {
-               st = rrdset_find_bytype("nfsd", "rpc");
-               if(!st) {
-                       st = rrdset_create("nfsd", "rpc", NULL, "nfsd", NULL, "Remote Procedure Calls", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "bad_format", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "bad_auth", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               // ignore rpc_bad_client
-               if(rpc_bad_client) {};
-
-               rrddim_set(st, "all", rpc_count);
-               rrddim_set(st, "bad_format", rpc_bad_format);
-               rrddim_set(st, "bad_auth", rpc_bad_auth);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_proc2 == 2) {
-               unsigned int i;
-               st = rrdset_find_bytype("nfsd", "proc2");
-               if(!st) {
-                       st = rrdset_create("nfsd", "proc2", NULL, "nfsd", NULL, "NFS v2 Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
-
-                       for(i = 0; nfsd_proc_values[i].present2 ; i++)
-                               rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               for(i = 0; nfsd_proc_values[i].present2 ; i++)
-                       rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc2);
-
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_proc3 == 2) {
-               unsigned int i;
-               st = rrdset_find_bytype("nfsd", "proc3");
-               if(!st) {
-                       st = rrdset_create("nfsd", "proc3", NULL, "nfsd", NULL, "NFS v3 Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
-
-                       for(i = 0; nfsd_proc_values[i].present3 ; i++)
-                               rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               for(i = 0; nfsd_proc_values[i].present3 ; i++)
-                       rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc3);
-
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_proc4 == 2) {
-               unsigned int i;
-               st = rrdset_find_bytype("nfsd", "proc4");
-               if(!st) {
-                       st = rrdset_create("nfsd", "proc4", NULL, "nfsd", NULL, "NFS v4 Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
-
-                       for(i = 0; nfsd_proc_values[i].present4 ; i++)
-                               rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+    static procfile *ff = NULL;
+    static int do_rc = -1, do_fh = -1, do_io = -1, do_th = -1, do_ra = -1, do_net = -1, do_rpc = -1, do_proc2 = -1, do_proc3 = -1, do_proc4 = -1, do_proc4ops = -1;
+    static int ra_warning = 0, th_warning = 0, proc2_warning = 0, proc3_warning = 0, proc4_warning = 0, proc4ops_warning = 0;
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/rpc/nfsd");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/rpc/nfsd", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    if(do_rc == -1) do_rc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read cache", 1);
+    if(do_fh == -1) do_fh = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "file handles", 1);
+    if(do_io == -1) do_io = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "I/O", 1);
+    if(do_th == -1) do_th = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "threads", 1);
+    if(do_ra == -1) do_ra = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "read ahead", 1);
+    if(do_net == -1) do_net = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "network", 1);
+    if(do_rpc == -1) do_rpc = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "rpc", 1);
+    if(do_proc2 == -1) do_proc2 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v2 procedures", 1);
+    if(do_proc3 == -1) do_proc3 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v3 procedures", 1);
+    if(do_proc4 == -1) do_proc4 = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 procedures", 1);
+    if(do_proc4ops == -1) do_proc4ops = config_get_boolean("plugin:proc:/proc/net/rpc/nfsd", "NFS v4 operations", 1);
+
+    // if they are enabled, reset them to 1
+    // later we do them =2 to avoid doing strcmp for all lines
+    if(do_rc) do_rc = 1;
+    if(do_fh) do_fh = 1;
+    if(do_io) do_io = 1;
+    if(do_th) do_th = 1;
+    if(do_ra) do_ra = 1;
+    if(do_net) do_net = 1;
+    if(do_rpc) do_rpc = 1;
+    if(do_proc2) do_proc2 = 1;
+    if(do_proc3) do_proc3 = 1;
+    if(do_proc4) do_proc4 = 1;
+    if(do_proc4ops) do_proc4ops = 1;
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    char *type;
+    unsigned long long rc_hits = 0, rc_misses = 0, rc_nocache = 0;
+    unsigned long long fh_stale = 0, fh_total_lookups = 0, fh_anonymous_lookups = 0, fh_dir_not_in_dcache = 0, fh_non_dir_not_in_dcache = 0;
+    unsigned long long io_read = 0, io_write = 0;
+    unsigned long long th_threads = 0, th_fullcnt = 0, th_hist10 = 0, th_hist20 = 0, th_hist30 = 0, th_hist40 = 0, th_hist50 = 0, th_hist60 = 0, th_hist70 = 0, th_hist80 = 0, th_hist90 = 0, th_hist100 = 0;
+    unsigned long long ra_size = 0, ra_hist10 = 0, ra_hist20 = 0, ra_hist30 = 0, ra_hist40 = 0, ra_hist50 = 0, ra_hist60 = 0, ra_hist70 = 0, ra_hist80 = 0, ra_hist90 = 0, ra_hist100 = 0, ra_none = 0;
+    unsigned long long net_count = 0, net_udp_count = 0, net_tcp_count = 0, net_tcp_connections = 0;
+    unsigned long long rpc_count = 0, rpc_bad_format = 0, rpc_bad_auth = 0, rpc_bad_client = 0;
+
+    for(l = 0; l < lines ;l++) {
+        words = procfile_linewords(ff, l);
+        if(!words) continue;
+
+        type        = procfile_lineword(ff, l, 0);
+
+        if(do_rc == 1 && strcmp(type, "rc") == 0) {
+            if(words < 4) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 4);
+                continue;
+            }
+
+            rc_hits = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            rc_misses = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            rc_nocache = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+
+            unsigned long long sum = rc_hits + rc_misses + rc_nocache;
+            if(sum == 0ULL) do_rc = -1;
+            else do_rc = 2;
+        }
+        else if(do_fh == 1 && strcmp(type, "fh") == 0) {
+            if(words < 6) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6);
+                continue;
+            }
+
+            fh_stale = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            fh_total_lookups = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            fh_anonymous_lookups = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            fh_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            fh_non_dir_not_in_dcache = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+
+            unsigned long long sum = fh_stale + fh_total_lookups + fh_anonymous_lookups + fh_dir_not_in_dcache + fh_non_dir_not_in_dcache;
+            if(sum == 0ULL) do_fh = -1;
+            else do_fh = 2;
+        }
+        else if(do_io == 1 && strcmp(type, "io") == 0) {
+            if(words < 3) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 3);
+                continue;
+            }
+
+            io_read = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            io_write = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+
+            unsigned long long sum = io_read + io_write;
+            if(sum == 0ULL) do_io = -1;
+            else do_io = 2;
+        }
+        else if(do_th == 1 && strcmp(type, "th") == 0) {
+            if(words < 13) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13);
+                continue;
+            }
+
+            th_threads = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            th_fullcnt = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            th_hist10 = (unsigned long long)(atof(procfile_lineword(ff, l, 3)) * 1000.0);
+            th_hist20 = (unsigned long long)(atof(procfile_lineword(ff, l, 4)) * 1000.0);
+            th_hist30 = (unsigned long long)(atof(procfile_lineword(ff, l, 5)) * 1000.0);
+            th_hist40 = (unsigned long long)(atof(procfile_lineword(ff, l, 6)) * 1000.0);
+            th_hist50 = (unsigned long long)(atof(procfile_lineword(ff, l, 7)) * 1000.0);
+            th_hist60 = (unsigned long long)(atof(procfile_lineword(ff, l, 8)) * 1000.0);
+            th_hist70 = (unsigned long long)(atof(procfile_lineword(ff, l, 9)) * 1000.0);
+            th_hist80 = (unsigned long long)(atof(procfile_lineword(ff, l, 10)) * 1000.0);
+            th_hist90 = (unsigned long long)(atof(procfile_lineword(ff, l, 11)) * 1000.0);
+            th_hist100 = (unsigned long long)(atof(procfile_lineword(ff, l, 12)) * 1000.0);
+
+            // threads histogram has been disabled on recent kernels
+            // http://permalink.gmane.org/gmane.linux.nfs/24528
+            unsigned long long sum = th_hist10 + th_hist20 + th_hist30 + th_hist40 + th_hist50 + th_hist60 + th_hist70 + th_hist80 + th_hist90 + th_hist100;
+            if(sum == 0ULL) {
+                if(!th_warning) {
+                    info("Disabling /proc/net/rpc/nfsd threads histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+                    th_warning = 1;
+                }
+                do_th = -1;
+            }
+            else do_th = 2;
+        }
+        else if(do_ra == 1 && strcmp(type, "ra") == 0) {
+            if(words < 13) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 13);
+                continue;
+            }
+
+            ra_size = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            ra_hist10 = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            ra_hist20 = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            ra_hist30 = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            ra_hist40 = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+            ra_hist50 = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+            ra_hist60 = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+            ra_hist70 = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+            ra_hist80 = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+            ra_hist90 = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+            ra_hist100 = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+            ra_none = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+
+            unsigned long long sum = ra_hist10 + ra_hist20 + ra_hist30 + ra_hist40 + ra_hist50 + ra_hist60 + ra_hist70 + ra_hist80 + ra_hist90 + ra_hist100 + ra_none;
+            if(sum == 0ULL) {
+                if(!ra_warning) {
+                    info("Disabling /proc/net/rpc/nfsd read ahead histogram. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+                    ra_warning = 1;
+                }
+                do_ra = -1;
+            }
+            else do_ra = 2;
+        }
+        else if(do_net == 1 && strcmp(type, "net") == 0) {
+            if(words < 5) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 5);
+                continue;
+            }
+
+            net_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            net_udp_count = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            net_tcp_count = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            net_tcp_connections = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+
+            unsigned long long sum = net_count + net_udp_count + net_tcp_count + net_tcp_connections;
+            if(sum == 0ULL) do_net = -1;
+            else do_net = 2;
+        }
+        else if(do_rpc == 1 && strcmp(type, "rpc") == 0) {
+            if(words < 6) {
+                error("%s line of /proc/net/rpc/nfsd has %u words, expected %d", type, words, 6);
+                continue;
+            }
+
+            rpc_count = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            rpc_bad_format = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            rpc_bad_auth = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            rpc_bad_client = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+
+            unsigned long long sum = rpc_count + rpc_bad_format + rpc_bad_auth + rpc_bad_client;
+            if(sum == 0ULL) do_rpc = -1;
+            else do_rpc = 2;
+        }
+        else if(do_proc2 == 1 && strcmp(type, "proc2") == 0) {
+            // the first number is the count of numbers present
+            // so we start for word 2
+
+            unsigned long long sum = 0;
+            unsigned int i, j;
+            for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
+                nfsd_proc_values[i].proc2 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+                nfsd_proc_values[i].present2 = 1;
+                sum += nfsd_proc_values[i].proc2;
+            }
+
+            if(sum == 0ULL) {
+                if(!proc2_warning) {
+                    error("Disabling /proc/net/rpc/nfsd v2 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+                    proc2_warning = 1;
+                }
+                do_proc2 = 0;
+            }
+            else do_proc2 = 2;
+        }
+        else if(do_proc3 == 1 && strcmp(type, "proc3") == 0) {
+            // the first number is the count of numbers present
+            // so we start for word 2
+
+            unsigned long long sum = 0;
+            unsigned int i, j;
+            for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
+                nfsd_proc_values[i].proc3 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+                nfsd_proc_values[i].present3 = 1;
+                sum += nfsd_proc_values[i].proc3;
+            }
+
+            if(sum == 0ULL) {
+                if(!proc3_warning) {
+                    info("Disabling /proc/net/rpc/nfsd v3 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+                    proc3_warning = 1;
+                }
+                do_proc3 = 0;
+            }
+            else do_proc3 = 2;
+        }
+        else if(do_proc4 == 1 && strcmp(type, "proc4") == 0) {
+            // the first number is the count of numbers present
+            // so we start for word 2
+
+            unsigned long long sum = 0;
+            unsigned int i, j;
+            for(i = 0, j = 2; j < words && nfsd_proc_values[i].name[0] ; i++, j++) {
+                nfsd_proc_values[i].proc4 = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+                nfsd_proc_values[i].present4 = 1;
+                sum += nfsd_proc_values[i].proc4;
+            }
+
+            if(sum == 0ULL) {
+                if(!proc4_warning) {
+                    info("Disabling /proc/net/rpc/nfsd v4 procedure calls chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+                    proc4_warning = 1;
+                }
+                do_proc4 = 0;
+            }
+            else do_proc4 = 2;
+        }
+        else if(do_proc4ops == 1 && strcmp(type, "proc4ops") == 0) {
+            // the first number is the count of numbers present
+            // so we start for word 2
+
+            unsigned long long sum = 0;
+            unsigned int i, j;
+            for(i = 0, j = 2; j < words && nfsd4_ops_values[i].name[0] ; i++, j++) {
+                nfsd4_ops_values[i].value = strtoull(procfile_lineword(ff, l, j), NULL, 10);
+                nfsd4_ops_values[i].present = 1;
+                sum += nfsd4_ops_values[i].value;
+            }
+
+            if(sum == 0ULL) {
+                if(!proc4ops_warning) {
+                    info("Disabling /proc/net/rpc/nfsd v4 operations chart. It seems unused on this machine. It will be enabled automatically when found with data in it.");
+                    proc4ops_warning = 1;
+                }
+                do_proc4ops = 0;
+            }
+            else do_proc4ops = 2;
+        }
+    }
+
+    RRDSET *st;
+
+    // --------------------------------------------------------------------
+
+    if(do_rc == 2) {
+        st = rrdset_find_bytype("nfsd", "readcache");
+        if(!st) {
+            st = rrdset_create("nfsd", "readcache", NULL, "nfsd", NULL, "Read Cache", "reads/s", 5000, update_every, RRDSET_TYPE_STACKED);
+
+            rrddim_add(st, "hits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "nocache", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "hits", rc_hits);
+        rrddim_set(st, "misses", rc_misses);
+        rrddim_set(st, "nocache", rc_nocache);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_fh == 2) {
+        st = rrdset_find_bytype("nfsd", "filehandles");
+        if(!st) {
+            st = rrdset_create("nfsd", "filehandles", NULL, "nfsd", NULL, "File Handles", "handles/s", 5001, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "stale", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "total_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "anonymous_lookups", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "non_dir_not_in_dcache", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "stale", fh_stale);
+        rrddim_set(st, "total_lookups", fh_total_lookups);
+        rrddim_set(st, "anonymous_lookups", fh_anonymous_lookups);
+        rrddim_set(st, "dir_not_in_dcache", fh_dir_not_in_dcache);
+        rrddim_set(st, "non_dir_not_in_dcache", fh_non_dir_not_in_dcache);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_io == 2) {
+        st = rrdset_find_bytype("nfsd", "io");
+        if(!st) {
+            st = rrdset_create("nfsd", "io", NULL, "nfsd", NULL, "I/O", "kilobytes/s", 5002, update_every, RRDSET_TYPE_AREA);
+
+            rrddim_add(st, "read", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "write", NULL, -1, 1000, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "read", io_read);
+        rrddim_set(st, "write", io_write);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_th == 2) {
+        st = rrdset_find_bytype("nfsd", "threads");
+        if(!st) {
+            st = rrdset_create("nfsd", "threads", NULL, "nfsd", NULL, "Threads", "threads", 5003, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "threads", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "threads", th_threads);
+        rrdset_done(st);
+
+        st = rrdset_find_bytype("nfsd", "threads_fullcnt");
+        if(!st) {
+            st = rrdset_create("nfsd", "threads_fullcnt", NULL, "nfsd", NULL, "Threads Full Count", "ops/s", 5004, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "full_count", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "full_count", th_fullcnt);
+        rrdset_done(st);
+
+        st = rrdset_find_bytype("nfsd", "threads_histogram");
+        if(!st) {
+            st = rrdset_create("nfsd", "threads_histogram", NULL, "nfsd", NULL, "Threads Usage Histogram", "percentage", 5005, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "0%-10%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "10%-20%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "20%-30%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "30%-40%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "40%-50%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "50%-60%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "60%-70%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "70%-80%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "80%-90%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "90%-100%", NULL, 1, 1000, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "0%-10%", th_hist10);
+        rrddim_set(st, "10%-20%", th_hist20);
+        rrddim_set(st, "20%-30%", th_hist30);
+        rrddim_set(st, "30%-40%", th_hist40);
+        rrddim_set(st, "40%-50%", th_hist50);
+        rrddim_set(st, "50%-60%", th_hist60);
+        rrddim_set(st, "60%-70%", th_hist70);
+        rrddim_set(st, "70%-80%", th_hist80);
+        rrddim_set(st, "80%-90%", th_hist90);
+        rrddim_set(st, "90%-100%", th_hist100);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_ra == 2) {
+        st = rrdset_find_bytype("nfsd", "readahead");
+        if(!st) {
+            st = rrdset_create("nfsd", "readahead", NULL, "nfsd", NULL, "Read Ahead Depth", "percentage", 5005, update_every, RRDSET_TYPE_STACKED);
+
+            rrddim_add(st, "10%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "20%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "30%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "40%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "50%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "60%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "70%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "80%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "90%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "100%", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+            rrddim_add(st, "misses", NULL, 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+        }
+        else rrdset_next(st);
+
+        // ignore ra_size
+        if(ra_size) {};
+
+        rrddim_set(st, "10%", ra_hist10);
+        rrddim_set(st, "20%", ra_hist20);
+        rrddim_set(st, "30%", ra_hist30);
+        rrddim_set(st, "40%", ra_hist40);
+        rrddim_set(st, "50%", ra_hist50);
+        rrddim_set(st, "60%", ra_hist60);
+        rrddim_set(st, "70%", ra_hist70);
+        rrddim_set(st, "80%", ra_hist80);
+        rrddim_set(st, "90%", ra_hist90);
+        rrddim_set(st, "100%", ra_hist100);
+        rrddim_set(st, "misses", ra_none);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_net == 2) {
+        st = rrdset_find_bytype("nfsd", "net");
+        if(!st) {
+            st = rrdset_create("nfsd", "net", NULL, "nfsd", NULL, "Network Reads", "reads/s", 5007, update_every, RRDSET_TYPE_STACKED);
+            st->isdetail = 1;
+
+            rrddim_add(st, "udp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "tcp", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        // ignore net_count, net_tcp_connections
+        if(net_count) {};
+        if(net_tcp_connections) {};
+
+        rrddim_set(st, "udp", net_udp_count);
+        rrddim_set(st, "tcp", net_tcp_count);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_rpc == 2) {
+        st = rrdset_find_bytype("nfsd", "rpc");
+        if(!st) {
+            st = rrdset_create("nfsd", "rpc", NULL, "nfsd", NULL, "Remote Procedure Calls", "calls/s", 5008, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "bad_format", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "bad_auth", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        // ignore rpc_bad_client
+        if(rpc_bad_client) {};
+
+        rrddim_set(st, "all", rpc_count);
+        rrddim_set(st, "bad_format", rpc_bad_format);
+        rrddim_set(st, "bad_auth", rpc_bad_auth);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_proc2 == 2) {
+        unsigned int i;
+        st = rrdset_find_bytype("nfsd", "proc2");
+        if(!st) {
+            st = rrdset_create("nfsd", "proc2", NULL, "nfsd", NULL, "NFS v2 Calls", "calls/s", 5009, update_every, RRDSET_TYPE_STACKED);
+
+            for(i = 0; nfsd_proc_values[i].present2 ; i++)
+                rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        for(i = 0; nfsd_proc_values[i].present2 ; i++)
+            rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc2);
+
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_proc3 == 2) {
+        unsigned int i;
+        st = rrdset_find_bytype("nfsd", "proc3");
+        if(!st) {
+            st = rrdset_create("nfsd", "proc3", NULL, "nfsd", NULL, "NFS v3 Calls", "calls/s", 5010, update_every, RRDSET_TYPE_STACKED);
+
+            for(i = 0; nfsd_proc_values[i].present3 ; i++)
+                rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        for(i = 0; nfsd_proc_values[i].present3 ; i++)
+            rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc3);
+
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_proc4 == 2) {
+        unsigned int i;
+        st = rrdset_find_bytype("nfsd", "proc4");
+        if(!st) {
+            st = rrdset_create("nfsd", "proc4", NULL, "nfsd", NULL, "NFS v4 Calls", "calls/s", 5011, update_every, RRDSET_TYPE_STACKED);
+
+            for(i = 0; nfsd_proc_values[i].present4 ; i++)
+                rrddim_add(st, nfsd_proc_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               for(i = 0; nfsd_proc_values[i].present4 ; i++)
-                       rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc4);
+        for(i = 0; nfsd_proc_values[i].present4 ; i++)
+            rrddim_set(st, nfsd_proc_values[i].name, nfsd_proc_values[i].proc4);
 
-               rrdset_done(st);
-       }
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_proc4ops == 2) {
-               unsigned int i;
-               st = rrdset_find_bytype("nfsd", "proc4ops");
-               if(!st) {
-                       st = rrdset_create("nfsd", "proc4ops", NULL, "nfsd", NULL, "NFS v4 Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
+    if(do_proc4ops == 2) {
+        unsigned int i;
+        st = rrdset_find_bytype("nfsd", "proc4ops");
+        if(!st) {
+            st = rrdset_create("nfsd", "proc4ops", NULL, "nfsd", NULL, "NFS v4 Operations", "operations/s", 5012, update_every, RRDSET_TYPE_STACKED);
 
-                       for(i = 0; nfsd4_ops_values[i].present ; i++)
-                               rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            for(i = 0; nfsd4_ops_values[i].present ; i++)
+                rrddim_add(st, nfsd4_ops_values[i].name, NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               for(i = 0; nfsd4_ops_values[i].present ; i++)
-                       rrddim_set(st, nfsd4_ops_values[i].name, nfsd4_ops_values[i].value);
+        for(i = 0; nfsd4_ops_values[i].present ; i++)
+            rrddim_set(st, nfsd4_ops_values[i].name, nfsd4_ops_values[i].value);
 
-               rrdset_done(st);
-       }
+        rrdset_done(st);
+    }
 
-       return 0;
+    return 0;
 }
index 69b2938aae159f005267d1f525cfbf6fc1ce2025..a773f55f603c1a18db44a030246650546f4f2e41 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
-#define RRD_TYPE_NET_SNMP                      "ipv4"
-#define RRD_TYPE_NET_SNMP_LEN          strlen(RRD_TYPE_NET_SNMP)
+#define RRD_TYPE_NET_SNMP           "ipv4"
+#define RRD_TYPE_NET_SNMP_LEN       strlen(RRD_TYPE_NET_SNMP)
 
 int do_proc_net_snmp(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
-               do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1,
-               do_udp_packets = -1, do_udp_errors = -1;
-
-       if(do_ip_packets == -1)         do_ip_packets           = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
-       if(do_ip_fragsout == -1)        do_ip_fragsout          = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", 1);
-       if(do_ip_fragsin == -1)         do_ip_fragsin           = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
-       if(do_ip_errors == -1)          do_ip_errors            = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
-       if(do_tcp_sockets == -1)        do_tcp_sockets          = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
-       if(do_tcp_packets == -1)        do_tcp_packets          = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
-       if(do_tcp_errors == -1)         do_tcp_errors           = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
-       if(do_tcp_handshake == -1)      do_tcp_handshake        = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
-       if(do_udp_packets == -1)        do_udp_packets          = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
-       if(do_udp_errors == -1)         do_udp_errors           = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       RRDSET *st;
-
-       for(l = 0; l < lines ;l++) {
-               if(strcmp(procfile_lineword(ff, l, 0), "Ip") == 0) {
-                       l++;
-
-                       if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) {
-                               error("Cannot read Ip line from /proc/net/snmp.");
-                               break;
-                       }
-
-                       words = procfile_linewords(ff, l);
-                       if(words < 20) {
-                               error("Cannot read /proc/net/snmp Ip line. Expected 20 params, read %u.", words);
-                               continue;
-                       }
-
-                       // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
-                       unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
-                               OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
-
-                       Forwarding              = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       DefaultTTL              = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       InReceives              = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       InHdrErrors             = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       InAddrErrors    = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-                       ForwDatagrams   = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-                       InUnknownProtos = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-                       InDiscards              = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-                       InDelivers              = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-                       OutRequests             = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-                       OutDiscards             = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
-                       OutNoRoutes             = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-                       ReasmTimeout    = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
-                       ReasmReqds              = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
-                       ReasmOKs                = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
-                       ReasmFails              = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
-                       FragOKs                 = strtoull(procfile_lineword(ff, l, 17), NULL, 10);
-                       FragFails               = strtoull(procfile_lineword(ff, l, 18), NULL, 10);
-                       FragCreates             = strtoull(procfile_lineword(ff, l, 19), NULL, 10);
-
-                       // these are not counters
-                       if(Forwarding) {};              // is forwarding enabled?
-                       if(DefaultTTL) {};              // the default ttl on packets
-                       if(ReasmTimeout) {};    // Reassembly timeout
-
-                       // this counter is not used
-                       if(InDelivers) {};              // total number of packets delivered to IP user-protocols
-
-                       // --------------------------------------------------------------------
-
-                       if(do_ip_packets) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".packets");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "sent", OutRequests);
-                               rrddim_set(st, "received", InReceives);
-                               rrddim_set(st, "forwarded", ForwDatagrams);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_ip_fragsout) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsout");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "ok", FragOKs);
-                               rrddim_set(st, "failed", FragFails);
-                               rrddim_set(st, "all", FragCreates);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_ip_fragsin) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsin");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "ok", ReasmOKs);
-                               rrddim_set(st, "failed", ReasmFails);
-                               rrddim_set(st, "all", ReasmReqds);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_ip_errors) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".errors");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
-                                       rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-
-                                       rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "InDiscards", InDiscards);
-                               rrddim_set(st, "OutDiscards", OutDiscards);
-                               rrddim_set(st, "InHdrErrors", InHdrErrors);
-                               rrddim_set(st, "InAddrErrors", InAddrErrors);
-                               rrddim_set(st, "InUnknownProtos", InUnknownProtos);
-                               rrddim_set(st, "OutNoRoutes", OutNoRoutes);
-                               rrdset_done(st);
-                       }
-               }
-               else if(strcmp(procfile_lineword(ff, l, 0), "Tcp") == 0) {
-                       l++;
-
-                       if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) {
-                               error("Cannot read Tcp line from /proc/net/snmp.");
-                               break;
-                       }
-
-                       words = procfile_linewords(ff, l);
-                       if(words < 15) {
-                               error("Cannot read /proc/net/snmp Tcp line. Expected 15 params, read %u.", words);
-                               continue;
-                       }
-
-                       unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets,
-                               CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
-
-                       RtoAlgorithm    = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       RtoMin                  = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       RtoMax                  = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       MaxConn                 = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       ActiveOpens             = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-                       PassiveOpens    = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-                       AttemptFails    = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-                       EstabResets             = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-                       CurrEstab               = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-                       InSegs                  = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-                       OutSegs                 = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
-                       RetransSegs             = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
-                       InErrs                  = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
-                       OutRsts                 = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
-
-                       // these are not counters
-                       if(RtoAlgorithm) {};
-                       if(RtoMin) {};
-                       if(RtoMax) {};
-                       if(MaxConn) {};
-
-                       // --------------------------------------------------------------------
-
-                       // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
-                       if(do_tcp_sockets) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".tcpsock");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", "active connections", 2500, update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "connections", CurrEstab);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_tcp_packets) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".tcppackets");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "received", InSegs);
-                               rrddim_set(st, "sent", OutSegs);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_tcp_errors) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".tcperrors");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "InErrs", InErrs);
-                               rrddim_set(st, "RetransSegs", RetransSegs);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_tcp_handshake) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".tcphandshake");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "OutRsts", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "EstabResets", EstabResets);
-                               rrddim_set(st, "OutRsts", OutRsts);
-                               rrddim_set(st, "ActiveOpens", ActiveOpens);
-                               rrddim_set(st, "PassiveOpens", PassiveOpens);
-                               rrddim_set(st, "AttemptFails", AttemptFails);
-                               rrdset_done(st);
-                       }
-               }
-               else if(strcmp(procfile_lineword(ff, l, 0), "Udp") == 0) {
-                       l++;
-
-                       if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) {
-                               error("Cannot read Udp line from /proc/net/snmp.");
-                               break;
-                       }
-
-                       words = procfile_linewords(ff, l);
-                       if(words < 7) {
-                               error("Cannot read /proc/net/snmp Udp line. Expected 7 params, read %u.", words);
-                               continue;
-                       }
-
-                       unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
-
-                       InDatagrams             = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       NoPorts                 = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       InErrors                = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       OutDatagrams    = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       RcvbufErrors    = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-                       SndbufErrors    = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-
-                       // --------------------------------------------------------------------
-
-                       // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
-                       if(do_udp_packets) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".udppackets");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "received", InDatagrams);
-                               rrddim_set(st, "sent", OutDatagrams);
-                               rrdset_done(st);
-                       }
-
-                       // --------------------------------------------------------------------
-
-                       if(do_udp_errors) {
-                               st = rrdset_find(RRD_TYPE_NET_SNMP ".udperrors");
-                               if(!st) {
-                                       st = rrdset_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", 2701, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "InErrors", InErrors);
-                               rrddim_set(st, "NoPorts", NoPorts);
-                               rrddim_set(st, "RcvbufErrors", RcvbufErrors);
-                               rrddim_set(st, "SndbufErrors", SndbufErrors);
-                               rrdset_done(st);
-                       }
-               }
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+    static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
+        do_tcp_sockets = -1, do_tcp_packets = -1, do_tcp_errors = -1, do_tcp_handshake = -1,
+        do_udp_packets = -1, do_udp_errors = -1;
+
+    if(do_ip_packets == -1)     do_ip_packets       = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 packets", 1);
+    if(do_ip_fragsout == -1)    do_ip_fragsout      = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments sent", 1);
+    if(do_ip_fragsin == -1)     do_ip_fragsin       = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 fragments assembly", 1);
+    if(do_ip_errors == -1)      do_ip_errors        = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 errors", 1);
+    if(do_tcp_sockets == -1)    do_tcp_sockets      = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP connections", 1);
+    if(do_tcp_packets == -1)    do_tcp_packets      = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP packets", 1);
+    if(do_tcp_errors == -1)     do_tcp_errors       = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP errors", 1);
+    if(do_tcp_handshake == -1)  do_tcp_handshake    = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 TCP handshake issues", 1);
+    if(do_udp_packets == -1)    do_udp_packets      = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP packets", 1);
+    if(do_udp_errors == -1)     do_udp_errors       = config_get_boolean("plugin:proc:/proc/net/snmp", "ipv4 UDP errors", 1);
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/snmp", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    RRDSET *st;
+
+    for(l = 0; l < lines ;l++) {
+        if(strcmp(procfile_lineword(ff, l, 0), "Ip") == 0) {
+            l++;
+
+            if(strcmp(procfile_lineword(ff, l, 0), "Ip") != 0) {
+                error("Cannot read Ip line from /proc/net/snmp.");
+                break;
+            }
+
+            words = procfile_linewords(ff, l);
+            if(words < 20) {
+                error("Cannot read /proc/net/snmp Ip line. Expected 20 params, read %u.", words);
+                continue;
+            }
+
+            // see also http://net-snmp.sourceforge.net/docs/mibs/ip.html
+            unsigned long long Forwarding, DefaultTTL, InReceives, InHdrErrors, InAddrErrors, ForwDatagrams, InUnknownProtos, InDiscards, InDelivers,
+                OutRequests, OutDiscards, OutNoRoutes, ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails, FragOKs, FragFails, FragCreates;
+
+            Forwarding      = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            DefaultTTL      = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            InReceives      = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            InHdrErrors     = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            InAddrErrors    = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+            ForwDatagrams   = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+            InUnknownProtos = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+            InDiscards      = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+            InDelivers      = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+            OutRequests     = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+            OutDiscards     = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+            OutNoRoutes     = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+            ReasmTimeout    = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+            ReasmReqds      = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
+            ReasmOKs        = strtoull(procfile_lineword(ff, l, 15), NULL, 10);
+            ReasmFails      = strtoull(procfile_lineword(ff, l, 16), NULL, 10);
+            FragOKs         = strtoull(procfile_lineword(ff, l, 17), NULL, 10);
+            FragFails       = strtoull(procfile_lineword(ff, l, 18), NULL, 10);
+            FragCreates     = strtoull(procfile_lineword(ff, l, 19), NULL, 10);
+
+            // these are not counters
+            if(Forwarding) {};      // is forwarding enabled?
+            if(DefaultTTL) {};      // the default ttl on packets
+            if(ReasmTimeout) {};    // Reassembly timeout
+
+            // this counter is not used
+            if(InDelivers) {};      // total number of packets delivered to IP user-protocols
+
+            // --------------------------------------------------------------------
+
+            if(do_ip_packets) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".packets");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "packets", NULL, "packets", NULL, "IPv4 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "sent", OutRequests);
+                rrddim_set(st, "received", InReceives);
+                rrddim_set(st, "forwarded", ForwDatagrams);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_ip_fragsout) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsout");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsout", NULL, "fragments", NULL, "IPv4 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "ok", FragOKs);
+                rrddim_set(st, "failed", FragFails);
+                rrddim_set(st, "all", FragCreates);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_ip_fragsin) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".fragsin");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "fragsin", NULL, "fragments", NULL, "IPv4 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "ok", ReasmOKs);
+                rrddim_set(st, "failed", ReasmFails);
+                rrddim_set(st, "all", ReasmReqds);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_ip_errors) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".errors");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "errors", NULL, "errors", NULL, "IPv4 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+
+                    rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+
+                    rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "InDiscards", InDiscards);
+                rrddim_set(st, "OutDiscards", OutDiscards);
+                rrddim_set(st, "InHdrErrors", InHdrErrors);
+                rrddim_set(st, "InAddrErrors", InAddrErrors);
+                rrddim_set(st, "InUnknownProtos", InUnknownProtos);
+                rrddim_set(st, "OutNoRoutes", OutNoRoutes);
+                rrdset_done(st);
+            }
+        }
+        else if(strcmp(procfile_lineword(ff, l, 0), "Tcp") == 0) {
+            l++;
+
+            if(strcmp(procfile_lineword(ff, l, 0), "Tcp") != 0) {
+                error("Cannot read Tcp line from /proc/net/snmp.");
+                break;
+            }
+
+            words = procfile_linewords(ff, l);
+            if(words < 15) {
+                error("Cannot read /proc/net/snmp Tcp line. Expected 15 params, read %u.", words);
+                continue;
+            }
+
+            unsigned long long RtoAlgorithm, RtoMin, RtoMax, MaxConn, ActiveOpens, PassiveOpens, AttemptFails, EstabResets,
+                CurrEstab, InSegs, OutSegs, RetransSegs, InErrs, OutRsts;
+
+            RtoAlgorithm    = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            RtoMin          = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            RtoMax          = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            MaxConn         = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            ActiveOpens     = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+            PassiveOpens    = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+            AttemptFails    = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+            EstabResets     = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+            CurrEstab       = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+            InSegs          = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+            OutSegs         = strtoull(procfile_lineword(ff, l, 11), NULL, 10);
+            RetransSegs     = strtoull(procfile_lineword(ff, l, 12), NULL, 10);
+            InErrs          = strtoull(procfile_lineword(ff, l, 13), NULL, 10);
+            OutRsts         = strtoull(procfile_lineword(ff, l, 14), NULL, 10);
+
+            // these are not counters
+            if(RtoAlgorithm) {};
+            if(RtoMin) {};
+            if(RtoMax) {};
+            if(MaxConn) {};
+
+            // --------------------------------------------------------------------
+
+            // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
+            if(do_tcp_sockets) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcpsock");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcpsock", NULL, "tcp", NULL, "IPv4 TCP Connections", "active connections", 2500, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "connections", CurrEstab);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_tcp_packets) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcppackets");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcppackets", NULL, "tcp", NULL, "IPv4 TCP Packets", "packets/s", 2600, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "received", InSegs);
+                rrddim_set(st, "sent", OutSegs);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_tcp_errors) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcperrors");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcperrors", NULL, "tcp", NULL, "IPv4 TCP Errors", "packets/s", 2700, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "InErrs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "RetransSegs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "InErrs", InErrs);
+                rrddim_set(st, "RetransSegs", RetransSegs);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_tcp_handshake) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".tcphandshake");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "tcphandshake", NULL, "tcp", NULL, "IPv4 TCP Handshake Issues", "events/s", 2900, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "EstabResets", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "OutRsts", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "ActiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "PassiveOpens", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "AttemptFails", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "EstabResets", EstabResets);
+                rrddim_set(st, "OutRsts", OutRsts);
+                rrddim_set(st, "ActiveOpens", ActiveOpens);
+                rrddim_set(st, "PassiveOpens", PassiveOpens);
+                rrddim_set(st, "AttemptFails", AttemptFails);
+                rrdset_done(st);
+            }
+        }
+        else if(strcmp(procfile_lineword(ff, l, 0), "Udp") == 0) {
+            l++;
+
+            if(strcmp(procfile_lineword(ff, l, 0), "Udp") != 0) {
+                error("Cannot read Udp line from /proc/net/snmp.");
+                break;
+            }
+
+            words = procfile_linewords(ff, l);
+            if(words < 7) {
+                error("Cannot read /proc/net/snmp Udp line. Expected 7 params, read %u.", words);
+                continue;
+            }
+
+            unsigned long long InDatagrams, NoPorts, InErrors, OutDatagrams, RcvbufErrors, SndbufErrors;
+
+            InDatagrams     = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            NoPorts         = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            InErrors        = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            OutDatagrams    = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            RcvbufErrors    = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+            SndbufErrors    = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+
+            // --------------------------------------------------------------------
+
+            // see http://net-snmp.sourceforge.net/docs/mibs/udp.html
+            if(do_udp_packets) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".udppackets");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "udppackets", NULL, "udp", NULL, "IPv4 UDP Packets", "packets/s", 2601, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "received", InDatagrams);
+                rrddim_set(st, "sent", OutDatagrams);
+                rrdset_done(st);
+            }
+
+            // --------------------------------------------------------------------
+
+            if(do_udp_errors) {
+                st = rrdset_find(RRD_TYPE_NET_SNMP ".udperrors");
+                if(!st) {
+                    st = rrdset_create(RRD_TYPE_NET_SNMP, "udperrors", NULL, "udp", NULL, "IPv4 UDP Errors", "events/s", 2701, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "InErrors", InErrors);
+                rrddim_set(st, "NoPorts", NoPorts);
+                rrddim_set(st, "RcvbufErrors", RcvbufErrors);
+                rrddim_set(st, "SndbufErrors", SndbufErrors);
+                rrdset_done(st);
+            }
+        }
+    }
+
+    return 0;
 }
 
index 4586ee7d0d638dd9216f81833159bb5be55ad755..97dc20edd27ad7f23c4350d60092085cf158a6a4 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
-#define RRD_TYPE_NET_SNMP6                     "ipv6"
-#define RRD_TYPE_NET_SNMP6_LEN         strlen(RRD_TYPE_NET_SNMP6)
+#define RRD_TYPE_NET_SNMP6          "ipv6"
+#define RRD_TYPE_NET_SNMP6_LEN      strlen(RRD_TYPE_NET_SNMP6)
 
 int do_proc_net_snmp6(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int gen_hashes = -1;
-
-       static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
-               do_udplite_packets = -1, do_udplite_errors = -1,
-               do_udp_packets = -1, do_udp_errors = -1,
-               do_bandwidth = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1,
-               do_icmp = -1, do_icmp_redir = -1, do_icmp_errors = -1, do_icmp_echos = -1, do_icmp_groupmemb = -1,
-               do_icmp_router = -1, do_icmp_neighbor = -1, do_icmp_mldv2 = -1, do_icmp_types = -1, do_ect = -1;
-
-       static uint32_t hash_Ip6InReceives = -1;
-
-       static uint32_t hash_Ip6InHdrErrors = -1;
-       static uint32_t hash_Ip6InTooBigErrors = -1;
-       static uint32_t hash_Ip6InNoRoutes = -1;
-       static uint32_t hash_Ip6InAddrErrors = -1;
-       static uint32_t hash_Ip6InUnknownProtos = -1;
-       static uint32_t hash_Ip6InTruncatedPkts = -1;
-       static uint32_t hash_Ip6InDiscards = -1;
-       static uint32_t hash_Ip6InDelivers = -1;
-
-       static uint32_t hash_Ip6OutForwDatagrams = -1;
-       static uint32_t hash_Ip6OutRequests = -1;
-       static uint32_t hash_Ip6OutDiscards = -1;
-       static uint32_t hash_Ip6OutNoRoutes = -1;
-
-       static uint32_t hash_Ip6ReasmTimeout = -1;
-       static uint32_t hash_Ip6ReasmReqds = -1;
-       static uint32_t hash_Ip6ReasmOKs = -1;
-       static uint32_t hash_Ip6ReasmFails = -1;
-
-       static uint32_t hash_Ip6FragOKs = -1;
-       static uint32_t hash_Ip6FragFails = -1;
-       static uint32_t hash_Ip6FragCreates = -1;
-
-       static uint32_t hash_Ip6InMcastPkts = -1;
-       static uint32_t hash_Ip6OutMcastPkts = -1;
-
-       static uint32_t hash_Ip6InOctets = -1;
-       static uint32_t hash_Ip6OutOctets = -1;
-
-       static uint32_t hash_Ip6InMcastOctets = -1;
-       static uint32_t hash_Ip6OutMcastOctets = -1;
-       static uint32_t hash_Ip6InBcastOctets = -1;
-       static uint32_t hash_Ip6OutBcastOctets = -1;
-
-       static uint32_t hash_Ip6InNoECTPkts = -1;
-       static uint32_t hash_Ip6InECT1Pkts = -1;
-       static uint32_t hash_Ip6InECT0Pkts = -1;
-       static uint32_t hash_Ip6InCEPkts = -1;
-
-       static uint32_t hash_Icmp6InMsgs = -1;
-       static uint32_t hash_Icmp6InErrors = -1;
-       static uint32_t hash_Icmp6OutMsgs = -1;
-       static uint32_t hash_Icmp6OutErrors = -1;
-       static uint32_t hash_Icmp6InCsumErrors = -1;
-       static uint32_t hash_Icmp6InDestUnreachs = -1;
-       static uint32_t hash_Icmp6InPktTooBigs = -1;
-       static uint32_t hash_Icmp6InTimeExcds = -1;
-       static uint32_t hash_Icmp6InParmProblems = -1;
-       static uint32_t hash_Icmp6InEchos = -1;
-       static uint32_t hash_Icmp6InEchoReplies = -1;
-       static uint32_t hash_Icmp6InGroupMembQueries = -1;
-       static uint32_t hash_Icmp6InGroupMembResponses = -1;
-       static uint32_t hash_Icmp6InGroupMembReductions = -1;
-       static uint32_t hash_Icmp6InRouterSolicits = -1;
-       static uint32_t hash_Icmp6InRouterAdvertisements = -1;
-       static uint32_t hash_Icmp6InNeighborSolicits = -1;
-       static uint32_t hash_Icmp6InNeighborAdvertisements = -1;
-       static uint32_t hash_Icmp6InRedirects = -1;
-       static uint32_t hash_Icmp6InMLDv2Reports = -1;
-       static uint32_t hash_Icmp6OutDestUnreachs = -1;
-       static uint32_t hash_Icmp6OutPktTooBigs = -1;
-       static uint32_t hash_Icmp6OutTimeExcds = -1;
-       static uint32_t hash_Icmp6OutParmProblems = -1;
-       static uint32_t hash_Icmp6OutEchos = -1;
-       static uint32_t hash_Icmp6OutEchoReplies = -1;
-       static uint32_t hash_Icmp6OutGroupMembQueries = -1;
-       static uint32_t hash_Icmp6OutGroupMembResponses = -1;
-       static uint32_t hash_Icmp6OutGroupMembReductions = -1;
-       static uint32_t hash_Icmp6OutRouterSolicits = -1;
-       static uint32_t hash_Icmp6OutRouterAdvertisements = -1;
-       static uint32_t hash_Icmp6OutNeighborSolicits = -1;
-       static uint32_t hash_Icmp6OutNeighborAdvertisements = -1;
-       static uint32_t hash_Icmp6OutRedirects = -1;
-       static uint32_t hash_Icmp6OutMLDv2Reports = -1;
-       static uint32_t hash_Icmp6InType1 = -1;
-       static uint32_t hash_Icmp6InType128 = -1;
-       static uint32_t hash_Icmp6InType129 = -1;
-       static uint32_t hash_Icmp6InType136 = -1;
-       static uint32_t hash_Icmp6OutType1 = -1;
-       static uint32_t hash_Icmp6OutType128 = -1;
-       static uint32_t hash_Icmp6OutType129 = -1;
-       static uint32_t hash_Icmp6OutType133 = -1;
-       static uint32_t hash_Icmp6OutType135 = -1;
-       static uint32_t hash_Icmp6OutType143 = -1;
-
-       static uint32_t hash_Udp6InDatagrams = -1;
-       static uint32_t hash_Udp6NoPorts = -1;
-       static uint32_t hash_Udp6InErrors = -1;
-       static uint32_t hash_Udp6OutDatagrams = -1;
-       static uint32_t hash_Udp6RcvbufErrors = -1;
-       static uint32_t hash_Udp6SndbufErrors = -1;
-       static uint32_t hash_Udp6InCsumErrors = -1;
-       static uint32_t hash_Udp6IgnoredMulti = -1;
-
-       static uint32_t hash_UdpLite6InDatagrams = -1;
-       static uint32_t hash_UdpLite6NoPorts = -1;
-       static uint32_t hash_UdpLite6InErrors = -1;
-       static uint32_t hash_UdpLite6OutDatagrams = -1;
-       static uint32_t hash_UdpLite6RcvbufErrors = -1;
-       static uint32_t hash_UdpLite6SndbufErrors = -1;
-       static uint32_t hash_UdpLite6InCsumErrors = -1;
-
-       if(gen_hashes != 1) {
-               gen_hashes = 1;
-               hash_Ip6InReceives = simple_hash("Ip6InReceives");
-               hash_Ip6InHdrErrors = simple_hash("Ip6InHdrErrors");
-               hash_Ip6InTooBigErrors = simple_hash("Ip6InTooBigErrors");
-               hash_Ip6InNoRoutes = simple_hash("Ip6InNoRoutes");
-               hash_Ip6InAddrErrors = simple_hash("Ip6InAddrErrors");
-               hash_Ip6InUnknownProtos = simple_hash("Ip6InUnknownProtos");
-               hash_Ip6InTruncatedPkts = simple_hash("Ip6InTruncatedPkts");
-               hash_Ip6InDiscards = simple_hash("Ip6InDiscards");
-               hash_Ip6InDelivers = simple_hash("Ip6InDelivers");
-               hash_Ip6OutForwDatagrams = simple_hash("Ip6OutForwDatagrams");
-               hash_Ip6OutRequests = simple_hash("Ip6OutRequests");
-               hash_Ip6OutDiscards = simple_hash("Ip6OutDiscards");
-               hash_Ip6OutNoRoutes = simple_hash("Ip6OutNoRoutes");
-               hash_Ip6ReasmTimeout = simple_hash("Ip6ReasmTimeout");
-               hash_Ip6ReasmReqds = simple_hash("Ip6ReasmReqds");
-               hash_Ip6ReasmOKs = simple_hash("Ip6ReasmOKs");
-               hash_Ip6ReasmFails = simple_hash("Ip6ReasmFails");
-               hash_Ip6FragOKs = simple_hash("Ip6FragOKs");
-               hash_Ip6FragFails = simple_hash("Ip6FragFails");
-               hash_Ip6FragCreates = simple_hash("Ip6FragCreates");
-               hash_Ip6InMcastPkts = simple_hash("Ip6InMcastPkts");
-               hash_Ip6OutMcastPkts = simple_hash("Ip6OutMcastPkts");
-               hash_Ip6InOctets = simple_hash("Ip6InOctets");
-               hash_Ip6OutOctets = simple_hash("Ip6OutOctets");
-               hash_Ip6InMcastOctets = simple_hash("Ip6InMcastOctets");
-               hash_Ip6OutMcastOctets = simple_hash("Ip6OutMcastOctets");
-               hash_Ip6InBcastOctets = simple_hash("Ip6InBcastOctets");
-               hash_Ip6OutBcastOctets = simple_hash("Ip6OutBcastOctets");
-               hash_Ip6InNoECTPkts = simple_hash("Ip6InNoECTPkts");
-               hash_Ip6InECT1Pkts = simple_hash("Ip6InECT1Pkts");
-               hash_Ip6InECT0Pkts = simple_hash("Ip6InECT0Pkts");
-               hash_Ip6InCEPkts = simple_hash("Ip6InCEPkts");
-               hash_Icmp6InMsgs = simple_hash("Icmp6InMsgs");
-               hash_Icmp6InErrors = simple_hash("Icmp6InErrors");
-               hash_Icmp6OutMsgs = simple_hash("Icmp6OutMsgs");
-               hash_Icmp6OutErrors = simple_hash("Icmp6OutErrors");
-               hash_Icmp6InCsumErrors = simple_hash("Icmp6InCsumErrors");
-               hash_Icmp6InDestUnreachs = simple_hash("Icmp6InDestUnreachs");
-               hash_Icmp6InPktTooBigs = simple_hash("Icmp6InPktTooBigs");
-               hash_Icmp6InTimeExcds = simple_hash("Icmp6InTimeExcds");
-               hash_Icmp6InParmProblems = simple_hash("Icmp6InParmProblems");
-               hash_Icmp6InEchos = simple_hash("Icmp6InEchos");
-               hash_Icmp6InEchoReplies = simple_hash("Icmp6InEchoReplies");
-               hash_Icmp6InGroupMembQueries = simple_hash("Icmp6InGroupMembQueries");
-               hash_Icmp6InGroupMembResponses = simple_hash("Icmp6InGroupMembResponses");
-               hash_Icmp6InGroupMembReductions = simple_hash("Icmp6InGroupMembReductions");
-               hash_Icmp6InRouterSolicits = simple_hash("Icmp6InRouterSolicits");
-               hash_Icmp6InRouterAdvertisements = simple_hash("Icmp6InRouterAdvertisements");
-               hash_Icmp6InNeighborSolicits = simple_hash("Icmp6InNeighborSolicits");
-               hash_Icmp6InNeighborAdvertisements = simple_hash("Icmp6InNeighborAdvertisements");
-               hash_Icmp6InRedirects = simple_hash("Icmp6InRedirects");
-               hash_Icmp6InMLDv2Reports = simple_hash("Icmp6InMLDv2Reports");
-               hash_Icmp6OutDestUnreachs = simple_hash("Icmp6OutDestUnreachs");
-               hash_Icmp6OutPktTooBigs = simple_hash("Icmp6OutPktTooBigs");
-               hash_Icmp6OutTimeExcds = simple_hash("Icmp6OutTimeExcds");
-               hash_Icmp6OutParmProblems = simple_hash("Icmp6OutParmProblems");
-               hash_Icmp6OutEchos = simple_hash("Icmp6OutEchos");
-               hash_Icmp6OutEchoReplies = simple_hash("Icmp6OutEchoReplies");
-               hash_Icmp6OutGroupMembQueries = simple_hash("Icmp6OutGroupMembQueries");
-               hash_Icmp6OutGroupMembResponses = simple_hash("Icmp6OutGroupMembResponses");
-               hash_Icmp6OutGroupMembReductions = simple_hash("Icmp6OutGroupMembReductions");
-               hash_Icmp6OutRouterSolicits = simple_hash("Icmp6OutRouterSolicits");
-               hash_Icmp6OutRouterAdvertisements = simple_hash("Icmp6OutRouterAdvertisements");
-               hash_Icmp6OutNeighborSolicits = simple_hash("Icmp6OutNeighborSolicits");
-               hash_Icmp6OutNeighborAdvertisements = simple_hash("Icmp6OutNeighborAdvertisements");
-               hash_Icmp6OutRedirects = simple_hash("Icmp6OutRedirects");
-               hash_Icmp6OutMLDv2Reports = simple_hash("Icmp6OutMLDv2Reports");
-               hash_Icmp6InType1 = simple_hash("Icmp6InType1");
-               hash_Icmp6InType128 = simple_hash("Icmp6InType128");
-               hash_Icmp6InType129 = simple_hash("Icmp6InType129");
-               hash_Icmp6InType136 = simple_hash("Icmp6InType136");
-               hash_Icmp6OutType1 = simple_hash("Icmp6OutType1");
-               hash_Icmp6OutType128 = simple_hash("Icmp6OutType128");
-               hash_Icmp6OutType129 = simple_hash("Icmp6OutType129");
-               hash_Icmp6OutType133 = simple_hash("Icmp6OutType133");
-               hash_Icmp6OutType135 = simple_hash("Icmp6OutType135");
-               hash_Icmp6OutType143 = simple_hash("Icmp6OutType143");
-               hash_Udp6InDatagrams = simple_hash("Udp6InDatagrams");
-               hash_Udp6NoPorts = simple_hash("Udp6NoPorts");
-               hash_Udp6InErrors = simple_hash("Udp6InErrors");
-               hash_Udp6OutDatagrams = simple_hash("Udp6OutDatagrams");
-               hash_Udp6RcvbufErrors = simple_hash("Udp6RcvbufErrors");
-               hash_Udp6SndbufErrors = simple_hash("Udp6SndbufErrors");
-               hash_Udp6InCsumErrors = simple_hash("Udp6InCsumErrors");
-               hash_Udp6IgnoredMulti = simple_hash("Udp6IgnoredMulti");
-               hash_UdpLite6InDatagrams = simple_hash("UdpLite6InDatagrams");
-               hash_UdpLite6NoPorts = simple_hash("UdpLite6NoPorts");
-               hash_UdpLite6InErrors = simple_hash("UdpLite6InErrors");
-               hash_UdpLite6OutDatagrams = simple_hash("UdpLite6OutDatagrams");
-               hash_UdpLite6RcvbufErrors = simple_hash("UdpLite6RcvbufErrors");
-               hash_UdpLite6SndbufErrors = simple_hash("UdpLite6SndbufErrors");
-               hash_UdpLite6InCsumErrors = simple_hash("UdpLite6InCsumErrors");
-       }
-
-       if(do_ip_packets == -1)                 do_ip_packets           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_ip_fragsout == -1)                do_ip_fragsout          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_ip_fragsin == -1)                 do_ip_fragsin           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_ip_errors == -1)                  do_ip_errors            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_udp_packets == -1)                do_udp_packets          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_udp_errors == -1)                 do_udp_errors           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_udplite_packets == -1)    do_udplite_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_udplite_errors == -1)             do_udplite_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_bandwidth == -1)                  do_bandwidth            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_mcast == -1)                              do_mcast                        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_bcast == -1)                              do_bcast                        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_mcast_p == -1)                    do_mcast_p                      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp == -1)                               do_icmp                         = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_redir == -1)                 do_icmp_redir           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_errors == -1)                do_icmp_errors          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_echos == -1)                 do_icmp_echos           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_groupmemb == -1)             do_icmp_groupmemb       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_router == -1)                do_icmp_router          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_neighbor == -1)              do_icmp_neighbor        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_mldv2 == -1)                 do_icmp_mldv2           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_icmp_types == -1)                 do_icmp_types           = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_ect == -1)                                do_ect                          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND);
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp6");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       unsigned long long Ip6InReceives = 0ULL;
-       unsigned long long Ip6InHdrErrors = 0ULL;
-       unsigned long long Ip6InTooBigErrors = 0ULL;
-       unsigned long long Ip6InNoRoutes = 0ULL;
-       unsigned long long Ip6InAddrErrors = 0ULL;
-       unsigned long long Ip6InUnknownProtos = 0ULL;
-       unsigned long long Ip6InTruncatedPkts = 0ULL;
-       unsigned long long Ip6InDiscards = 0ULL;
-       unsigned long long Ip6InDelivers = 0ULL;
-       unsigned long long Ip6OutForwDatagrams = 0ULL;
-       unsigned long long Ip6OutRequests = 0ULL;
-       unsigned long long Ip6OutDiscards = 0ULL;
-       unsigned long long Ip6OutNoRoutes = 0ULL;
-       unsigned long long Ip6ReasmTimeout = 0ULL;
-       unsigned long long Ip6ReasmReqds = 0ULL;
-       unsigned long long Ip6ReasmOKs = 0ULL;
-       unsigned long long Ip6ReasmFails = 0ULL;
-       unsigned long long Ip6FragOKs = 0ULL;
-       unsigned long long Ip6FragFails = 0ULL;
-       unsigned long long Ip6FragCreates = 0ULL;
-       unsigned long long Ip6InMcastPkts = 0ULL;
-       unsigned long long Ip6OutMcastPkts = 0ULL;
-       unsigned long long Ip6InOctets = 0ULL;
-       unsigned long long Ip6OutOctets = 0ULL;
-       unsigned long long Ip6InMcastOctets = 0ULL;
-       unsigned long long Ip6OutMcastOctets = 0ULL;
-       unsigned long long Ip6InBcastOctets = 0ULL;
-       unsigned long long Ip6OutBcastOctets = 0ULL;
-       unsigned long long Ip6InNoECTPkts = 0ULL;
-       unsigned long long Ip6InECT1Pkts = 0ULL;
-       unsigned long long Ip6InECT0Pkts = 0ULL;
-       unsigned long long Ip6InCEPkts = 0ULL;
-       unsigned long long Icmp6InMsgs = 0ULL;
-       unsigned long long Icmp6InErrors = 0ULL;
-       unsigned long long Icmp6OutMsgs = 0ULL;
-       unsigned long long Icmp6OutErrors = 0ULL;
-       unsigned long long Icmp6InCsumErrors = 0ULL;
-       unsigned long long Icmp6InDestUnreachs = 0ULL;
-       unsigned long long Icmp6InPktTooBigs = 0ULL;
-       unsigned long long Icmp6InTimeExcds = 0ULL;
-       unsigned long long Icmp6InParmProblems = 0ULL;
-       unsigned long long Icmp6InEchos = 0ULL;
-       unsigned long long Icmp6InEchoReplies = 0ULL;
-       unsigned long long Icmp6InGroupMembQueries = 0ULL;
-       unsigned long long Icmp6InGroupMembResponses = 0ULL;
-       unsigned long long Icmp6InGroupMembReductions = 0ULL;
-       unsigned long long Icmp6InRouterSolicits = 0ULL;
-       unsigned long long Icmp6InRouterAdvertisements = 0ULL;
-       unsigned long long Icmp6InNeighborSolicits = 0ULL;
-       unsigned long long Icmp6InNeighborAdvertisements = 0ULL;
-       unsigned long long Icmp6InRedirects = 0ULL;
-       unsigned long long Icmp6InMLDv2Reports = 0ULL;
-       unsigned long long Icmp6OutDestUnreachs = 0ULL;
-       unsigned long long Icmp6OutPktTooBigs = 0ULL;
-       unsigned long long Icmp6OutTimeExcds = 0ULL;
-       unsigned long long Icmp6OutParmProblems = 0ULL;
-       unsigned long long Icmp6OutEchos = 0ULL;
-       unsigned long long Icmp6OutEchoReplies = 0ULL;
-       unsigned long long Icmp6OutGroupMembQueries = 0ULL;
-       unsigned long long Icmp6OutGroupMembResponses = 0ULL;
-       unsigned long long Icmp6OutGroupMembReductions = 0ULL;
-       unsigned long long Icmp6OutRouterSolicits = 0ULL;
-       unsigned long long Icmp6OutRouterAdvertisements = 0ULL;
-       unsigned long long Icmp6OutNeighborSolicits = 0ULL;
-       unsigned long long Icmp6OutNeighborAdvertisements = 0ULL;
-       unsigned long long Icmp6OutRedirects = 0ULL;
-       unsigned long long Icmp6OutMLDv2Reports = 0ULL;
-       unsigned long long Icmp6InType1 = 0ULL;
-       unsigned long long Icmp6InType128 = 0ULL;
-       unsigned long long Icmp6InType129 = 0ULL;
-       unsigned long long Icmp6InType136 = 0ULL;
-       unsigned long long Icmp6OutType1 = 0ULL;
-       unsigned long long Icmp6OutType128 = 0ULL;
-       unsigned long long Icmp6OutType129 = 0ULL;
-       unsigned long long Icmp6OutType133 = 0ULL;
-       unsigned long long Icmp6OutType135 = 0ULL;
-       unsigned long long Icmp6OutType143 = 0ULL;
-       unsigned long long Udp6InDatagrams = 0ULL;
-       unsigned long long Udp6NoPorts = 0ULL;
-       unsigned long long Udp6InErrors = 0ULL;
-       unsigned long long Udp6OutDatagrams = 0ULL;
-       unsigned long long Udp6RcvbufErrors = 0ULL;
-       unsigned long long Udp6SndbufErrors = 0ULL;
-       unsigned long long Udp6InCsumErrors = 0ULL;
-       unsigned long long Udp6IgnoredMulti = 0ULL;
-       unsigned long long UdpLite6InDatagrams = 0ULL;
-       unsigned long long UdpLite6NoPorts = 0ULL;
-       unsigned long long UdpLite6InErrors = 0ULL;
-       unsigned long long UdpLite6OutDatagrams = 0ULL;
-       unsigned long long UdpLite6RcvbufErrors = 0ULL;
-       unsigned long long UdpLite6SndbufErrors = 0ULL;
-       unsigned long long UdpLite6InCsumErrors = 0ULL;
-
-       for(l = 0; l < lines ;l++) {
-               words = procfile_linewords(ff, l);
-               if(words < 2) {
-                       if(words) error("Cannot read /proc/net/snmp6 line %u. Expected 2 params, read %u.", l, words);
-                       continue;
-               }
-
-               char *name = procfile_lineword(ff, l, 0);
-               char * value = procfile_lineword(ff, l, 1);
-               if(!name || !*name || !value || !*value) continue;
-
-               uint32_t hash = simple_hash(name);
-
-               if(0) ;
-               else if(hash == hash_Ip6InReceives && strcmp(name, "Ip6InReceives") == 0) Ip6InReceives = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InHdrErrors && strcmp(name, "Ip6InHdrErrors") == 0) Ip6InHdrErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InTooBigErrors && strcmp(name, "Ip6InTooBigErrors") == 0) Ip6InTooBigErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InNoRoutes && strcmp(name, "Ip6InNoRoutes") == 0) Ip6InNoRoutes = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InAddrErrors && strcmp(name, "Ip6InAddrErrors") == 0) Ip6InAddrErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InUnknownProtos && strcmp(name, "Ip6InUnknownProtos") == 0) Ip6InUnknownProtos = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InTruncatedPkts && strcmp(name, "Ip6InTruncatedPkts") == 0) Ip6InTruncatedPkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InDiscards && strcmp(name, "Ip6InDiscards") == 0) Ip6InDiscards = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InDelivers && strcmp(name, "Ip6InDelivers") == 0) Ip6InDelivers = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutForwDatagrams && strcmp(name, "Ip6OutForwDatagrams") == 0) Ip6OutForwDatagrams = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutRequests && strcmp(name, "Ip6OutRequests") == 0) Ip6OutRequests = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutDiscards && strcmp(name, "Ip6OutDiscards") == 0) Ip6OutDiscards = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutNoRoutes && strcmp(name, "Ip6OutNoRoutes") == 0) Ip6OutNoRoutes = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6ReasmTimeout && strcmp(name, "Ip6ReasmTimeout") == 0) Ip6ReasmTimeout = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6ReasmReqds && strcmp(name, "Ip6ReasmReqds") == 0) Ip6ReasmReqds = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6ReasmOKs && strcmp(name, "Ip6ReasmOKs") == 0) Ip6ReasmOKs = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6ReasmFails && strcmp(name, "Ip6ReasmFails") == 0) Ip6ReasmFails = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6FragOKs && strcmp(name, "Ip6FragOKs") == 0) Ip6FragOKs = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6FragFails && strcmp(name, "Ip6FragFails") == 0) Ip6FragFails = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6FragCreates && strcmp(name, "Ip6FragCreates") == 0) Ip6FragCreates = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InMcastPkts && strcmp(name, "Ip6InMcastPkts") == 0) Ip6InMcastPkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutMcastPkts && strcmp(name, "Ip6OutMcastPkts") == 0) Ip6OutMcastPkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InOctets && strcmp(name, "Ip6InOctets") == 0) Ip6InOctets = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutOctets && strcmp(name, "Ip6OutOctets") == 0) Ip6OutOctets = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InMcastOctets && strcmp(name, "Ip6InMcastOctets") == 0) Ip6InMcastOctets = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutMcastOctets && strcmp(name, "Ip6OutMcastOctets") == 0) Ip6OutMcastOctets = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InBcastOctets && strcmp(name, "Ip6InBcastOctets") == 0) Ip6InBcastOctets = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6OutBcastOctets && strcmp(name, "Ip6OutBcastOctets") == 0) Ip6OutBcastOctets = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InNoECTPkts && strcmp(name, "Ip6InNoECTPkts") == 0) Ip6InNoECTPkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InECT1Pkts && strcmp(name, "Ip6InECT1Pkts") == 0) Ip6InECT1Pkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InECT0Pkts && strcmp(name, "Ip6InECT0Pkts") == 0) Ip6InECT0Pkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Ip6InCEPkts && strcmp(name, "Ip6InCEPkts") == 0) Ip6InCEPkts = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InMsgs && strcmp(name, "Icmp6InMsgs") == 0) Icmp6InMsgs = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InErrors && strcmp(name, "Icmp6InErrors") == 0) Icmp6InErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutMsgs && strcmp(name, "Icmp6OutMsgs") == 0) Icmp6OutMsgs = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutErrors && strcmp(name, "Icmp6OutErrors") == 0) Icmp6OutErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InCsumErrors && strcmp(name, "Icmp6InCsumErrors") == 0) Icmp6InCsumErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InDestUnreachs && strcmp(name, "Icmp6InDestUnreachs") == 0) Icmp6InDestUnreachs = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InPktTooBigs && strcmp(name, "Icmp6InPktTooBigs") == 0) Icmp6InPktTooBigs = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InTimeExcds && strcmp(name, "Icmp6InTimeExcds") == 0) Icmp6InTimeExcds = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InParmProblems && strcmp(name, "Icmp6InParmProblems") == 0) Icmp6InParmProblems = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InEchos && strcmp(name, "Icmp6InEchos") == 0) Icmp6InEchos = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InEchoReplies && strcmp(name, "Icmp6InEchoReplies") == 0) Icmp6InEchoReplies = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InGroupMembQueries && strcmp(name, "Icmp6InGroupMembQueries") == 0) Icmp6InGroupMembQueries = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InGroupMembResponses && strcmp(name, "Icmp6InGroupMembResponses") == 0) Icmp6InGroupMembResponses = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InGroupMembReductions && strcmp(name, "Icmp6InGroupMembReductions") == 0) Icmp6InGroupMembReductions = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InRouterSolicits && strcmp(name, "Icmp6InRouterSolicits") == 0) Icmp6InRouterSolicits = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InRouterAdvertisements && strcmp(name, "Icmp6InRouterAdvertisements") == 0) Icmp6InRouterAdvertisements = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InNeighborSolicits && strcmp(name, "Icmp6InNeighborSolicits") == 0) Icmp6InNeighborSolicits = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InNeighborAdvertisements && strcmp(name, "Icmp6InNeighborAdvertisements") == 0) Icmp6InNeighborAdvertisements = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InRedirects && strcmp(name, "Icmp6InRedirects") == 0) Icmp6InRedirects = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InMLDv2Reports && strcmp(name, "Icmp6InMLDv2Reports") == 0) Icmp6InMLDv2Reports = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutDestUnreachs && strcmp(name, "Icmp6OutDestUnreachs") == 0) Icmp6OutDestUnreachs = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutPktTooBigs && strcmp(name, "Icmp6OutPktTooBigs") == 0) Icmp6OutPktTooBigs = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutTimeExcds && strcmp(name, "Icmp6OutTimeExcds") == 0) Icmp6OutTimeExcds = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutParmProblems && strcmp(name, "Icmp6OutParmProblems") == 0) Icmp6OutParmProblems = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutEchos && strcmp(name, "Icmp6OutEchos") == 0) Icmp6OutEchos = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutEchoReplies && strcmp(name, "Icmp6OutEchoReplies") == 0) Icmp6OutEchoReplies = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutGroupMembQueries && strcmp(name, "Icmp6OutGroupMembQueries") == 0) Icmp6OutGroupMembQueries = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutGroupMembResponses && strcmp(name, "Icmp6OutGroupMembResponses") == 0) Icmp6OutGroupMembResponses = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutGroupMembReductions && strcmp(name, "Icmp6OutGroupMembReductions") == 0) Icmp6OutGroupMembReductions = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutRouterSolicits && strcmp(name, "Icmp6OutRouterSolicits") == 0) Icmp6OutRouterSolicits = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutRouterAdvertisements && strcmp(name, "Icmp6OutRouterAdvertisements") == 0) Icmp6OutRouterAdvertisements = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutNeighborSolicits && strcmp(name, "Icmp6OutNeighborSolicits") == 0) Icmp6OutNeighborSolicits = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutNeighborAdvertisements && strcmp(name, "Icmp6OutNeighborAdvertisements") == 0) Icmp6OutNeighborAdvertisements = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutRedirects && strcmp(name, "Icmp6OutRedirects") == 0) Icmp6OutRedirects = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutMLDv2Reports && strcmp(name, "Icmp6OutMLDv2Reports") == 0) Icmp6OutMLDv2Reports = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InType1 && strcmp(name, "Icmp6InType1") == 0) Icmp6InType1 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InType128 && strcmp(name, "Icmp6InType128") == 0) Icmp6InType128 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InType129 && strcmp(name, "Icmp6InType129") == 0) Icmp6InType129 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6InType136 && strcmp(name, "Icmp6InType136") == 0) Icmp6InType136 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutType1 && strcmp(name, "Icmp6OutType1") == 0) Icmp6OutType1 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutType128 && strcmp(name, "Icmp6OutType128") == 0) Icmp6OutType128 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutType129 && strcmp(name, "Icmp6OutType129") == 0) Icmp6OutType129 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutType133 && strcmp(name, "Icmp6OutType133") == 0) Icmp6OutType133 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutType135 && strcmp(name, "Icmp6OutType135") == 0) Icmp6OutType135 = strtoull(value, NULL, 10);
-               else if(hash == hash_Icmp6OutType143 && strcmp(name, "Icmp6OutType143") == 0) Icmp6OutType143 = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6InDatagrams && strcmp(name, "Udp6InDatagrams") == 0) Udp6InDatagrams = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6NoPorts && strcmp(name, "Udp6NoPorts") == 0) Udp6NoPorts = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6InErrors && strcmp(name, "Udp6InErrors") == 0) Udp6InErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6OutDatagrams && strcmp(name, "Udp6OutDatagrams") == 0) Udp6OutDatagrams = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6RcvbufErrors && strcmp(name, "Udp6RcvbufErrors") == 0) Udp6RcvbufErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6SndbufErrors && strcmp(name, "Udp6SndbufErrors") == 0) Udp6SndbufErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6InCsumErrors && strcmp(name, "Udp6InCsumErrors") == 0) Udp6InCsumErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_Udp6IgnoredMulti && strcmp(name, "Udp6IgnoredMulti") == 0) Udp6IgnoredMulti = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6InDatagrams && strcmp(name, "UdpLite6InDatagrams") == 0) UdpLite6InDatagrams = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6NoPorts && strcmp(name, "UdpLite6NoPorts") == 0) UdpLite6NoPorts = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6InErrors && strcmp(name, "UdpLite6InErrors") == 0) UdpLite6InErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6OutDatagrams && strcmp(name, "UdpLite6OutDatagrams") == 0) UdpLite6OutDatagrams = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6RcvbufErrors && strcmp(name, "UdpLite6RcvbufErrors") == 0) UdpLite6RcvbufErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6SndbufErrors && strcmp(name, "UdpLite6SndbufErrors") == 0) UdpLite6SndbufErrors = strtoull(value, NULL, 10);
-               else if(hash == hash_UdpLite6InCsumErrors && strcmp(name, "UdpLite6InCsumErrors") == 0) UdpLite6InCsumErrors = strtoull(value, NULL, 10);
-       }
-
-       RRDSET *st;
-
-       // --------------------------------------------------------------------
-
-       if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) {
-               do_bandwidth = CONFIG_ONDEMAND_YES;
-               st = rrdset_find("system.ipv6");
-               if(!st) {
-                       st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
-
-                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Ip6OutOctets);
-               rrddim_set(st, "received", Ip6InOctets);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_ip_packets == CONFIG_ONDEMAND_YES || (do_ip_packets == CONFIG_ONDEMAND_ONDEMAND && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) {
-               do_ip_packets = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".packets");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Ip6OutRequests);
-               rrddim_set(st, "received", Ip6InReceives);
-               rrddim_set(st, "forwarded", Ip6InDelivers);
-               rrddim_set(st, "delivers", Ip6OutForwDatagrams);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_ip_fragsout == CONFIG_ONDEMAND_YES || (do_ip_fragsout == CONFIG_ONDEMAND_ONDEMAND && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) {
-               do_ip_fragsout = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsout");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "ok", Ip6FragOKs);
-               rrddim_set(st, "failed", Ip6FragFails);
-               rrddim_set(st, "all", Ip6FragCreates);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_ip_fragsin == CONFIG_ONDEMAND_YES || (do_ip_fragsin == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Ip6ReasmOKs
-                       || Ip6ReasmFails
-                       || Ip6ReasmTimeout
-                       || Ip6ReasmReqds
-                       ))) {
-               do_ip_fragsin = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "ok", Ip6ReasmOKs);
-               rrddim_set(st, "failed", Ip6ReasmFails);
-               rrddim_set(st, "timeout", Ip6ReasmTimeout);
-               rrddim_set(st, "all", Ip6ReasmReqds);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_ip_errors == CONFIG_ONDEMAND_YES || (do_ip_errors == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Ip6InDiscards
-                       || Ip6OutDiscards
-                       || Ip6InHdrErrors
-                       || Ip6InAddrErrors
-                       || Ip6InUnknownProtos
-                       || Ip6InTooBigErrors
-                       || Ip6InTruncatedPkts
-                       || Ip6InNoRoutes
-               ))) {
-               do_ip_errors = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
-                       rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
-
-                       rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InDiscards", Ip6InDiscards);
-               rrddim_set(st, "OutDiscards", Ip6OutDiscards);
-
-               rrddim_set(st, "InHdrErrors", Ip6InHdrErrors);
-               rrddim_set(st, "InAddrErrors", Ip6InAddrErrors);
-               rrddim_set(st, "InUnknownProtos", Ip6InUnknownProtos);
-               rrddim_set(st, "InTooBigErrors", Ip6InTooBigErrors);
-               rrddim_set(st, "InTruncatedPkts", Ip6InTruncatedPkts);
-               rrddim_set(st, "InNoRoutes", Ip6InNoRoutes);
-
-               rrddim_set(st, "OutNoRoutes", Ip6OutNoRoutes);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_udp_packets == CONFIG_ONDEMAND_YES || (do_udp_packets == CONFIG_ONDEMAND_ONDEMAND && (Udp6InDatagrams || Udp6OutDatagrams))) {
-               do_udp_packets = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udppackets");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "received", Udp6InDatagrams);
-               rrddim_set(st, "sent", Udp6OutDatagrams);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_udp_errors == CONFIG_ONDEMAND_YES || (do_udp_errors == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Udp6InErrors
-                       || Udp6NoPorts
-                       || Udp6RcvbufErrors
-                       || Udp6SndbufErrors
-                       || Udp6InCsumErrors
-                       || Udp6IgnoredMulti
-               ))) {
-               do_udp_errors = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InErrors", Udp6InErrors);
-               rrddim_set(st, "NoPorts", Udp6NoPorts);
-               rrddim_set(st, "RcvbufErrors", Udp6RcvbufErrors);
-               rrddim_set(st, "SndbufErrors", Udp6SndbufErrors);
-               rrddim_set(st, "InCsumErrors", Udp6InCsumErrors);
-               rrddim_set(st, "IgnoredMulti", Udp6IgnoredMulti);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_udplite_packets == CONFIG_ONDEMAND_YES || (do_udplite_packets == CONFIG_ONDEMAND_ONDEMAND && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) {
-               do_udplite_packets = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udplitepackets");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "received", UdpLite6InDatagrams);
-               rrddim_set(st, "sent", UdpLite6OutDatagrams);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_udplite_errors == CONFIG_ONDEMAND_YES || (do_udplite_errors == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       UdpLite6InErrors
-                       || UdpLite6NoPorts
-                       || UdpLite6RcvbufErrors
-                       || UdpLite6SndbufErrors
-                       || Udp6InCsumErrors
-                       || UdpLite6InCsumErrors
-               ))) {
-               do_udplite_errors = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL, "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InErrors", UdpLite6InErrors);
-               rrddim_set(st, "NoPorts", UdpLite6NoPorts);
-               rrddim_set(st, "RcvbufErrors", UdpLite6RcvbufErrors);
-               rrddim_set(st, "SndbufErrors", UdpLite6SndbufErrors);
-               rrddim_set(st, "InCsumErrors", UdpLite6InCsumErrors);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastOctets || Ip6InMcastOctets))) {
-               do_mcast = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcast");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL, "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Ip6OutMcastOctets);
-               rrddim_set(st, "received", Ip6InMcastOctets);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutBcastOctets || Ip6InBcastOctets))) {
-               do_bcast = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".bcast");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL, "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Ip6OutBcastOctets);
-               rrddim_set(st, "received", Ip6InBcastOctets);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastPkts || Ip6InMcastPkts))) {
-               do_mcast_p = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcastpkts");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL, "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Ip6OutMcastPkts);
-               rrddim_set(st, "received", Ip6InMcastPkts);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp == CONFIG_ONDEMAND_YES || (do_icmp == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMsgs || Icmp6OutMsgs))) {
-               do_icmp = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmp");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Icmp6InMsgs);
-               rrddim_set(st, "received", Icmp6OutMsgs);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_redir == CONFIG_ONDEMAND_YES || (do_icmp_redir == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InRedirects || Icmp6OutRedirects))) {
-               do_icmp_redir = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpredir");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Icmp6InRedirects);
-               rrddim_set(st, "received", Icmp6OutRedirects);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_errors == CONFIG_ONDEMAND_YES || (do_icmp_errors == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Icmp6InErrors
-                       || Icmp6OutErrors
-                       || Icmp6InCsumErrors
-                       || Icmp6InDestUnreachs
-                       || Icmp6InPktTooBigs
-                       || Icmp6InTimeExcds
-                       || Icmp6InParmProblems
-                       || Icmp6OutDestUnreachs
-                       || Icmp6OutPktTooBigs
-                       || Icmp6OutTimeExcds
-                       || Icmp6OutParmProblems
-               ))) {
-               do_icmp_errors = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
-
-                       rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InErrors", Icmp6InErrors);
-               rrddim_set(st, "OutErrors", Icmp6OutErrors);
-               rrddim_set(st, "InCsumErrors", Icmp6InCsumErrors);
-               rrddim_set(st, "InDestUnreachs", Icmp6InDestUnreachs);
-               rrddim_set(st, "InPktTooBigs", Icmp6InPktTooBigs);
-               rrddim_set(st, "InTimeExcds", Icmp6InTimeExcds);
-               rrddim_set(st, "InParmProblems", Icmp6InParmProblems);
-               rrddim_set(st, "OutDestUnreachs", Icmp6OutDestUnreachs);
-               rrddim_set(st, "OutPktTooBigs", Icmp6OutPktTooBigs);
-               rrddim_set(st, "OutTimeExcds", Icmp6OutTimeExcds);
-               rrddim_set(st, "OutParmProblems", Icmp6OutParmProblems);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_echos == CONFIG_ONDEMAND_YES || (do_icmp_echos == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Icmp6InEchos
-                       || Icmp6OutEchos
-                       || Icmp6InEchoReplies
-                       || Icmp6OutEchoReplies
-               ))) {
-               do_icmp_echos = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InEchos", Icmp6InEchos);
-               rrddim_set(st, "OutEchos", Icmp6OutEchos);
-               rrddim_set(st, "InEchoReplies", Icmp6InEchoReplies);
-               rrddim_set(st, "OutEchoReplies", Icmp6OutEchoReplies);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_groupmemb == CONFIG_ONDEMAND_YES || (do_icmp_groupmemb == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Icmp6InGroupMembQueries
-                       || Icmp6OutGroupMembQueries
-                       || Icmp6InGroupMembResponses
-                       || Icmp6OutGroupMembResponses
-                       || Icmp6InGroupMembReductions
-                       || Icmp6OutGroupMembReductions
-               ))) {
-               do_icmp_groupmemb = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL, "IPv6 ICMP Group Membership", "messages/s", 10300, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InQueries", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutQueries", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InResponses", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutResponses", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InReductions", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutReductions", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InQueries", Icmp6InGroupMembQueries);
-               rrddim_set(st, "OutQueries", Icmp6OutGroupMembQueries);
-               rrddim_set(st, "InResponses", Icmp6InGroupMembResponses);
-               rrddim_set(st, "OutResponses", Icmp6OutGroupMembResponses);
-               rrddim_set(st, "InReductions", Icmp6InGroupMembReductions);
-               rrddim_set(st, "OutReductions", Icmp6OutGroupMembReductions);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_router == CONFIG_ONDEMAND_YES || (do_icmp_router == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Icmp6InRouterSolicits
-                       || Icmp6OutRouterSolicits
-                       || Icmp6InRouterAdvertisements
-                       || Icmp6OutRouterAdvertisements
-               ))) {
-               do_icmp_router = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InSolicits", Icmp6InRouterSolicits);
-               rrddim_set(st, "OutSolicits", Icmp6OutRouterSolicits);
-               rrddim_set(st, "InAdvertisements", Icmp6InRouterAdvertisements);
-               rrddim_set(st, "OutAdvertisements", Icmp6OutRouterAdvertisements);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_neighbor == CONFIG_ONDEMAND_YES || (do_icmp_neighbor == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Icmp6InNeighborSolicits
-                       || Icmp6OutNeighborSolicits
-                       || Icmp6InNeighborAdvertisements
-                       || Icmp6OutNeighborAdvertisements
-               ))) {
-               do_icmp_neighbor = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InSolicits", Icmp6InNeighborSolicits);
-               rrddim_set(st, "OutSolicits", Icmp6OutNeighborSolicits);
-               rrddim_set(st, "InAdvertisements", Icmp6InNeighborAdvertisements);
-               rrddim_set(st, "OutAdvertisements", Icmp6OutNeighborAdvertisements);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_mldv2 == CONFIG_ONDEMAND_YES || (do_icmp_mldv2 == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) {
-               do_icmp_mldv2 = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpmldv2");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "sent", Icmp6InMLDv2Reports);
-               rrddim_set(st, "received", Icmp6OutMLDv2Reports);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_icmp_types == CONFIG_ONDEMAND_YES || (do_icmp_types == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Icmp6InType1
-                       || Icmp6InType128
-                       || Icmp6InType129
-                       || Icmp6InType136
-                       || Icmp6OutType1
-                       || Icmp6OutType128
-                       || Icmp6OutType129
-                       || Icmp6OutType133
-                       || Icmp6OutType135
-                       || Icmp6OutType143
-               ))) {
-               do_icmp_types = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InType1", Icmp6InType1);
-               rrddim_set(st, "InType128", Icmp6InType128);
-               rrddim_set(st, "InType129", Icmp6InType129);
-               rrddim_set(st, "InType136", Icmp6InType136);
-               rrddim_set(st, "OutType1", Icmp6OutType1);
-               rrddim_set(st, "OutType128", Icmp6OutType128);
-               rrddim_set(st, "OutType129", Icmp6OutType129);
-               rrddim_set(st, "OutType133", Icmp6OutType133);
-               rrddim_set(st, "OutType135", Icmp6OutType135);
-               rrddim_set(st, "OutType143", Icmp6OutType143);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_ect == CONFIG_ONDEMAND_YES || (do_ect == CONFIG_ONDEMAND_ONDEMAND
-               && (
-                       Ip6InNoECTPkts
-                       || Ip6InECT1Pkts
-                       || Ip6InECT0Pkts
-                       || Ip6InCEPkts
-               ))) {
-               do_ect = CONFIG_ONDEMAND_YES;
-               st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "InCEPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "InNoECTPkts", Ip6InNoECTPkts);
-               rrddim_set(st, "InECT1Pkts", Ip6InECT1Pkts);
-               rrddim_set(st, "InECT0Pkts", Ip6InECT0Pkts);
-               rrddim_set(st, "InCEPkts", Ip6InCEPkts);
-               rrdset_done(st);
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+    static int gen_hashes = -1;
+
+    static int do_ip_packets = -1, do_ip_fragsout = -1, do_ip_fragsin = -1, do_ip_errors = -1,
+        do_udplite_packets = -1, do_udplite_errors = -1,
+        do_udp_packets = -1, do_udp_errors = -1,
+        do_bandwidth = -1, do_mcast = -1, do_bcast = -1, do_mcast_p = -1,
+        do_icmp = -1, do_icmp_redir = -1, do_icmp_errors = -1, do_icmp_echos = -1, do_icmp_groupmemb = -1,
+        do_icmp_router = -1, do_icmp_neighbor = -1, do_icmp_mldv2 = -1, do_icmp_types = -1, do_ect = -1;
+
+    static uint32_t hash_Ip6InReceives = -1;
+
+    static uint32_t hash_Ip6InHdrErrors = -1;
+    static uint32_t hash_Ip6InTooBigErrors = -1;
+    static uint32_t hash_Ip6InNoRoutes = -1;
+    static uint32_t hash_Ip6InAddrErrors = -1;
+    static uint32_t hash_Ip6InUnknownProtos = -1;
+    static uint32_t hash_Ip6InTruncatedPkts = -1;
+    static uint32_t hash_Ip6InDiscards = -1;
+    static uint32_t hash_Ip6InDelivers = -1;
+
+    static uint32_t hash_Ip6OutForwDatagrams = -1;
+    static uint32_t hash_Ip6OutRequests = -1;
+    static uint32_t hash_Ip6OutDiscards = -1;
+    static uint32_t hash_Ip6OutNoRoutes = -1;
+
+    static uint32_t hash_Ip6ReasmTimeout = -1;
+    static uint32_t hash_Ip6ReasmReqds = -1;
+    static uint32_t hash_Ip6ReasmOKs = -1;
+    static uint32_t hash_Ip6ReasmFails = -1;
+
+    static uint32_t hash_Ip6FragOKs = -1;
+    static uint32_t hash_Ip6FragFails = -1;
+    static uint32_t hash_Ip6FragCreates = -1;
+
+    static uint32_t hash_Ip6InMcastPkts = -1;
+    static uint32_t hash_Ip6OutMcastPkts = -1;
+
+    static uint32_t hash_Ip6InOctets = -1;
+    static uint32_t hash_Ip6OutOctets = -1;
+
+    static uint32_t hash_Ip6InMcastOctets = -1;
+    static uint32_t hash_Ip6OutMcastOctets = -1;
+    static uint32_t hash_Ip6InBcastOctets = -1;
+    static uint32_t hash_Ip6OutBcastOctets = -1;
+
+    static uint32_t hash_Ip6InNoECTPkts = -1;
+    static uint32_t hash_Ip6InECT1Pkts = -1;
+    static uint32_t hash_Ip6InECT0Pkts = -1;
+    static uint32_t hash_Ip6InCEPkts = -1;
+
+    static uint32_t hash_Icmp6InMsgs = -1;
+    static uint32_t hash_Icmp6InErrors = -1;
+    static uint32_t hash_Icmp6OutMsgs = -1;
+    static uint32_t hash_Icmp6OutErrors = -1;
+    static uint32_t hash_Icmp6InCsumErrors = -1;
+    static uint32_t hash_Icmp6InDestUnreachs = -1;
+    static uint32_t hash_Icmp6InPktTooBigs = -1;
+    static uint32_t hash_Icmp6InTimeExcds = -1;
+    static uint32_t hash_Icmp6InParmProblems = -1;
+    static uint32_t hash_Icmp6InEchos = -1;
+    static uint32_t hash_Icmp6InEchoReplies = -1;
+    static uint32_t hash_Icmp6InGroupMembQueries = -1;
+    static uint32_t hash_Icmp6InGroupMembResponses = -1;
+    static uint32_t hash_Icmp6InGroupMembReductions = -1;
+    static uint32_t hash_Icmp6InRouterSolicits = -1;
+    static uint32_t hash_Icmp6InRouterAdvertisements = -1;
+    static uint32_t hash_Icmp6InNeighborSolicits = -1;
+    static uint32_t hash_Icmp6InNeighborAdvertisements = -1;
+    static uint32_t hash_Icmp6InRedirects = -1;
+    static uint32_t hash_Icmp6InMLDv2Reports = -1;
+    static uint32_t hash_Icmp6OutDestUnreachs = -1;
+    static uint32_t hash_Icmp6OutPktTooBigs = -1;
+    static uint32_t hash_Icmp6OutTimeExcds = -1;
+    static uint32_t hash_Icmp6OutParmProblems = -1;
+    static uint32_t hash_Icmp6OutEchos = -1;
+    static uint32_t hash_Icmp6OutEchoReplies = -1;
+    static uint32_t hash_Icmp6OutGroupMembQueries = -1;
+    static uint32_t hash_Icmp6OutGroupMembResponses = -1;
+    static uint32_t hash_Icmp6OutGroupMembReductions = -1;
+    static uint32_t hash_Icmp6OutRouterSolicits = -1;
+    static uint32_t hash_Icmp6OutRouterAdvertisements = -1;
+    static uint32_t hash_Icmp6OutNeighborSolicits = -1;
+    static uint32_t hash_Icmp6OutNeighborAdvertisements = -1;
+    static uint32_t hash_Icmp6OutRedirects = -1;
+    static uint32_t hash_Icmp6OutMLDv2Reports = -1;
+    static uint32_t hash_Icmp6InType1 = -1;
+    static uint32_t hash_Icmp6InType128 = -1;
+    static uint32_t hash_Icmp6InType129 = -1;
+    static uint32_t hash_Icmp6InType136 = -1;
+    static uint32_t hash_Icmp6OutType1 = -1;
+    static uint32_t hash_Icmp6OutType128 = -1;
+    static uint32_t hash_Icmp6OutType129 = -1;
+    static uint32_t hash_Icmp6OutType133 = -1;
+    static uint32_t hash_Icmp6OutType135 = -1;
+    static uint32_t hash_Icmp6OutType143 = -1;
+
+    static uint32_t hash_Udp6InDatagrams = -1;
+    static uint32_t hash_Udp6NoPorts = -1;
+    static uint32_t hash_Udp6InErrors = -1;
+    static uint32_t hash_Udp6OutDatagrams = -1;
+    static uint32_t hash_Udp6RcvbufErrors = -1;
+    static uint32_t hash_Udp6SndbufErrors = -1;
+    static uint32_t hash_Udp6InCsumErrors = -1;
+    static uint32_t hash_Udp6IgnoredMulti = -1;
+
+    static uint32_t hash_UdpLite6InDatagrams = -1;
+    static uint32_t hash_UdpLite6NoPorts = -1;
+    static uint32_t hash_UdpLite6InErrors = -1;
+    static uint32_t hash_UdpLite6OutDatagrams = -1;
+    static uint32_t hash_UdpLite6RcvbufErrors = -1;
+    static uint32_t hash_UdpLite6SndbufErrors = -1;
+    static uint32_t hash_UdpLite6InCsumErrors = -1;
+
+    if(gen_hashes != 1) {
+        gen_hashes = 1;
+        hash_Ip6InReceives = simple_hash("Ip6InReceives");
+        hash_Ip6InHdrErrors = simple_hash("Ip6InHdrErrors");
+        hash_Ip6InTooBigErrors = simple_hash("Ip6InTooBigErrors");
+        hash_Ip6InNoRoutes = simple_hash("Ip6InNoRoutes");
+        hash_Ip6InAddrErrors = simple_hash("Ip6InAddrErrors");
+        hash_Ip6InUnknownProtos = simple_hash("Ip6InUnknownProtos");
+        hash_Ip6InTruncatedPkts = simple_hash("Ip6InTruncatedPkts");
+        hash_Ip6InDiscards = simple_hash("Ip6InDiscards");
+        hash_Ip6InDelivers = simple_hash("Ip6InDelivers");
+        hash_Ip6OutForwDatagrams = simple_hash("Ip6OutForwDatagrams");
+        hash_Ip6OutRequests = simple_hash("Ip6OutRequests");
+        hash_Ip6OutDiscards = simple_hash("Ip6OutDiscards");
+        hash_Ip6OutNoRoutes = simple_hash("Ip6OutNoRoutes");
+        hash_Ip6ReasmTimeout = simple_hash("Ip6ReasmTimeout");
+        hash_Ip6ReasmReqds = simple_hash("Ip6ReasmReqds");
+        hash_Ip6ReasmOKs = simple_hash("Ip6ReasmOKs");
+        hash_Ip6ReasmFails = simple_hash("Ip6ReasmFails");
+        hash_Ip6FragOKs = simple_hash("Ip6FragOKs");
+        hash_Ip6FragFails = simple_hash("Ip6FragFails");
+        hash_Ip6FragCreates = simple_hash("Ip6FragCreates");
+        hash_Ip6InMcastPkts = simple_hash("Ip6InMcastPkts");
+        hash_Ip6OutMcastPkts = simple_hash("Ip6OutMcastPkts");
+        hash_Ip6InOctets = simple_hash("Ip6InOctets");
+        hash_Ip6OutOctets = simple_hash("Ip6OutOctets");
+        hash_Ip6InMcastOctets = simple_hash("Ip6InMcastOctets");
+        hash_Ip6OutMcastOctets = simple_hash("Ip6OutMcastOctets");
+        hash_Ip6InBcastOctets = simple_hash("Ip6InBcastOctets");
+        hash_Ip6OutBcastOctets = simple_hash("Ip6OutBcastOctets");
+        hash_Ip6InNoECTPkts = simple_hash("Ip6InNoECTPkts");
+        hash_Ip6InECT1Pkts = simple_hash("Ip6InECT1Pkts");
+        hash_Ip6InECT0Pkts = simple_hash("Ip6InECT0Pkts");
+        hash_Ip6InCEPkts = simple_hash("Ip6InCEPkts");
+        hash_Icmp6InMsgs = simple_hash("Icmp6InMsgs");
+        hash_Icmp6InErrors = simple_hash("Icmp6InErrors");
+        hash_Icmp6OutMsgs = simple_hash("Icmp6OutMsgs");
+        hash_Icmp6OutErrors = simple_hash("Icmp6OutErrors");
+        hash_Icmp6InCsumErrors = simple_hash("Icmp6InCsumErrors");
+        hash_Icmp6InDestUnreachs = simple_hash("Icmp6InDestUnreachs");
+        hash_Icmp6InPktTooBigs = simple_hash("Icmp6InPktTooBigs");
+        hash_Icmp6InTimeExcds = simple_hash("Icmp6InTimeExcds");
+        hash_Icmp6InParmProblems = simple_hash("Icmp6InParmProblems");
+        hash_Icmp6InEchos = simple_hash("Icmp6InEchos");
+        hash_Icmp6InEchoReplies = simple_hash("Icmp6InEchoReplies");
+        hash_Icmp6InGroupMembQueries = simple_hash("Icmp6InGroupMembQueries");
+        hash_Icmp6InGroupMembResponses = simple_hash("Icmp6InGroupMembResponses");
+        hash_Icmp6InGroupMembReductions = simple_hash("Icmp6InGroupMembReductions");
+        hash_Icmp6InRouterSolicits = simple_hash("Icmp6InRouterSolicits");
+        hash_Icmp6InRouterAdvertisements = simple_hash("Icmp6InRouterAdvertisements");
+        hash_Icmp6InNeighborSolicits = simple_hash("Icmp6InNeighborSolicits");
+        hash_Icmp6InNeighborAdvertisements = simple_hash("Icmp6InNeighborAdvertisements");
+        hash_Icmp6InRedirects = simple_hash("Icmp6InRedirects");
+        hash_Icmp6InMLDv2Reports = simple_hash("Icmp6InMLDv2Reports");
+        hash_Icmp6OutDestUnreachs = simple_hash("Icmp6OutDestUnreachs");
+        hash_Icmp6OutPktTooBigs = simple_hash("Icmp6OutPktTooBigs");
+        hash_Icmp6OutTimeExcds = simple_hash("Icmp6OutTimeExcds");
+        hash_Icmp6OutParmProblems = simple_hash("Icmp6OutParmProblems");
+        hash_Icmp6OutEchos = simple_hash("Icmp6OutEchos");
+        hash_Icmp6OutEchoReplies = simple_hash("Icmp6OutEchoReplies");
+        hash_Icmp6OutGroupMembQueries = simple_hash("Icmp6OutGroupMembQueries");
+        hash_Icmp6OutGroupMembResponses = simple_hash("Icmp6OutGroupMembResponses");
+        hash_Icmp6OutGroupMembReductions = simple_hash("Icmp6OutGroupMembReductions");
+        hash_Icmp6OutRouterSolicits = simple_hash("Icmp6OutRouterSolicits");
+        hash_Icmp6OutRouterAdvertisements = simple_hash("Icmp6OutRouterAdvertisements");
+        hash_Icmp6OutNeighborSolicits = simple_hash("Icmp6OutNeighborSolicits");
+        hash_Icmp6OutNeighborAdvertisements = simple_hash("Icmp6OutNeighborAdvertisements");
+        hash_Icmp6OutRedirects = simple_hash("Icmp6OutRedirects");
+        hash_Icmp6OutMLDv2Reports = simple_hash("Icmp6OutMLDv2Reports");
+        hash_Icmp6InType1 = simple_hash("Icmp6InType1");
+        hash_Icmp6InType128 = simple_hash("Icmp6InType128");
+        hash_Icmp6InType129 = simple_hash("Icmp6InType129");
+        hash_Icmp6InType136 = simple_hash("Icmp6InType136");
+        hash_Icmp6OutType1 = simple_hash("Icmp6OutType1");
+        hash_Icmp6OutType128 = simple_hash("Icmp6OutType128");
+        hash_Icmp6OutType129 = simple_hash("Icmp6OutType129");
+        hash_Icmp6OutType133 = simple_hash("Icmp6OutType133");
+        hash_Icmp6OutType135 = simple_hash("Icmp6OutType135");
+        hash_Icmp6OutType143 = simple_hash("Icmp6OutType143");
+        hash_Udp6InDatagrams = simple_hash("Udp6InDatagrams");
+        hash_Udp6NoPorts = simple_hash("Udp6NoPorts");
+        hash_Udp6InErrors = simple_hash("Udp6InErrors");
+        hash_Udp6OutDatagrams = simple_hash("Udp6OutDatagrams");
+        hash_Udp6RcvbufErrors = simple_hash("Udp6RcvbufErrors");
+        hash_Udp6SndbufErrors = simple_hash("Udp6SndbufErrors");
+        hash_Udp6InCsumErrors = simple_hash("Udp6InCsumErrors");
+        hash_Udp6IgnoredMulti = simple_hash("Udp6IgnoredMulti");
+        hash_UdpLite6InDatagrams = simple_hash("UdpLite6InDatagrams");
+        hash_UdpLite6NoPorts = simple_hash("UdpLite6NoPorts");
+        hash_UdpLite6InErrors = simple_hash("UdpLite6InErrors");
+        hash_UdpLite6OutDatagrams = simple_hash("UdpLite6OutDatagrams");
+        hash_UdpLite6RcvbufErrors = simple_hash("UdpLite6RcvbufErrors");
+        hash_UdpLite6SndbufErrors = simple_hash("UdpLite6SndbufErrors");
+        hash_UdpLite6InCsumErrors = simple_hash("UdpLite6InCsumErrors");
+    }
+
+    if(do_ip_packets == -1)         do_ip_packets       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 packets", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_ip_fragsout == -1)        do_ip_fragsout      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments sent", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_ip_fragsin == -1)         do_ip_fragsin       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 fragments assembly", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_ip_errors == -1)          do_ip_errors        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 errors", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_udp_packets == -1)        do_udp_packets      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP packets", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_udp_errors == -1)         do_udp_errors       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDP errors", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_udplite_packets == -1)    do_udplite_packets  = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite packets", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_udplite_errors == -1)     do_udplite_errors   = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ipv6 UDPlite errors", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_bandwidth == -1)          do_bandwidth        = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_mcast == -1)              do_mcast            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_bcast == -1)              do_bcast            = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "broadcast bandwidth", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_mcast_p == -1)            do_mcast_p          = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "multicast packets", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp == -1)               do_icmp             = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_redir == -1)         do_icmp_redir       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp redirects", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_errors == -1)        do_icmp_errors      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp errors", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_echos == -1)         do_icmp_echos       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp echos", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_groupmemb == -1)     do_icmp_groupmemb   = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp group membership", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_router == -1)        do_icmp_router      = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp router", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_neighbor == -1)      do_icmp_neighbor    = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp neighbor", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_mldv2 == -1)         do_icmp_mldv2       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp mldv2", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_icmp_types == -1)         do_icmp_types       = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "icmp types", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_ect == -1)                do_ect              = config_get_boolean_ondemand("plugin:proc:/proc/net/snmp6", "ect", CONFIG_ONDEMAND_ONDEMAND);
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/snmp6");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/snmp6", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    unsigned long long Ip6InReceives = 0ULL;
+    unsigned long long Ip6InHdrErrors = 0ULL;
+    unsigned long long Ip6InTooBigErrors = 0ULL;
+    unsigned long long Ip6InNoRoutes = 0ULL;
+    unsigned long long Ip6InAddrErrors = 0ULL;
+    unsigned long long Ip6InUnknownProtos = 0ULL;
+    unsigned long long Ip6InTruncatedPkts = 0ULL;
+    unsigned long long Ip6InDiscards = 0ULL;
+    unsigned long long Ip6InDelivers = 0ULL;
+    unsigned long long Ip6OutForwDatagrams = 0ULL;
+    unsigned long long Ip6OutRequests = 0ULL;
+    unsigned long long Ip6OutDiscards = 0ULL;
+    unsigned long long Ip6OutNoRoutes = 0ULL;
+    unsigned long long Ip6ReasmTimeout = 0ULL;
+    unsigned long long Ip6ReasmReqds = 0ULL;
+    unsigned long long Ip6ReasmOKs = 0ULL;
+    unsigned long long Ip6ReasmFails = 0ULL;
+    unsigned long long Ip6FragOKs = 0ULL;
+    unsigned long long Ip6FragFails = 0ULL;
+    unsigned long long Ip6FragCreates = 0ULL;
+    unsigned long long Ip6InMcastPkts = 0ULL;
+    unsigned long long Ip6OutMcastPkts = 0ULL;
+    unsigned long long Ip6InOctets = 0ULL;
+    unsigned long long Ip6OutOctets = 0ULL;
+    unsigned long long Ip6InMcastOctets = 0ULL;
+    unsigned long long Ip6OutMcastOctets = 0ULL;
+    unsigned long long Ip6InBcastOctets = 0ULL;
+    unsigned long long Ip6OutBcastOctets = 0ULL;
+    unsigned long long Ip6InNoECTPkts = 0ULL;
+    unsigned long long Ip6InECT1Pkts = 0ULL;
+    unsigned long long Ip6InECT0Pkts = 0ULL;
+    unsigned long long Ip6InCEPkts = 0ULL;
+    unsigned long long Icmp6InMsgs = 0ULL;
+    unsigned long long Icmp6InErrors = 0ULL;
+    unsigned long long Icmp6OutMsgs = 0ULL;
+    unsigned long long Icmp6OutErrors = 0ULL;
+    unsigned long long Icmp6InCsumErrors = 0ULL;
+    unsigned long long Icmp6InDestUnreachs = 0ULL;
+    unsigned long long Icmp6InPktTooBigs = 0ULL;
+    unsigned long long Icmp6InTimeExcds = 0ULL;
+    unsigned long long Icmp6InParmProblems = 0ULL;
+    unsigned long long Icmp6InEchos = 0ULL;
+    unsigned long long Icmp6InEchoReplies = 0ULL;
+    unsigned long long Icmp6InGroupMembQueries = 0ULL;
+    unsigned long long Icmp6InGroupMembResponses = 0ULL;
+    unsigned long long Icmp6InGroupMembReductions = 0ULL;
+    unsigned long long Icmp6InRouterSolicits = 0ULL;
+    unsigned long long Icmp6InRouterAdvertisements = 0ULL;
+    unsigned long long Icmp6InNeighborSolicits = 0ULL;
+    unsigned long long Icmp6InNeighborAdvertisements = 0ULL;
+    unsigned long long Icmp6InRedirects = 0ULL;
+    unsigned long long Icmp6InMLDv2Reports = 0ULL;
+    unsigned long long Icmp6OutDestUnreachs = 0ULL;
+    unsigned long long Icmp6OutPktTooBigs = 0ULL;
+    unsigned long long Icmp6OutTimeExcds = 0ULL;
+    unsigned long long Icmp6OutParmProblems = 0ULL;
+    unsigned long long Icmp6OutEchos = 0ULL;
+    unsigned long long Icmp6OutEchoReplies = 0ULL;
+    unsigned long long Icmp6OutGroupMembQueries = 0ULL;
+    unsigned long long Icmp6OutGroupMembResponses = 0ULL;
+    unsigned long long Icmp6OutGroupMembReductions = 0ULL;
+    unsigned long long Icmp6OutRouterSolicits = 0ULL;
+    unsigned long long Icmp6OutRouterAdvertisements = 0ULL;
+    unsigned long long Icmp6OutNeighborSolicits = 0ULL;
+    unsigned long long Icmp6OutNeighborAdvertisements = 0ULL;
+    unsigned long long Icmp6OutRedirects = 0ULL;
+    unsigned long long Icmp6OutMLDv2Reports = 0ULL;
+    unsigned long long Icmp6InType1 = 0ULL;
+    unsigned long long Icmp6InType128 = 0ULL;
+    unsigned long long Icmp6InType129 = 0ULL;
+    unsigned long long Icmp6InType136 = 0ULL;
+    unsigned long long Icmp6OutType1 = 0ULL;
+    unsigned long long Icmp6OutType128 = 0ULL;
+    unsigned long long Icmp6OutType129 = 0ULL;
+    unsigned long long Icmp6OutType133 = 0ULL;
+    unsigned long long Icmp6OutType135 = 0ULL;
+    unsigned long long Icmp6OutType143 = 0ULL;
+    unsigned long long Udp6InDatagrams = 0ULL;
+    unsigned long long Udp6NoPorts = 0ULL;
+    unsigned long long Udp6InErrors = 0ULL;
+    unsigned long long Udp6OutDatagrams = 0ULL;
+    unsigned long long Udp6RcvbufErrors = 0ULL;
+    unsigned long long Udp6SndbufErrors = 0ULL;
+    unsigned long long Udp6InCsumErrors = 0ULL;
+    unsigned long long Udp6IgnoredMulti = 0ULL;
+    unsigned long long UdpLite6InDatagrams = 0ULL;
+    unsigned long long UdpLite6NoPorts = 0ULL;
+    unsigned long long UdpLite6InErrors = 0ULL;
+    unsigned long long UdpLite6OutDatagrams = 0ULL;
+    unsigned long long UdpLite6RcvbufErrors = 0ULL;
+    unsigned long long UdpLite6SndbufErrors = 0ULL;
+    unsigned long long UdpLite6InCsumErrors = 0ULL;
+
+    for(l = 0; l < lines ;l++) {
+        words = procfile_linewords(ff, l);
+        if(words < 2) {
+            if(words) error("Cannot read /proc/net/snmp6 line %u. Expected 2 params, read %u.", l, words);
+            continue;
+        }
+
+        char *name = procfile_lineword(ff, l, 0);
+        char * value = procfile_lineword(ff, l, 1);
+        if(!name || !*name || !value || !*value) continue;
+
+        uint32_t hash = simple_hash(name);
+
+        if(0) ;
+        else if(hash == hash_Ip6InReceives && strcmp(name, "Ip6InReceives") == 0) Ip6InReceives = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InHdrErrors && strcmp(name, "Ip6InHdrErrors") == 0) Ip6InHdrErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InTooBigErrors && strcmp(name, "Ip6InTooBigErrors") == 0) Ip6InTooBigErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InNoRoutes && strcmp(name, "Ip6InNoRoutes") == 0) Ip6InNoRoutes = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InAddrErrors && strcmp(name, "Ip6InAddrErrors") == 0) Ip6InAddrErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InUnknownProtos && strcmp(name, "Ip6InUnknownProtos") == 0) Ip6InUnknownProtos = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InTruncatedPkts && strcmp(name, "Ip6InTruncatedPkts") == 0) Ip6InTruncatedPkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InDiscards && strcmp(name, "Ip6InDiscards") == 0) Ip6InDiscards = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InDelivers && strcmp(name, "Ip6InDelivers") == 0) Ip6InDelivers = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutForwDatagrams && strcmp(name, "Ip6OutForwDatagrams") == 0) Ip6OutForwDatagrams = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutRequests && strcmp(name, "Ip6OutRequests") == 0) Ip6OutRequests = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutDiscards && strcmp(name, "Ip6OutDiscards") == 0) Ip6OutDiscards = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutNoRoutes && strcmp(name, "Ip6OutNoRoutes") == 0) Ip6OutNoRoutes = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6ReasmTimeout && strcmp(name, "Ip6ReasmTimeout") == 0) Ip6ReasmTimeout = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6ReasmReqds && strcmp(name, "Ip6ReasmReqds") == 0) Ip6ReasmReqds = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6ReasmOKs && strcmp(name, "Ip6ReasmOKs") == 0) Ip6ReasmOKs = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6ReasmFails && strcmp(name, "Ip6ReasmFails") == 0) Ip6ReasmFails = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6FragOKs && strcmp(name, "Ip6FragOKs") == 0) Ip6FragOKs = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6FragFails && strcmp(name, "Ip6FragFails") == 0) Ip6FragFails = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6FragCreates && strcmp(name, "Ip6FragCreates") == 0) Ip6FragCreates = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InMcastPkts && strcmp(name, "Ip6InMcastPkts") == 0) Ip6InMcastPkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutMcastPkts && strcmp(name, "Ip6OutMcastPkts") == 0) Ip6OutMcastPkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InOctets && strcmp(name, "Ip6InOctets") == 0) Ip6InOctets = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutOctets && strcmp(name, "Ip6OutOctets") == 0) Ip6OutOctets = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InMcastOctets && strcmp(name, "Ip6InMcastOctets") == 0) Ip6InMcastOctets = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutMcastOctets && strcmp(name, "Ip6OutMcastOctets") == 0) Ip6OutMcastOctets = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InBcastOctets && strcmp(name, "Ip6InBcastOctets") == 0) Ip6InBcastOctets = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6OutBcastOctets && strcmp(name, "Ip6OutBcastOctets") == 0) Ip6OutBcastOctets = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InNoECTPkts && strcmp(name, "Ip6InNoECTPkts") == 0) Ip6InNoECTPkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InECT1Pkts && strcmp(name, "Ip6InECT1Pkts") == 0) Ip6InECT1Pkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InECT0Pkts && strcmp(name, "Ip6InECT0Pkts") == 0) Ip6InECT0Pkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Ip6InCEPkts && strcmp(name, "Ip6InCEPkts") == 0) Ip6InCEPkts = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InMsgs && strcmp(name, "Icmp6InMsgs") == 0) Icmp6InMsgs = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InErrors && strcmp(name, "Icmp6InErrors") == 0) Icmp6InErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutMsgs && strcmp(name, "Icmp6OutMsgs") == 0) Icmp6OutMsgs = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutErrors && strcmp(name, "Icmp6OutErrors") == 0) Icmp6OutErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InCsumErrors && strcmp(name, "Icmp6InCsumErrors") == 0) Icmp6InCsumErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InDestUnreachs && strcmp(name, "Icmp6InDestUnreachs") == 0) Icmp6InDestUnreachs = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InPktTooBigs && strcmp(name, "Icmp6InPktTooBigs") == 0) Icmp6InPktTooBigs = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InTimeExcds && strcmp(name, "Icmp6InTimeExcds") == 0) Icmp6InTimeExcds = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InParmProblems && strcmp(name, "Icmp6InParmProblems") == 0) Icmp6InParmProblems = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InEchos && strcmp(name, "Icmp6InEchos") == 0) Icmp6InEchos = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InEchoReplies && strcmp(name, "Icmp6InEchoReplies") == 0) Icmp6InEchoReplies = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InGroupMembQueries && strcmp(name, "Icmp6InGroupMembQueries") == 0) Icmp6InGroupMembQueries = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InGroupMembResponses && strcmp(name, "Icmp6InGroupMembResponses") == 0) Icmp6InGroupMembResponses = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InGroupMembReductions && strcmp(name, "Icmp6InGroupMembReductions") == 0) Icmp6InGroupMembReductions = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InRouterSolicits && strcmp(name, "Icmp6InRouterSolicits") == 0) Icmp6InRouterSolicits = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InRouterAdvertisements && strcmp(name, "Icmp6InRouterAdvertisements") == 0) Icmp6InRouterAdvertisements = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InNeighborSolicits && strcmp(name, "Icmp6InNeighborSolicits") == 0) Icmp6InNeighborSolicits = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InNeighborAdvertisements && strcmp(name, "Icmp6InNeighborAdvertisements") == 0) Icmp6InNeighborAdvertisements = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InRedirects && strcmp(name, "Icmp6InRedirects") == 0) Icmp6InRedirects = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InMLDv2Reports && strcmp(name, "Icmp6InMLDv2Reports") == 0) Icmp6InMLDv2Reports = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutDestUnreachs && strcmp(name, "Icmp6OutDestUnreachs") == 0) Icmp6OutDestUnreachs = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutPktTooBigs && strcmp(name, "Icmp6OutPktTooBigs") == 0) Icmp6OutPktTooBigs = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutTimeExcds && strcmp(name, "Icmp6OutTimeExcds") == 0) Icmp6OutTimeExcds = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutParmProblems && strcmp(name, "Icmp6OutParmProblems") == 0) Icmp6OutParmProblems = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutEchos && strcmp(name, "Icmp6OutEchos") == 0) Icmp6OutEchos = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutEchoReplies && strcmp(name, "Icmp6OutEchoReplies") == 0) Icmp6OutEchoReplies = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutGroupMembQueries && strcmp(name, "Icmp6OutGroupMembQueries") == 0) Icmp6OutGroupMembQueries = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutGroupMembResponses && strcmp(name, "Icmp6OutGroupMembResponses") == 0) Icmp6OutGroupMembResponses = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutGroupMembReductions && strcmp(name, "Icmp6OutGroupMembReductions") == 0) Icmp6OutGroupMembReductions = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutRouterSolicits && strcmp(name, "Icmp6OutRouterSolicits") == 0) Icmp6OutRouterSolicits = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutRouterAdvertisements && strcmp(name, "Icmp6OutRouterAdvertisements") == 0) Icmp6OutRouterAdvertisements = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutNeighborSolicits && strcmp(name, "Icmp6OutNeighborSolicits") == 0) Icmp6OutNeighborSolicits = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutNeighborAdvertisements && strcmp(name, "Icmp6OutNeighborAdvertisements") == 0) Icmp6OutNeighborAdvertisements = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutRedirects && strcmp(name, "Icmp6OutRedirects") == 0) Icmp6OutRedirects = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutMLDv2Reports && strcmp(name, "Icmp6OutMLDv2Reports") == 0) Icmp6OutMLDv2Reports = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InType1 && strcmp(name, "Icmp6InType1") == 0) Icmp6InType1 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InType128 && strcmp(name, "Icmp6InType128") == 0) Icmp6InType128 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InType129 && strcmp(name, "Icmp6InType129") == 0) Icmp6InType129 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6InType136 && strcmp(name, "Icmp6InType136") == 0) Icmp6InType136 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutType1 && strcmp(name, "Icmp6OutType1") == 0) Icmp6OutType1 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutType128 && strcmp(name, "Icmp6OutType128") == 0) Icmp6OutType128 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutType129 && strcmp(name, "Icmp6OutType129") == 0) Icmp6OutType129 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutType133 && strcmp(name, "Icmp6OutType133") == 0) Icmp6OutType133 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutType135 && strcmp(name, "Icmp6OutType135") == 0) Icmp6OutType135 = strtoull(value, NULL, 10);
+        else if(hash == hash_Icmp6OutType143 && strcmp(name, "Icmp6OutType143") == 0) Icmp6OutType143 = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6InDatagrams && strcmp(name, "Udp6InDatagrams") == 0) Udp6InDatagrams = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6NoPorts && strcmp(name, "Udp6NoPorts") == 0) Udp6NoPorts = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6InErrors && strcmp(name, "Udp6InErrors") == 0) Udp6InErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6OutDatagrams && strcmp(name, "Udp6OutDatagrams") == 0) Udp6OutDatagrams = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6RcvbufErrors && strcmp(name, "Udp6RcvbufErrors") == 0) Udp6RcvbufErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6SndbufErrors && strcmp(name, "Udp6SndbufErrors") == 0) Udp6SndbufErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6InCsumErrors && strcmp(name, "Udp6InCsumErrors") == 0) Udp6InCsumErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_Udp6IgnoredMulti && strcmp(name, "Udp6IgnoredMulti") == 0) Udp6IgnoredMulti = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6InDatagrams && strcmp(name, "UdpLite6InDatagrams") == 0) UdpLite6InDatagrams = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6NoPorts && strcmp(name, "UdpLite6NoPorts") == 0) UdpLite6NoPorts = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6InErrors && strcmp(name, "UdpLite6InErrors") == 0) UdpLite6InErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6OutDatagrams && strcmp(name, "UdpLite6OutDatagrams") == 0) UdpLite6OutDatagrams = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6RcvbufErrors && strcmp(name, "UdpLite6RcvbufErrors") == 0) UdpLite6RcvbufErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6SndbufErrors && strcmp(name, "UdpLite6SndbufErrors") == 0) UdpLite6SndbufErrors = strtoull(value, NULL, 10);
+        else if(hash == hash_UdpLite6InCsumErrors && strcmp(name, "UdpLite6InCsumErrors") == 0) UdpLite6InCsumErrors = strtoull(value, NULL, 10);
+    }
+
+    RRDSET *st;
+
+    // --------------------------------------------------------------------
+
+    if(do_bandwidth == CONFIG_ONDEMAND_YES || (do_bandwidth == CONFIG_ONDEMAND_ONDEMAND && (Ip6InOctets || Ip6OutOctets))) {
+        do_bandwidth = CONFIG_ONDEMAND_YES;
+        st = rrdset_find("system.ipv6");
+        if(!st) {
+            st = rrdset_create("system", "ipv6", NULL, "network", NULL, "IPv6 Bandwidth", "kilobits/s", 500, update_every, RRDSET_TYPE_AREA);
+
+            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Ip6OutOctets);
+        rrddim_set(st, "received", Ip6InOctets);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_ip_packets == CONFIG_ONDEMAND_YES || (do_ip_packets == CONFIG_ONDEMAND_ONDEMAND && (Ip6InReceives || Ip6OutRequests || Ip6InDelivers || Ip6OutForwDatagrams))) {
+        do_ip_packets = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".packets");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "packets", NULL, "packets", NULL, "IPv6 Packets", "packets/s", 3000, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "forwarded", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "delivers", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Ip6OutRequests);
+        rrddim_set(st, "received", Ip6InReceives);
+        rrddim_set(st, "forwarded", Ip6InDelivers);
+        rrddim_set(st, "delivers", Ip6OutForwDatagrams);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_ip_fragsout == CONFIG_ONDEMAND_YES || (do_ip_fragsout == CONFIG_ONDEMAND_ONDEMAND && (Ip6FragOKs || Ip6FragFails || Ip6FragCreates))) {
+        do_ip_fragsout = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsout");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsout", NULL, "fragments", NULL, "IPv6 Fragments Sent", "packets/s", 3010, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "ok", Ip6FragOKs);
+        rrddim_set(st, "failed", Ip6FragFails);
+        rrddim_set(st, "all", Ip6FragCreates);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_ip_fragsin == CONFIG_ONDEMAND_YES || (do_ip_fragsin == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Ip6ReasmOKs
+            || Ip6ReasmFails
+            || Ip6ReasmTimeout
+            || Ip6ReasmReqds
+            ))) {
+        do_ip_fragsin = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".fragsin");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "fragsin", NULL, "fragments", NULL, "IPv6 Fragments Reassembly", "packets/s", 3011, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "ok", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "timeout", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "all", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "ok", Ip6ReasmOKs);
+        rrddim_set(st, "failed", Ip6ReasmFails);
+        rrddim_set(st, "timeout", Ip6ReasmTimeout);
+        rrddim_set(st, "all", Ip6ReasmReqds);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_ip_errors == CONFIG_ONDEMAND_YES || (do_ip_errors == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Ip6InDiscards
+            || Ip6OutDiscards
+            || Ip6InHdrErrors
+            || Ip6InAddrErrors
+            || Ip6InUnknownProtos
+            || Ip6InTooBigErrors
+            || Ip6InTruncatedPkts
+            || Ip6InNoRoutes
+        ))) {
+        do_ip_errors = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".errors");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "errors", NULL, "errors", NULL, "IPv6 Errors", "packets/s", 3002, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "InDiscards", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutDiscards", NULL, -1, 1, RRDDIM_INCREMENTAL);
+
+            rrddim_add(st, "InHdrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InAddrErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InUnknownProtos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InTooBigErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InTruncatedPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InNoRoutes", NULL, 1, 1, RRDDIM_INCREMENTAL);
+
+            rrddim_add(st, "OutNoRoutes", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InDiscards", Ip6InDiscards);
+        rrddim_set(st, "OutDiscards", Ip6OutDiscards);
+
+        rrddim_set(st, "InHdrErrors", Ip6InHdrErrors);
+        rrddim_set(st, "InAddrErrors", Ip6InAddrErrors);
+        rrddim_set(st, "InUnknownProtos", Ip6InUnknownProtos);
+        rrddim_set(st, "InTooBigErrors", Ip6InTooBigErrors);
+        rrddim_set(st, "InTruncatedPkts", Ip6InTruncatedPkts);
+        rrddim_set(st, "InNoRoutes", Ip6InNoRoutes);
+
+        rrddim_set(st, "OutNoRoutes", Ip6OutNoRoutes);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_udp_packets == CONFIG_ONDEMAND_YES || (do_udp_packets == CONFIG_ONDEMAND_ONDEMAND && (Udp6InDatagrams || Udp6OutDatagrams))) {
+        do_udp_packets = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udppackets");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udppackets", NULL, "udp", NULL, "IPv6 UDP Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "received", Udp6InDatagrams);
+        rrddim_set(st, "sent", Udp6OutDatagrams);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_udp_errors == CONFIG_ONDEMAND_YES || (do_udp_errors == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Udp6InErrors
+            || Udp6NoPorts
+            || Udp6RcvbufErrors
+            || Udp6SndbufErrors
+            || Udp6InCsumErrors
+            || Udp6IgnoredMulti
+        ))) {
+        do_udp_errors = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udperrors");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udperrors", NULL, "udp", NULL, "IPv6 UDP Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "IgnoredMulti", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InErrors", Udp6InErrors);
+        rrddim_set(st, "NoPorts", Udp6NoPorts);
+        rrddim_set(st, "RcvbufErrors", Udp6RcvbufErrors);
+        rrddim_set(st, "SndbufErrors", Udp6SndbufErrors);
+        rrddim_set(st, "InCsumErrors", Udp6InCsumErrors);
+        rrddim_set(st, "IgnoredMulti", Udp6IgnoredMulti);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_udplite_packets == CONFIG_ONDEMAND_YES || (do_udplite_packets == CONFIG_ONDEMAND_ONDEMAND && (UdpLite6InDatagrams || UdpLite6OutDatagrams))) {
+        do_udplite_packets = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udplitepackets");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udplitepackets", NULL, "udplite", NULL, "IPv6 UDPlite Packets", "packets/s", 3601, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "received", UdpLite6InDatagrams);
+        rrddim_set(st, "sent", UdpLite6OutDatagrams);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_udplite_errors == CONFIG_ONDEMAND_YES || (do_udplite_errors == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            UdpLite6InErrors
+            || UdpLite6NoPorts
+            || UdpLite6RcvbufErrors
+            || UdpLite6SndbufErrors
+            || Udp6InCsumErrors
+            || UdpLite6InCsumErrors
+        ))) {
+        do_udplite_errors = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".udpliteerrors");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "udpliteerrors", NULL, "udplite", NULL, "IPv6 UDP Lite Errors", "events/s", 3701, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "RcvbufErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "SndbufErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "NoPorts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InErrors", UdpLite6InErrors);
+        rrddim_set(st, "NoPorts", UdpLite6NoPorts);
+        rrddim_set(st, "RcvbufErrors", UdpLite6RcvbufErrors);
+        rrddim_set(st, "SndbufErrors", UdpLite6SndbufErrors);
+        rrddim_set(st, "InCsumErrors", UdpLite6InCsumErrors);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_mcast == CONFIG_ONDEMAND_YES || (do_mcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastOctets || Ip6InMcastOctets))) {
+        do_mcast = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcast");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcast", NULL, "multicast", NULL, "IPv6 Multicast Bandwidth", "kilobits/s", 9000, update_every, RRDSET_TYPE_AREA);
+            st->isdetail = 1;
+
+            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Ip6OutMcastOctets);
+        rrddim_set(st, "received", Ip6InMcastOctets);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_bcast == CONFIG_ONDEMAND_YES || (do_bcast == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutBcastOctets || Ip6InBcastOctets))) {
+        do_bcast = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".bcast");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "bcast", NULL, "broadcast", NULL, "IPv6 Broadcast Bandwidth", "kilobits/s", 8000, update_every, RRDSET_TYPE_AREA);
+            st->isdetail = 1;
+
+            rrddim_add(st, "received", NULL, 8, 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -8, 1024, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Ip6OutBcastOctets);
+        rrddim_set(st, "received", Ip6InBcastOctets);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_mcast_p == CONFIG_ONDEMAND_YES || (do_mcast_p == CONFIG_ONDEMAND_ONDEMAND && (Ip6OutMcastPkts || Ip6InMcastPkts))) {
+        do_mcast_p = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".mcastpkts");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "mcastpkts", NULL, "multicast", NULL, "IPv6 Multicast Packets", "packets/s", 9500, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Ip6OutMcastPkts);
+        rrddim_set(st, "received", Ip6InMcastPkts);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp == CONFIG_ONDEMAND_YES || (do_icmp == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMsgs || Icmp6OutMsgs))) {
+        do_icmp = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmp");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmp", NULL, "icmp", NULL, "IPv6 ICMP Messages", "messages/s", 10000, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Icmp6InMsgs);
+        rrddim_set(st, "received", Icmp6OutMsgs);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_redir == CONFIG_ONDEMAND_YES || (do_icmp_redir == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InRedirects || Icmp6OutRedirects))) {
+        do_icmp_redir = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpredir");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpredir", NULL, "icmp", NULL, "IPv6 ICMP Redirects", "redirects/s", 10050, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Icmp6InRedirects);
+        rrddim_set(st, "received", Icmp6OutRedirects);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_errors == CONFIG_ONDEMAND_YES || (do_icmp_errors == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Icmp6InErrors
+            || Icmp6OutErrors
+            || Icmp6InCsumErrors
+            || Icmp6InDestUnreachs
+            || Icmp6InPktTooBigs
+            || Icmp6InTimeExcds
+            || Icmp6InParmProblems
+            || Icmp6OutDestUnreachs
+            || Icmp6OutPktTooBigs
+            || Icmp6OutTimeExcds
+            || Icmp6OutParmProblems
+        ))) {
+        do_icmp_errors = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmperrors");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmperrors", NULL, "icmp", NULL, "IPv6 ICMP Errors", "errors/s", 10100, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutErrors", NULL, -1, 1, RRDDIM_INCREMENTAL);
+
+            rrddim_add(st, "InCsumErrors", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InDestUnreachs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InPktTooBigs", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InTimeExcds", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InParmProblems", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutDestUnreachs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutPktTooBigs", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutTimeExcds", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutParmProblems", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InErrors", Icmp6InErrors);
+        rrddim_set(st, "OutErrors", Icmp6OutErrors);
+        rrddim_set(st, "InCsumErrors", Icmp6InCsumErrors);
+        rrddim_set(st, "InDestUnreachs", Icmp6InDestUnreachs);
+        rrddim_set(st, "InPktTooBigs", Icmp6InPktTooBigs);
+        rrddim_set(st, "InTimeExcds", Icmp6InTimeExcds);
+        rrddim_set(st, "InParmProblems", Icmp6InParmProblems);
+        rrddim_set(st, "OutDestUnreachs", Icmp6OutDestUnreachs);
+        rrddim_set(st, "OutPktTooBigs", Icmp6OutPktTooBigs);
+        rrddim_set(st, "OutTimeExcds", Icmp6OutTimeExcds);
+        rrddim_set(st, "OutParmProblems", Icmp6OutParmProblems);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_echos == CONFIG_ONDEMAND_YES || (do_icmp_echos == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Icmp6InEchos
+            || Icmp6OutEchos
+            || Icmp6InEchoReplies
+            || Icmp6OutEchoReplies
+        ))) {
+        do_icmp_echos = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpechos");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpechos", NULL, "icmp", NULL, "IPv6 ICMP Echo", "messages/s", 10200, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InEchos", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutEchos", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InEchoReplies", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutEchoReplies", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InEchos", Icmp6InEchos);
+        rrddim_set(st, "OutEchos", Icmp6OutEchos);
+        rrddim_set(st, "InEchoReplies", Icmp6InEchoReplies);
+        rrddim_set(st, "OutEchoReplies", Icmp6OutEchoReplies);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_groupmemb == CONFIG_ONDEMAND_YES || (do_icmp_groupmemb == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Icmp6InGroupMembQueries
+            || Icmp6OutGroupMembQueries
+            || Icmp6InGroupMembResponses
+            || Icmp6OutGroupMembResponses
+            || Icmp6InGroupMembReductions
+            || Icmp6OutGroupMembReductions
+        ))) {
+        do_icmp_groupmemb = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".groupmemb");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "groupmemb", NULL, "icmp", NULL, "IPv6 ICMP Group Membership", "messages/s", 10300, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InQueries", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutQueries", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InResponses", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutResponses", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InReductions", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutReductions", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InQueries", Icmp6InGroupMembQueries);
+        rrddim_set(st, "OutQueries", Icmp6OutGroupMembQueries);
+        rrddim_set(st, "InResponses", Icmp6InGroupMembResponses);
+        rrddim_set(st, "OutResponses", Icmp6OutGroupMembResponses);
+        rrddim_set(st, "InReductions", Icmp6InGroupMembReductions);
+        rrddim_set(st, "OutReductions", Icmp6OutGroupMembReductions);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_router == CONFIG_ONDEMAND_YES || (do_icmp_router == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Icmp6InRouterSolicits
+            || Icmp6OutRouterSolicits
+            || Icmp6InRouterAdvertisements
+            || Icmp6OutRouterAdvertisements
+        ))) {
+        do_icmp_router = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmprouter");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmprouter", NULL, "icmp", NULL, "IPv6 Router Messages", "messages/s", 10400, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InSolicits", Icmp6InRouterSolicits);
+        rrddim_set(st, "OutSolicits", Icmp6OutRouterSolicits);
+        rrddim_set(st, "InAdvertisements", Icmp6InRouterAdvertisements);
+        rrddim_set(st, "OutAdvertisements", Icmp6OutRouterAdvertisements);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_neighbor == CONFIG_ONDEMAND_YES || (do_icmp_neighbor == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Icmp6InNeighborSolicits
+            || Icmp6OutNeighborSolicits
+            || Icmp6InNeighborAdvertisements
+            || Icmp6OutNeighborAdvertisements
+        ))) {
+        do_icmp_neighbor = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpneighbor");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpneighbor", NULL, "icmp", NULL, "IPv6 Neighbor Messages", "messages/s", 10500, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InSolicits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutSolicits", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InAdvertisements", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutAdvertisements", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InSolicits", Icmp6InNeighborSolicits);
+        rrddim_set(st, "OutSolicits", Icmp6OutNeighborSolicits);
+        rrddim_set(st, "InAdvertisements", Icmp6InNeighborAdvertisements);
+        rrddim_set(st, "OutAdvertisements", Icmp6OutNeighborAdvertisements);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_mldv2 == CONFIG_ONDEMAND_YES || (do_icmp_mldv2 == CONFIG_ONDEMAND_ONDEMAND && (Icmp6InMLDv2Reports || Icmp6OutMLDv2Reports))) {
+        do_icmp_mldv2 = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmpmldv2");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmpmldv2", NULL, "icmp", NULL, "IPv6 ICMP MLDv2 Reports", "reports/s", 10600, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "sent", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "sent", Icmp6InMLDv2Reports);
+        rrddim_set(st, "received", Icmp6OutMLDv2Reports);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_icmp_types == CONFIG_ONDEMAND_YES || (do_icmp_types == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Icmp6InType1
+            || Icmp6InType128
+            || Icmp6InType129
+            || Icmp6InType136
+            || Icmp6OutType1
+            || Icmp6OutType128
+            || Icmp6OutType129
+            || Icmp6OutType133
+            || Icmp6OutType135
+            || Icmp6OutType143
+        ))) {
+        do_icmp_types = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".icmptypes");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "icmptypes", NULL, "icmp", NULL, "IPv6 ICMP Types", "messages/s", 10700, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InType1", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InType128", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InType129", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InType136", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutType1", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutType128", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutType129", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutType133", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutType135", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "OutType143", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InType1", Icmp6InType1);
+        rrddim_set(st, "InType128", Icmp6InType128);
+        rrddim_set(st, "InType129", Icmp6InType129);
+        rrddim_set(st, "InType136", Icmp6InType136);
+        rrddim_set(st, "OutType1", Icmp6OutType1);
+        rrddim_set(st, "OutType128", Icmp6OutType128);
+        rrddim_set(st, "OutType129", Icmp6OutType129);
+        rrddim_set(st, "OutType133", Icmp6OutType133);
+        rrddim_set(st, "OutType135", Icmp6OutType135);
+        rrddim_set(st, "OutType143", Icmp6OutType143);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_ect == CONFIG_ONDEMAND_YES || (do_ect == CONFIG_ONDEMAND_ONDEMAND
+        && (
+            Ip6InNoECTPkts
+            || Ip6InECT1Pkts
+            || Ip6InECT0Pkts
+            || Ip6InCEPkts
+        ))) {
+        do_ect = CONFIG_ONDEMAND_YES;
+        st = rrdset_find(RRD_TYPE_NET_SNMP6 ".ect");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_SNMP6, "ect", NULL, "packets", NULL, "IPv6 ECT Packets", "packets/s", 10800, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "InNoECTPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InECT1Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InECT0Pkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "InCEPkts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "InNoECTPkts", Ip6InNoECTPkts);
+        rrddim_set(st, "InECT1Pkts", Ip6InECT1Pkts);
+        rrddim_set(st, "InECT0Pkts", Ip6InECT0Pkts);
+        rrddim_set(st, "InCEPkts", Ip6InCEPkts);
+        rrdset_done(st);
+    }
+
+    return 0;
 }
 
index 1fcf6ffceaf27491401ea539bf5265fcc4c3ee4d..8234b20d28e7c4a19de12f9ded9386a4aae2f653 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
-#define RRD_TYPE_NET_STAT_NETFILTER            "netfilter"
-#define RRD_TYPE_NET_STAT_CONNTRACK    "conntrack"
-#define RRD_TYPE_NET_STAT_CONNTRACK_LEN        strlen(RRD_TYPE_NET_STAT_CONNTRACK)
+#define RRD_TYPE_NET_STAT_NETFILTER     "netfilter"
+#define RRD_TYPE_NET_STAT_CONNTRACK     "conntrack"
+#define RRD_TYPE_NET_STAT_CONNTRACK_LEN strlen(RRD_TYPE_NET_STAT_CONNTRACK)
 
 int do_proc_net_stat_conntrack(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1;
-
-       if(do_sockets == -1)    do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", 1);
-       if(do_new == -1)                do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", 1);
-       if(do_changes == -1)    do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", 1);
-       if(do_expect == -1)             do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", 1);
-       if(do_search == -1)             do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", 1);
-       if(do_errors == -1)             do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", 1);
-
-       if(dt) {};
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/nf_conntrack");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
-               ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0;
-
-       for(l = 1; l < lines ;l++) {
-               words = procfile_linewords(ff, l);
-               if(words < 17) {
-                       if(words) error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %u.", words);
-                       continue;
-               }
-
-               unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0;
-
-               tentries                = strtoull(procfile_lineword(ff, l, 0), NULL, 16);
-               tsearched               = strtoull(procfile_lineword(ff, l, 1), NULL, 16);
-               tfound                  = strtoull(procfile_lineword(ff, l, 2), NULL, 16);
-               tnew                    = strtoull(procfile_lineword(ff, l, 3), NULL, 16);
-               tinvalid                = strtoull(procfile_lineword(ff, l, 4), NULL, 16);
-               tignore                 = strtoull(procfile_lineword(ff, l, 5), NULL, 16);
-               tdelete                 = strtoull(procfile_lineword(ff, l, 6), NULL, 16);
-               tdelete_list    = strtoull(procfile_lineword(ff, l, 7), NULL, 16);
-               tinsert                 = strtoull(procfile_lineword(ff, l, 8), NULL, 16);
-               tinsert_failed  = strtoull(procfile_lineword(ff, l, 9), NULL, 16);
-               tdrop                   = strtoull(procfile_lineword(ff, l, 10), NULL, 16);
-               tearly_drop             = strtoull(procfile_lineword(ff, l, 11), NULL, 16);
-               ticmp_error             = strtoull(procfile_lineword(ff, l, 12), NULL, 16);
-               texpect_new             = strtoull(procfile_lineword(ff, l, 13), NULL, 16);
-               texpect_create  = strtoull(procfile_lineword(ff, l, 14), NULL, 16);
-               texpect_delete  = strtoull(procfile_lineword(ff, l, 15), NULL, 16);
-               tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16);
-
-               if(!aentries) aentries =  tentries;
-
-               // sum all the cpus together
-               asearched                       += tsearched;           // conntrack.search
-               afound                          += tfound;                      // conntrack.search
-               anew                            += tnew;                        // conntrack.new
-               ainvalid                        += tinvalid;            // conntrack.new
-               aignore                         += tignore;                     // conntrack.new
-               adelete                         += tdelete;                     // conntrack.changes
-               adelete_list            += tdelete_list;        // conntrack.changes
-               ainsert                         += tinsert;                     // conntrack.changes
-               ainsert_failed          += tinsert_failed;      // conntrack.errors
-               adrop                           += tdrop;                       // conntrack.errors
-               aearly_drop             += tearly_drop;         // conntrack.errors
-               aicmp_error             += ticmp_error;         // conntrack.errors
-               aexpect_new             += texpect_new;         // conntrack.expect
-               aexpect_create          += texpect_create;      // conntrack.expect
-               aexpect_delete          += texpect_delete;      // conntrack.expect
-               asearch_restart         += tsearch_restart;     // conntrack.search
-       }
-
-       RRDSET *st;
-
-       // --------------------------------------------------------------------
-
-       if(do_sockets) {
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 1000, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "connections", aentries);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_new) {
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "ignore", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "new", anew);
-               rrddim_set(st, "ignore", aignore);
-               rrddim_set(st, "invalid", ainvalid);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_changes) {
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 1002, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "inserted", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "delete_list", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "inserted", ainsert);
-               rrddim_set(st, "deleted", adelete);
-               rrddim_set(st, "delete_list", adelete_list);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_expect) {
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 1003, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "created", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "created", aexpect_create);
-               rrddim_set(st, "deleted", aexpect_delete);
-               rrddim_set(st, "new", aexpect_new);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_search) {
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 1010, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "searched", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "restarted", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "found", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "searched", asearched);
-               rrddim_set(st, "restarted", asearch_restart);
-               rrddim_set(st, "found", afound);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(do_errors) {
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 1005, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "icmp_error", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "insert_failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "early_drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "icmp_error", aicmp_error);
-               rrddim_set(st, "insert_failed", ainsert_failed);
-               rrddim_set(st, "drop", adrop);
-               rrddim_set(st, "early_drop", aearly_drop);
-               rrdset_done(st);
-       }
-
-       return 0;
+    static procfile *ff = NULL;
+    static int do_sockets = -1, do_new = -1, do_changes = -1, do_expect = -1, do_search = -1, do_errors = -1;
+
+    if(do_sockets == -1)    do_sockets = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connections", 1);
+    if(do_new == -1)        do_new = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter new connections", 1);
+    if(do_changes == -1)    do_changes = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection changes", 1);
+    if(do_expect == -1)     do_expect = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection expectations", 1);
+    if(do_search == -1)     do_search = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter connection searches", 1);
+    if(do_errors == -1)     do_errors = config_get_boolean("plugin:proc:/proc/net/stat/nf_conntrack", "netfilter errors", 1);
+
+    if(dt) {};
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/nf_conntrack");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/stat/nf_conntrack", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    unsigned long long aentries = 0, asearched = 0, afound = 0, anew = 0, ainvalid = 0, aignore = 0, adelete = 0, adelete_list = 0,
+        ainsert = 0, ainsert_failed = 0, adrop = 0, aearly_drop = 0, aicmp_error = 0, aexpect_new = 0, aexpect_create = 0, aexpect_delete = 0, asearch_restart = 0;
+
+    for(l = 1; l < lines ;l++) {
+        words = procfile_linewords(ff, l);
+        if(words < 17) {
+            if(words) error("Cannot read /proc/net/stat/nf_conntrack line. Expected 17 params, read %u.", words);
+            continue;
+        }
+
+        unsigned long long tentries = 0, tsearched = 0, tfound = 0, tnew = 0, tinvalid = 0, tignore = 0, tdelete = 0, tdelete_list = 0, tinsert = 0, tinsert_failed = 0, tdrop = 0, tearly_drop = 0, ticmp_error = 0, texpect_new = 0, texpect_create = 0, texpect_delete = 0, tsearch_restart = 0;
+
+        tentries        = strtoull(procfile_lineword(ff, l, 0), NULL, 16);
+        tsearched       = strtoull(procfile_lineword(ff, l, 1), NULL, 16);
+        tfound          = strtoull(procfile_lineword(ff, l, 2), NULL, 16);
+        tnew            = strtoull(procfile_lineword(ff, l, 3), NULL, 16);
+        tinvalid        = strtoull(procfile_lineword(ff, l, 4), NULL, 16);
+        tignore         = strtoull(procfile_lineword(ff, l, 5), NULL, 16);
+        tdelete         = strtoull(procfile_lineword(ff, l, 6), NULL, 16);
+        tdelete_list    = strtoull(procfile_lineword(ff, l, 7), NULL, 16);
+        tinsert         = strtoull(procfile_lineword(ff, l, 8), NULL, 16);
+        tinsert_failed  = strtoull(procfile_lineword(ff, l, 9), NULL, 16);
+        tdrop           = strtoull(procfile_lineword(ff, l, 10), NULL, 16);
+        tearly_drop     = strtoull(procfile_lineword(ff, l, 11), NULL, 16);
+        ticmp_error     = strtoull(procfile_lineword(ff, l, 12), NULL, 16);
+        texpect_new     = strtoull(procfile_lineword(ff, l, 13), NULL, 16);
+        texpect_create  = strtoull(procfile_lineword(ff, l, 14), NULL, 16);
+        texpect_delete  = strtoull(procfile_lineword(ff, l, 15), NULL, 16);
+        tsearch_restart = strtoull(procfile_lineword(ff, l, 16), NULL, 16);
+
+        if(!aentries) aentries =  tentries;
+
+        // sum all the cpus together
+        asearched           += tsearched;       // conntrack.search
+        afound              += tfound;          // conntrack.search
+        anew                += tnew;            // conntrack.new
+        ainvalid            += tinvalid;        // conntrack.new
+        aignore             += tignore;         // conntrack.new
+        adelete             += tdelete;         // conntrack.changes
+        adelete_list        += tdelete_list;    // conntrack.changes
+        ainsert             += tinsert;         // conntrack.changes
+        ainsert_failed      += tinsert_failed;  // conntrack.errors
+        adrop               += tdrop;           // conntrack.errors
+        aearly_drop         += tearly_drop;     // conntrack.errors
+        aicmp_error         += ticmp_error;     // conntrack.errors
+        aexpect_new         += texpect_new;     // conntrack.expect
+        aexpect_create      += texpect_create;  // conntrack.expect
+        aexpect_delete      += texpect_delete;  // conntrack.expect
+        asearch_restart     += tsearch_restart; // conntrack.search
+    }
+
+    RRDSET *st;
+
+    // --------------------------------------------------------------------
+
+    if(do_sockets) {
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_sockets");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_sockets", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Connections", "active connections", 1000, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "connections", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "connections", aentries);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_new) {
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_new");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_new", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker New Connections", "connections/s", 1001, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "ignore", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "new", anew);
+        rrddim_set(st, "ignore", aignore);
+        rrddim_set(st, "invalid", ainvalid);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_changes) {
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_changes");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_changes", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Changes", "changes/s", 1002, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "inserted", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "delete_list", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "inserted", ainsert);
+        rrddim_set(st, "deleted", adelete);
+        rrddim_set(st, "delete_list", adelete_list);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_expect) {
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_expect");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_expect", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Expectations", "expectations/s", 1003, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "created", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "deleted", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "new", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "created", aexpect_create);
+        rrddim_set(st, "deleted", aexpect_delete);
+        rrddim_set(st, "new", aexpect_new);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_search) {
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_search");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_search", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Searches", "searches/s", 1010, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "searched", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "restarted", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "found", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "searched", asearched);
+        rrddim_set(st, "restarted", asearch_restart);
+        rrddim_set(st, "found", afound);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(do_errors) {
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_CONNTRACK "_errors");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_CONNTRACK "_errors", NULL, RRD_TYPE_NET_STAT_CONNTRACK, NULL, "Connection Tracker Errors", "events/s", 1005, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "icmp_error", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "insert_failed", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "early_drop", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "icmp_error", aicmp_error);
+        rrddim_set(st, "insert_failed", ainsert_failed);
+        rrddim_set(st, "drop", adrop);
+        rrddim_set(st, "early_drop", aearly_drop);
+        rrdset_done(st);
+    }
+
+    return 0;
 }
index d90a376f0914bbe3a9b903ad74b9302fde6d1b20..758c35dee2b23fa302f109cfe5e48767e0698e8d 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
 
-#define RRD_TYPE_NET_STAT_NETFILTER                    "netfilter"
-#define RRD_TYPE_NET_STAT_SYNPROXY                     "synproxy"
-#define RRD_TYPE_NET_STAT_SYNPROXY_LEN         strlen(RRD_TYPE_NET_STAT_SYNPROXY)
+#define RRD_TYPE_NET_STAT_NETFILTER         "netfilter"
+#define RRD_TYPE_NET_STAT_SYNPROXY          "synproxy"
+#define RRD_TYPE_NET_STAT_SYNPROXY_LEN      strlen(RRD_TYPE_NET_STAT_SYNPROXY)
 
 int do_proc_net_stat_synproxy(int update_every, unsigned long long dt) {
-       static int do_entries = -1, do_cookies = -1, do_syns = -1, do_reopened = -1;
-       static procfile *ff = NULL;
+    static int do_entries = -1, do_cookies = -1, do_syns = -1, do_reopened = -1;
+    static procfile *ff = NULL;
 
-       if(do_entries == -1)    do_entries      = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_cookies == -1)    do_cookies      = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_syns == -1)               do_syns         = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND);
-       if(do_reopened == -1)   do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_entries == -1)    do_entries  = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY entries", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_cookies == -1)    do_cookies  = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY cookies", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_syns == -1)       do_syns     = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY SYN received", CONFIG_ONDEMAND_ONDEMAND);
+    if(do_reopened == -1)   do_reopened = config_get_boolean_ondemand("plugin:proc:/proc/net/stat/synproxy", "SYNPROXY connections reopened", CONFIG_ONDEMAND_ONDEMAND);
 
-       if(dt) {};
+    if(dt) {};
 
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/synproxy");
-               ff = procfile_open(config_get("plugin:proc:/proc/net/stat/synproxy", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/net/stat/synproxy");
+        ff = procfile_open(config_get("plugin:proc:/proc/net/stat/synproxy", "filename to monitor", filename), " \t,:|", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
 
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
 
-       // make sure we have 3 lines
-       size_t lines = procfile_lines(ff), l;
-       if(lines < 2) {
-               error("/proc/net/stat/synproxy has %zu lines, expected no less than 2. Disabling it.", lines);
-               return 1;
-       }
+    // make sure we have 3 lines
+    size_t lines = procfile_lines(ff), l;
+    if(lines < 2) {
+        error("/proc/net/stat/synproxy has %zu lines, expected no less than 2. Disabling it.", lines);
+        return 1;
+    }
 
-       unsigned long long entries = 0, syn_received = 0, cookie_invalid = 0, cookie_valid = 0, cookie_retrans = 0, conn_reopened = 0;
+    unsigned long long entries = 0, syn_received = 0, cookie_invalid = 0, cookie_valid = 0, cookie_retrans = 0, conn_reopened = 0;
 
-       // synproxy gives its values per CPU
-       for(l = 1; l < lines ;l++) {
-               int words = procfile_linewords(ff, l);
-               if(words < 6) continue;
+    // synproxy gives its values per CPU
+    for(l = 1; l < lines ;l++) {
+        int words = procfile_linewords(ff, l);
+        if(words < 6) continue;
 
-               entries                 += strtoull(procfile_lineword(ff, l, 0), NULL, 16);
-               syn_received    += strtoull(procfile_lineword(ff, l, 1), NULL, 16);
-               cookie_invalid  += strtoull(procfile_lineword(ff, l, 2), NULL, 16);
-               cookie_valid    += strtoull(procfile_lineword(ff, l, 3), NULL, 16);
-               cookie_retrans  += strtoull(procfile_lineword(ff, l, 4), NULL, 16);
-               conn_reopened   += strtoull(procfile_lineword(ff, l, 5), NULL, 16);
-       }
+        entries         += strtoull(procfile_lineword(ff, l, 0), NULL, 16);
+        syn_received    += strtoull(procfile_lineword(ff, l, 1), NULL, 16);
+        cookie_invalid  += strtoull(procfile_lineword(ff, l, 2), NULL, 16);
+        cookie_valid    += strtoull(procfile_lineword(ff, l, 3), NULL, 16);
+        cookie_retrans  += strtoull(procfile_lineword(ff, l, 4), NULL, 16);
+        conn_reopened   += strtoull(procfile_lineword(ff, l, 5), NULL, 16);
+    }
 
-       unsigned long long events = entries + syn_received + cookie_invalid + cookie_valid + cookie_retrans + conn_reopened;
+    unsigned long long events = entries + syn_received + cookie_invalid + cookie_valid + cookie_retrans + conn_reopened;
 
-       RRDSET *st;
+    RRDSET *st;
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if((do_entries == CONFIG_ONDEMAND_ONDEMAND && events) || do_entries == CONFIG_ONDEMAND_YES) {
-               do_entries = CONFIG_ONDEMAND_YES;
+    if((do_entries == CONFIG_ONDEMAND_ONDEMAND && events) || do_entries == CONFIG_ONDEMAND_YES) {
+        do_entries = CONFIG_ONDEMAND_YES;
 
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 1004, update_every, RRDSET_TYPE_LINE);
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_entries");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_entries", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Entries Used", "entries", 1004, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "entries", NULL, 1, 1, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "entries", entries);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "entries", entries);
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if((do_syns == CONFIG_ONDEMAND_ONDEMAND && events) || do_syns == CONFIG_ONDEMAND_YES) {
-               do_syns = CONFIG_ONDEMAND_YES;
+    if((do_syns == CONFIG_ONDEMAND_ONDEMAND && events) || do_syns == CONFIG_ONDEMAND_YES) {
+        do_syns = CONFIG_ONDEMAND_YES;
 
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 1001, update_every, RRDSET_TYPE_LINE);
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_syn_received");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_syn_received", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY SYN Packets received", "SYN/s", 1001, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "received", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "received", syn_received);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "received", syn_received);
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if((do_reopened == CONFIG_ONDEMAND_ONDEMAND && events) || do_reopened == CONFIG_ONDEMAND_YES) {
-               do_reopened = CONFIG_ONDEMAND_YES;
+    if((do_reopened == CONFIG_ONDEMAND_ONDEMAND && events) || do_reopened == CONFIG_ONDEMAND_YES) {
+        do_reopened = CONFIG_ONDEMAND_YES;
 
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 1003, update_every, RRDSET_TYPE_LINE);
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_conn_reopened", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY Connections Reopened", "connections/s", 1003, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "reopened", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "reopened", conn_reopened);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "reopened", conn_reopened);
+        rrdset_done(st);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if((do_cookies == CONFIG_ONDEMAND_ONDEMAND && events) || do_cookies == CONFIG_ONDEMAND_YES) {
-               do_cookies = CONFIG_ONDEMAND_YES;
+    if((do_cookies == CONFIG_ONDEMAND_ONDEMAND && events) || do_cookies == CONFIG_ONDEMAND_YES) {
+        do_cookies = CONFIG_ONDEMAND_YES;
 
-               st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
-               if(!st) {
-                       st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 1002, update_every, RRDSET_TYPE_LINE);
+        st = rrdset_find(RRD_TYPE_NET_STAT_NETFILTER "." RRD_TYPE_NET_STAT_SYNPROXY "_cookies");
+        if(!st) {
+            st = rrdset_create(RRD_TYPE_NET_STAT_NETFILTER, RRD_TYPE_NET_STAT_SYNPROXY "_cookies", NULL, RRD_TYPE_NET_STAT_SYNPROXY, NULL, "SYNPROXY TCP Cookies", "cookies/s", 1002, update_every, RRDSET_TYPE_LINE);
 
-                       rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st, "retransmits", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
+            rrddim_add(st, "valid", NULL, 1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "invalid", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st, "retransmits", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
 
-               rrddim_set(st, "valid", cookie_valid);
-               rrddim_set(st, "invalid", cookie_invalid);
-               rrddim_set(st, "retransmits", cookie_retrans);
-               rrdset_done(st);
-       }
+        rrddim_set(st, "valid", cookie_valid);
+        rrddim_set(st, "invalid", cookie_invalid);
+        rrddim_set(st, "retransmits", cookie_retrans);
+        rrdset_done(st);
+    }
 
-       return 0;
+    return 0;
 }
index ac5baa4e6e3b281280ff631c35ddaee8a7052e4d..633565840dc6e2247d9f37d95e73a6c9e87936e1 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-
-#include "proc_self_mountinfo.h"
 
 // find the mount info with the given major:minor
 // in the supplied linked list of mountinfo structures
 struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor) {
-       struct mountinfo *mi;
+    struct mountinfo *mi;
 
-       for(mi = root; mi ; mi = mi->next)
-               if(mi->major == major && mi->minor == minor)
-                       return mi;
+    for(mi = root; mi ; mi = mi->next)
+        if(mi->major == major && mi->minor == minor)
+            return mi;
 
-       return NULL;
+    return NULL;
 }
 
 // find the mount info with the given filesystem and mount_source
 // in the supplied linked list of mountinfo structures
 struct mountinfo *mountinfo_find_by_filesystem_mount_source(struct mountinfo *root, const char *filesystem, const char *mount_source) {
-       struct mountinfo *mi;
-       uint32_t filesystem_hash = simple_hash(filesystem), mount_source_hash = simple_hash(mount_source);
-
-       for(mi = root; mi ; mi = mi->next)
-               if(mi->filesystem
-                               && mi->mount_source
-                               && mi->filesystem_hash == filesystem_hash
-                               && mi->mount_source_hash == mount_source_hash
-                               && !strcmp(mi->filesystem, filesystem)
-                               && !strcmp(mi->mount_source, mount_source))
-                       return mi;
-
-       return NULL;
+    struct mountinfo *mi;
+    uint32_t filesystem_hash = simple_hash(filesystem), mount_source_hash = simple_hash(mount_source);
+
+    for(mi = root; mi ; mi = mi->next)
+        if(mi->filesystem
+                && mi->mount_source
+                && mi->filesystem_hash == filesystem_hash
+                && mi->mount_source_hash == mount_source_hash
+                && !strcmp(mi->filesystem, filesystem)
+                && !strcmp(mi->mount_source, mount_source))
+            return mi;
+
+    return NULL;
 }
 
 struct mountinfo *mountinfo_find_by_filesystem_super_option(struct mountinfo *root, const char *filesystem, const char *super_options) {
-       struct mountinfo *mi;
-       uint32_t filesystem_hash = simple_hash(filesystem);
+    struct mountinfo *mi;
+    uint32_t filesystem_hash = simple_hash(filesystem);
 
-       size_t solen = strlen(super_options);
+    size_t solen = strlen(super_options);
 
-       for(mi = root; mi ; mi = mi->next)
-               if(mi->filesystem
-                               && mi->super_options
-                               && mi->filesystem_hash == filesystem_hash
-                               && !strcmp(mi->filesystem, filesystem)) {
+    for(mi = root; mi ; mi = mi->next)
+        if(mi->filesystem
+                && mi->super_options
+                && mi->filesystem_hash == filesystem_hash
+                && !strcmp(mi->filesystem, filesystem)) {
 
-                       // super_options is a comma separated list
-                       char *s = mi->super_options, *e;
-                       while(*s) {
-                               e = s + 1;
-                               while(*e && *e != ',') e++;
+            // super_options is a comma separated list
+            char *s = mi->super_options, *e;
+            while(*s) {
+                e = s + 1;
+                while(*e && *e != ',') e++;
 
-                               size_t len = e - s;
-                               if(len == solen && !strncmp(s, super_options, len))
-                                       return mi;
+                size_t len = e - s;
+                if(len == solen && !strncmp(s, super_options, len))
+                    return mi;
 
-                               if(*e == ',') s = ++e;
-                               else s = e;
-                       }
-               }
+                if(*e == ',') s = ++e;
+                else s = e;
+            }
+        }
 
-       return NULL;
+    return NULL;
 }
 
 
 // free a linked list of mountinfo structures
 void mountinfo_free(struct mountinfo *mi) {
-       if(unlikely(!mi))
-               return;
+    if(unlikely(!mi))
+        return;
 
-       if(likely(mi->next))
-               mountinfo_free(mi->next);
+    if(likely(mi->next))
+        mountinfo_free(mi->next);
 
-       if(mi->root) free(mi->root);
-       if(mi->mount_point) free(mi->mount_point);
-       if(mi->mount_options) free(mi->mount_options);
+    freez(mi->root);
+    freez(mi->mount_point);
+    freez(mi->mount_options);
 
 /*
-       if(mi->optional_fields_count) {
-               int i;
-               for(i = 0; i < mi->optional_fields_count ; i++)
-                       free(*mi->optional_fields[i]);
-       }
-       free(mi->optional_fields);
+    if(mi->optional_fields_count) {
+        int i;
+        for(i = 0; i < mi->optional_fields_count ; i++)
+            free(*mi->optional_fields[i]);
+    }
+    free(mi->optional_fields);
 */
-       free(mi->filesystem);
-       free(mi->mount_source);
-       free(mi->super_options);
-       free(mi);
+    freez(mi->filesystem);
+    freez(mi->mount_source);
+    freez(mi->super_options);
+    freez(mi);
 }
 
-static char *strdup_decoding_octal(const char *string) {
-       char *buffer = strdup(string);
-       if(!buffer) fatal("Cannot allocate memory.");
-
-       char *d = buffer;
-       const char *s = string;
-
-       while(*s) {
-               if(unlikely(*s == '\\')) {
-                       s++;
-                       if(likely(isdigit(*s) && isdigit(s[1]) && isdigit(s[2]))) {
-                               char c = *s++ - '0';
-                               c <<= 3;
-                               c |= *s++ - '0';
-                               c <<= 3;
-                               c |= *s++ - '0';
-                               *d++ = c;
-                       }
-                       else *d++ = '_';
-               }
-               else *d++ = *s++;
-       }
-       *d = '\0';
-
-       return buffer;
+static char *strdupz_decoding_octal(const char *string) {
+    char *buffer = strdupz(string);
+
+    char *d = buffer;
+    const char *s = string;
+
+    while(*s) {
+        if(unlikely(*s == '\\')) {
+            s++;
+            if(likely(isdigit(*s) && isdigit(s[1]) && isdigit(s[2]))) {
+                char c = *s++ - '0';
+                c <<= 3;
+                c |= *s++ - '0';
+                c <<= 3;
+                c |= *s++ - '0';
+                *d++ = c;
+            }
+            else *d++ = '_';
+        }
+        else *d++ = *s++;
+    }
+    *d = '\0';
+
+    return buffer;
 }
 
 // read the whole mountinfo into a linked list
 struct mountinfo *mountinfo_read() {
-       procfile *ff = NULL;
+    procfile *ff = NULL;
 
-       char filename[FILENAME_MAX + 1];
-       snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", global_host_prefix);
-       ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
-       if(!ff) {
-               snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", global_host_prefix);
-               ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
-               if(!ff) return NULL;
-       }
+    char filename[FILENAME_MAX + 1];
+    snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", global_host_prefix);
+    ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
+    if(!ff) {
+        snprintfz(filename, FILENAME_MAX, "%s/proc/1/mountinfo", global_host_prefix);
+        ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
+        if(!ff) return NULL;
+    }
 
-       ff = procfile_readall(ff);
-       if(!ff) return NULL;
+    ff = procfile_readall(ff);
+    if(!ff) return NULL;
 
-       struct mountinfo *root = NULL, *last = NULL, *mi = NULL;
+    struct mountinfo *root = NULL, *last = NULL, *mi = NULL;
 
-       unsigned long l, lines = procfile_lines(ff);
-       for(l = 0; l < lines ;l++) {
-               if(procfile_linewords(ff, l) < 5)
-                       continue;
+    unsigned long l, lines = procfile_lines(ff);
+    for(l = 0; l < lines ;l++) {
+        if(procfile_linewords(ff, l) < 5)
+            continue;
 
-               mi = malloc(sizeof(struct mountinfo));
-               if(unlikely(!mi)) fatal("Cannot allocate memory for mountinfo");
+        mi = mallocz(sizeof(struct mountinfo));
 
-               if(unlikely(!root))
-                       root = last = mi;
-               else
-                       last->next = mi;
+        if(unlikely(!root))
+            root = last = mi;
+        else
+            last->next = mi;
 
-               last = mi;
-               mi->next = NULL;
+        last = mi;
+        mi->next = NULL;
 
-               unsigned long w = 0;
-               mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
-               mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
+        unsigned long w = 0;
+        mi->id = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
+        mi->parentid = strtoul(procfile_lineword(ff, l, w), NULL, 10); w++;
 
-               char *major = procfile_lineword(ff, l, w), *minor; w++;
-               for(minor = major; *minor && *minor != ':' ;minor++) ;
-               *minor = '\0';
-               minor++;
+        char *major = procfile_lineword(ff, l, w), *minor; w++;
+        for(minor = major; *minor && *minor != ':' ;minor++) ;
+        *minor = '\0';
+        minor++;
 
-               mi->major = strtoul(major, NULL, 10);
-               mi->minor = strtoul(minor, NULL, 10);
+        mi->major = strtoul(major, NULL, 10);
+        mi->minor = strtoul(minor, NULL, 10);
 
-               mi->root = strdup(procfile_lineword(ff, l, w)); w++;
-               if(unlikely(!mi->root)) fatal("Cannot allocate memory");
-               mi->root_hash = simple_hash(mi->root);
+        mi->root = strdupz(procfile_lineword(ff, l, w)); w++;
+        mi->root_hash = simple_hash(mi->root);
 
-               mi->mount_point = strdup_decoding_octal(procfile_lineword(ff, l, w)); w++;
-               if(unlikely(!mi->mount_point)) fatal("Cannot allocate memory");
-               mi->mount_point_hash = simple_hash(mi->mount_point);
+        mi->mount_point = strdupz_decoding_octal(procfile_lineword(ff, l, w)); w++;
+        mi->mount_point_hash = simple_hash(mi->mount_point);
 
-               mi->mount_options = strdup(procfile_lineword(ff, l, w)); w++;
-               if(unlikely(!mi->mount_options)) fatal("Cannot allocate memory");
+        mi->mount_options = strdupz(procfile_lineword(ff, l, w)); w++;
 
-               // count the optional fields
+        // count the optional fields
 /*
-               unsigned long wo = w;
+        unsigned long wo = w;
 */
-               mi->optional_fields_count = 0;
-               char *s = procfile_lineword(ff, l, w);
-               while(*s && *s != '-') {
-                       w++;
-                       s = procfile_lineword(ff, l, w);
-                       mi->optional_fields_count++;
-               }
+        mi->optional_fields_count = 0;
+        char *s = procfile_lineword(ff, l, w);
+        while(*s && *s != '-') {
+            w++;
+            s = procfile_lineword(ff, l, w);
+            mi->optional_fields_count++;
+        }
 
 /*
-               if(unlikely(mi->optional_fields_count)) {
-                       // we have some optional fields
-                       // read them into a new array of pointers;
-
-                       mi->optional_fields = malloc(mi->optional_fields_count * sizeof(char *));
-                       if(unlikely(!mi->optional_fields))
-                               fatal("Cannot allocate memory for %d mountinfo optional fields", mi->optional_fields_count);
-
-                       int i;
-                       for(i = 0; i < mi->optional_fields_count ; i++) {
-                               *mi->optional_fields[wo] = strdup(procfile_lineword(ff, l, w));
-                               if(!mi->optional_fields[wo]) fatal("Cannot allocate memory");
-                               wo++;
-                       }
-               }
-               else
-                       mi->optional_fields = NULL;
+        if(unlikely(mi->optional_fields_count)) {
+            // we have some optional fields
+            // read them into a new array of pointers;
+
+            mi->optional_fields = malloc(mi->optional_fields_count * sizeof(char *));
+            if(unlikely(!mi->optional_fields))
+                fatal("Cannot allocate memory for %d mountinfo optional fields", mi->optional_fields_count);
+
+            int i;
+            for(i = 0; i < mi->optional_fields_count ; i++) {
+                *mi->optional_fields[wo] = strdup(procfile_lineword(ff, l, w));
+                if(!mi->optional_fields[wo]) fatal("Cannot allocate memory");
+                wo++;
+            }
+        }
+        else
+            mi->optional_fields = NULL;
 */
 
-               if(likely(*s == '-')) {
-                       w++;
+        if(likely(*s == '-')) {
+            w++;
 
-                       mi->filesystem = strdup(procfile_lineword(ff, l, w)); w++;
-                       if(!mi->filesystem) fatal("Cannot allocate memory");
-                       mi->filesystem_hash = simple_hash(mi->filesystem);
+            mi->filesystem = strdupz(procfile_lineword(ff, l, w)); w++;
+            mi->filesystem_hash = simple_hash(mi->filesystem);
 
-                       mi->mount_source = strdup(procfile_lineword(ff, l, w)); w++;
-                       if(!mi->mount_source) fatal("Cannot allocate memory");
-                       mi->mount_source_hash = simple_hash(mi->mount_source);
+            mi->mount_source = strdupz(procfile_lineword(ff, l, w)); w++;
+            mi->mount_source_hash = simple_hash(mi->mount_source);
 
-                       mi->super_options = strdup(procfile_lineword(ff, l, w)); w++;
-                       if(!mi->super_options) fatal("Cannot allocate memory");
-               }
-               else {
-                       mi->filesystem = NULL;
-                       mi->mount_source = NULL;
-                       mi->super_options = NULL;
-               }
+            mi->super_options = strdupz(procfile_lineword(ff, l, w)); w++;
+        }
+        else {
+            mi->filesystem = NULL;
+            mi->mount_source = NULL;
+            mi->super_options = NULL;
+        }
 
 /*
-               info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'",
-                    mi->id,
-                    mi->parentid,
-                    mi->major,
-                    mi->minor,
-                    mi->root,
-                    mi->mount_point,
-                    mi->mount_options,
-                    mi->filesystem,
-                    mi->mount_source,
-                    mi->super_options
-               );
+        info("MOUNTINFO: %u %u %u:%u root '%s', mount point '%s', mount options '%s', filesystem '%s', mount source '%s', super options '%s'",
+             mi->id,
+             mi->parentid,
+             mi->major,
+             mi->minor,
+             mi->root,
+             mi->mount_point,
+             mi->mount_options,
+             mi->filesystem,
+             mi->mount_source,
+             mi->super_options
+        );
 */
-       }
+    }
 
-       procfile_close(ff);
-       return root;
+    procfile_close(ff);
+    return root;
 }
index 51712a58aa792dfc92ef981b1ef6390af54fc379..c2d9688c19e836e221f7e0f445fc961abbad3b69 100644 (file)
@@ -1,35 +1,33 @@
-#include "procfile.h"
-
 #ifndef NETDATA_PROC_SELF_MOUNTINFO_H
 #define NETDATA_PROC_SELF_MOUNTINFO_H 1
 
 struct mountinfo {
-       long id;                // mount ID: unique identifier of the mount (may be reused after umount(2)).
-       long parentid;          // parent ID: ID of parent mount (or of self for the top of the mount tree).
-       unsigned long major;    // major:minor: value of st_dev for files on filesystem (see stat(2)).
-       unsigned long minor;
+    long id;                // mount ID: unique identifier of the mount (may be reused after umount(2)).
+    long parentid;          // parent ID: ID of parent mount (or of self for the top of the mount tree).
+    unsigned long major;    // major:minor: value of st_dev for files on filesystem (see stat(2)).
+    unsigned long minor;
 
-       char *root;             // root: root of the mount within the filesystem.
-       uint32_t root_hash;
+    char *root;             // root: root of the mount within the filesystem.
+    uint32_t root_hash;
 
-       char *mount_point;      // mount point: mount point relative to the process's root.
-       uint32_t mount_point_hash;
+    char *mount_point;      // mount point: mount point relative to the process's root.
+    uint32_t mount_point_hash;
 
-       char *mount_options;    // mount options: per-mount options.
+    char *mount_options;    // mount options: per-mount options.
 
-       int optional_fields_count;
+    int optional_fields_count;
 /*
-       char ***optional_fields; // optional fields: zero or more fields of the form "tag[:value]".
+    char ***optional_fields; // optional fields: zero or more fields of the form "tag[:value]".
 */
-       char *filesystem;       // filesystem type: name of filesystem in the form "type[.subtype]".
-       uint32_t filesystem_hash;
+    char *filesystem;       // filesystem type: name of filesystem in the form "type[.subtype]".
+    uint32_t filesystem_hash;
 
-       char *mount_source;     // mount source: filesystem-specific information or "none".
-       uint32_t mount_source_hash;
+    char *mount_source;     // mount source: filesystem-specific information or "none".
+    uint32_t mount_source_hash;
 
-       char *super_options;    // super options: per-superblock options.
+    char *super_options;    // super options: per-superblock options.
 
-       struct mountinfo *next;
+    struct mountinfo *next;
 };
 
 extern struct mountinfo *mountinfo_find(struct mountinfo *root, unsigned long major, unsigned long minor);
index 96b5d3d30224fa42b18ce5917d838b4135bc7ee3..a5165040acecc9da79ba349c001c7a2729800bbc 100644 (file)
@@ -1,26 +1,13 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
-#include "log.h"
 
 #define MAX_INTERRUPT_NAME 50
 
 struct interrupt {
-       int used;
-       char *id;
-       char name[MAX_INTERRUPT_NAME + 1];
-       unsigned long long total;
-       unsigned long long value[];
+    int used;
+    char *id;
+    char name[MAX_INTERRUPT_NAME + 1];
+    unsigned long long total;
+    unsigned long long value[];
 };
 
 // since each interrupt is variable in size
@@ -31,160 +18,157 @@ struct interrupt {
 #define irrindex(base, line, cpus) ((struct interrupt *)&((char *)(base))[line * recordsize(cpus)])
 
 static inline struct interrupt *get_interrupts_array(int lines, int cpus) {
-       static struct interrupt *irrs = NULL;
-       static int allocated = 0;
-
-       if(lines < allocated) return irrs;
-       else {
-               irrs = (struct interrupt *)realloc(irrs, lines * recordsize(cpus));
-               if(!irrs)
-                       fatal("Cannot allocate memory for %d interrupts", lines);
+    static struct interrupt *irrs = NULL;
+    static int allocated = 0;
 
-               allocated = lines;
-       }
+    if(lines < allocated) return irrs;
+    else {
+        irrs = (struct interrupt *)reallocz(irrs, lines * recordsize(cpus));
+        allocated = lines;
+    }
 
-       return irrs;
+    return irrs;
 }
 
 int do_proc_softirqs(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int cpus = -1, do_per_core = -1;
+    static procfile *ff = NULL;
+    static int cpus = -1, do_per_core = -1;
 
-       struct interrupt *irrs = NULL;
-
-       if(dt) {};
+    struct interrupt *irrs = NULL;
+
+    if(dt) {};
 
-       if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1);
-
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/softirqs");
-               ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words = procfile_linewords(ff, 0), w;
-
-       if(!lines) {
-               error("Cannot read /proc/softirqs, zero lines reported.");
-               return 1;
-       }
-
-       // find how many CPUs are there
-       if(cpus == -1) {
-               cpus = 0;
-               for(w = 0; w < words ; w++) {
-                       if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
-                               cpus++;
-               }
-       }
-
-       if(!cpus) {
-               error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs");
-               return 1;
-       }
-
-       // allocate the size we need;
-       irrs = get_interrupts_array(lines, cpus);
-       irrs[0].used = 0;
-
-       // loop through all lines
-       for(l = 1; l < lines ;l++) {
-               struct interrupt *irr = irrindex(irrs, l, cpus);
-               irr->used = 0;
-               irr->total = 0;
-
-               words = procfile_linewords(ff, l);
-               if(!words) continue;
-
-               irr->id = procfile_lineword(ff, l, 0);
-               if(!irr->id || !irr->id[0]) continue;
-
-               int idlen = strlen(irr->id);
-               if(irr->id[idlen - 1] == ':')
-                       irr->id[idlen - 1] = '\0';
-
-               int c;
-               for(c = 0; c < cpus ;c++) {
-                       if((c + 1) < (int)words)
-                               irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
-                       else
-                               irr->value[c] = 0;
-
-                       irr->total += irr->value[c];
-               }
-
-               strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
-
-               irr->used = 1;
-       }
-
-       RRDSET *st;
-
-       // --------------------------------------------------------------------
-
-       st = rrdset_find_bytype("system", "softirqs");
-       if(!st) {
-               st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED);
-
-               for(l = 0; l < lines ;l++) {
-                       struct interrupt *irr = irrindex(irrs, l, cpus);
-                       if(!irr->used) continue;
-                       rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
-               }
-       }
-       else rrdset_next(st);
-
-       for(l = 0; l < lines ;l++) {
-               struct interrupt *irr = irrindex(irrs, l, cpus);
-               if(!irr->used) continue;
-               rrddim_set(st, irr->id, irr->total);
-       }
-       rrdset_done(st);
-
-       if(do_per_core) {
-               int c;
-
-               for(c = 0; c < cpus ; c++) {
-                       char id[256+1];
-                       snprintfz(id, 256, "cpu%d_softirqs", c);
-
-                       st = rrdset_find_bytype("cpu", id);
-                       if(!st) {
-                               // find if everything is zero
-                               unsigned long long core_sum = 0 ;
-                               for(l = 0; l < lines ;l++) {
-                                       struct interrupt *irr = irrindex(irrs, l, cpus);
-                                       if(!irr->used) continue;
-                                       core_sum += irr->value[c];
-                               }
-                               if(core_sum == 0) continue; // try next core
-
-                               char name[256+1], title[256+1];
-                               snprintfz(name, 256, "cpu%d_softirqs", c);
-                               snprintfz(title, 256, "CPU%d softirqs", c);
-                               st = rrdset_create("cpu", id, name, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
-
-                               for(l = 0; l < lines ;l++) {
-                                       struct interrupt *irr = irrindex(irrs, l, cpus);
-                                       if(!irr->used) continue;
-                                       rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                       }
-                       else rrdset_next(st);
-
-                       for(l = 0; l < lines ;l++) {
-                               struct interrupt *irr = irrindex(irrs, l, cpus);
-                               if(!irr->used) continue;
-                               rrddim_set(st, irr->id, irr->value[c]);
-                       }
-                       rrdset_done(st);
-               }
-       }
-
-       return 0;
+    if(do_per_core == -1) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1);
+
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/softirqs");
+        ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
+
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words = procfile_linewords(ff, 0), w;
+
+    if(!lines) {
+        error("Cannot read /proc/softirqs, zero lines reported.");
+        return 1;
+    }
+
+    // find how many CPUs are there
+    if(cpus == -1) {
+        cpus = 0;
+        for(w = 0; w < words ; w++) {
+            if(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)
+                cpus++;
+        }
+    }
+
+    if(!cpus) {
+        error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs");
+        return 1;
+    }
+
+    // allocate the size we need;
+    irrs = get_interrupts_array(lines, cpus);
+    irrs[0].used = 0;
+
+    // loop through all lines
+    for(l = 1; l < lines ;l++) {
+        struct interrupt *irr = irrindex(irrs, l, cpus);
+        irr->used = 0;
+        irr->total = 0;
+
+        words = procfile_linewords(ff, l);
+        if(!words) continue;
+
+        irr->id = procfile_lineword(ff, l, 0);
+        if(!irr->id || !irr->id[0]) continue;
+
+        int idlen = strlen(irr->id);
+        if(irr->id[idlen - 1] == ':')
+            irr->id[idlen - 1] = '\0';
+
+        int c;
+        for(c = 0; c < cpus ;c++) {
+            if((c + 1) < (int)words)
+                irr->value[c] = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10);
+            else
+                irr->value[c] = 0;
+
+            irr->total += irr->value[c];
+        }
+
+        strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME);
+
+        irr->used = 1;
+    }
+
+    RRDSET *st;
+
+    // --------------------------------------------------------------------
+
+    st = rrdset_find_bytype("system", "softirqs");
+    if(!st) {
+        st = rrdset_create("system", "softirqs", NULL, "softirqs", NULL, "System softirqs", "softirqs/s", 950, update_every, RRDSET_TYPE_STACKED);
+
+        for(l = 0; l < lines ;l++) {
+            struct interrupt *irr = irrindex(irrs, l, cpus);
+            if(!irr->used) continue;
+            rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+        }
+    }
+    else rrdset_next(st);
+
+    for(l = 0; l < lines ;l++) {
+        struct interrupt *irr = irrindex(irrs, l, cpus);
+        if(!irr->used) continue;
+        rrddim_set(st, irr->id, irr->total);
+    }
+    rrdset_done(st);
+
+    if(do_per_core) {
+        int c;
+
+        for(c = 0; c < cpus ; c++) {
+            char id[256+1];
+            snprintfz(id, 256, "cpu%d_softirqs", c);
+
+            st = rrdset_find_bytype("cpu", id);
+            if(!st) {
+                // find if everything is zero
+                unsigned long long core_sum = 0 ;
+                for(l = 0; l < lines ;l++) {
+                    struct interrupt *irr = irrindex(irrs, l, cpus);
+                    if(!irr->used) continue;
+                    core_sum += irr->value[c];
+                }
+                if(core_sum == 0) continue; // try next core
+
+                char name[256+1], title[256+1];
+                snprintfz(name, 256, "cpu%d_softirqs", c);
+                snprintfz(title, 256, "CPU%d softirqs", c);
+                st = rrdset_create("cpu", id, name, "softirqs", "cpu.softirqs", title, "softirqs/s", 3000 + c, update_every, RRDSET_TYPE_STACKED);
+
+                for(l = 0; l < lines ;l++) {
+                    struct interrupt *irr = irrindex(irrs, l, cpus);
+                    if(!irr->used) continue;
+                    rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL);
+                }
+            }
+            else rrdset_next(st);
+
+            for(l = 0; l < lines ;l++) {
+                struct interrupt *irr = irrindex(irrs, l, cpus);
+                if(!irr->used) continue;
+                rrddim_set(st, irr->id, irr->value[c]);
+            }
+            rrdset_done(st);
+        }
+    }
+
+    return 0;
 }
index a1e9c2f520c10fa72ca3658fd51a900f7601b0a9..88cb820b38d16f5808dc4a7967efa87c39f13af8 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 int do_proc_stat(int update_every, unsigned long long dt) {
-       (void)dt;
-
-       static procfile *ff = NULL;
-       static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1;
-       static uint32_t hash_intr, hash_ctxt, hash_processes, hash_procs_running, hash_procs_blocked;
-
-       if(unlikely(do_cpu == -1)) {
-               do_cpu                  = config_get_boolean("plugin:proc:/proc/stat", "cpu utilization", 1);
-               do_cpu_cores    = config_get_boolean("plugin:proc:/proc/stat", "per cpu core utilization", 1);
-               do_interrupts   = config_get_boolean("plugin:proc:/proc/stat", "cpu interrupts", 1);
-               do_context              = config_get_boolean("plugin:proc:/proc/stat", "context switches", 1);
-               do_forks                = config_get_boolean("plugin:proc:/proc/stat", "processes started", 1);
-               do_processes    = config_get_boolean("plugin:proc:/proc/stat", "processes running", 1);
-
-               hash_intr = simple_hash("intr");
-               hash_ctxt = simple_hash("ctxt");
-               hash_processes = simple_hash("processes");
-               hash_procs_running = simple_hash("procs_running");
-               hash_procs_blocked = simple_hash("procs_blocked");
-       }
-
-       if(unlikely(!ff)) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/stat");
-               ff = procfile_open(config_get("plugin:proc:/proc/stat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-               if(unlikely(!ff)) return 1;
-       }
-
-       ff = procfile_readall(ff);
-       if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
-
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
-
-       unsigned long long processes = 0, running = 0 , blocked = 0;
-       RRDSET *st;
-
-       for(l = 0; l < lines ;l++) {
-               char *row_key = procfile_lineword(ff, l, 0);
-               uint32_t hash = simple_hash(row_key);
-
-               // faster strncmp(row_key, "cpu", 3) == 0
-               if(likely(row_key[0] == 'c' && row_key[1] == 'p' && row_key[2] == 'u')) {
-                       words = procfile_linewords(ff, l);
-                       if(unlikely(words < 9)) {
-                               error("Cannot read /proc/stat cpu line. Expected 9 params, read %u.", words);
-                               continue;
-                       }
-
-                       char *id;
-                       unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
-
-                       id                      = row_key;
-                       user            = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-                       nice            = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
-                       system          = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
-                       idle            = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
-                       iowait          = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
-                       irq                     = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
-                       softirq         = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
-                       steal           = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
-
-                       guest           = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
-                       user -= guest;
-
-                       guest_nice      = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
-                       nice -= guest_nice;
-
-                       char *title, *type, *context, *family;
-                       long priority;
-                       int isthistotal;
-
-                       if(unlikely(strcmp(id, "cpu")) == 0) {
-                               title = "Total CPU utilization";
-                               type = "system";
-                               context = "system.cpu";
-                               family = id;
-                               priority = 100;
-                               isthistotal = 1;
-                       }
-                       else {
-                               title = "Core utilization";
-                               type = "cpu";
-                               context = "cpu.cpu";
-                               family = "utilization";
-                               priority = 1000;
-                               isthistotal = 0;
-                       }
-
-                       if(likely((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores))) {
-                               st = rrdset_find_bytype(type, id);
-                               if(unlikely(!st)) {
-                                       st = rrdset_create(type, id, NULL, family, context, title, "percentage", priority, update_every, RRDSET_TYPE_STACKED);
-
-                                       long multiplier = 1;
-                                       long divisor = 1; // sysconf(_SC_CLK_TCK);
-
-                                       rrddim_add(st, "guest_nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "guest", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "steal", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "softirq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "irq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "user", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "system", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_add(st, "iowait", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-
-                                       rrddim_add(st, "idle", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-                                       rrddim_hide(st, "idle");
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "user", user);
-                               rrddim_set(st, "nice", nice);
-                               rrddim_set(st, "system", system);
-                               rrddim_set(st, "idle", idle);
-                               rrddim_set(st, "iowait", iowait);
-                               rrddim_set(st, "irq", irq);
-                               rrddim_set(st, "softirq", softirq);
-                               rrddim_set(st, "steal", steal);
-                               rrddim_set(st, "guest", guest);
-                               rrddim_set(st, "guest_nice", guest_nice);
-                               rrdset_done(st);
-                       }
-               }
-               else if(hash == hash_intr && strcmp(row_key, "intr") == 0) {
-                       unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-
-                       // --------------------------------------------------------------------
-
-                       if(likely(do_interrupts)) {
-                               st = rrdset_find_bytype("system", "intr");
-                               if(unlikely(!st)) {
-                                       st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
-                                       st->isdetail = 1;
-
-                                       rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "interrupts", value);
-                               rrdset_done(st);
-                       }
-               }
-               else if(hash == hash_ctxt && strcmp(row_key, "ctxt") == 0) {
-                       unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-
-                       // --------------------------------------------------------------------
-
-                       if(likely(do_context)) {
-                               st = rrdset_find_bytype("system", "ctxt");
-                               if(unlikely(!st)) {
-                                       st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "switches", value);
-                               rrdset_done(st);
-                       }
-               }
-               else if(hash == hash_processes && !processes && strcmp(row_key, "processes") == 0) {
-                       processes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-               }
-               else if(hash == hash_procs_running && !running && strcmp(row_key, "procs_running") == 0) {
-                       running = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-               }
-               else if(hash == hash_procs_blocked && !blocked && strcmp(row_key, "procs_blocked") == 0) {
-                       blocked = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
-               }
-       }
-
-       // --------------------------------------------------------------------
-
-       if(likely(do_forks)) {
-               st = rrdset_find_bytype("system", "forks");
-               if(unlikely(!st)) {
-                       st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
-                       st->isdetail = 1;
-
-                       rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "started", processes);
-               rrdset_done(st);
-       }
-
-       // --------------------------------------------------------------------
-
-       if(likely(do_processes)) {
-               st = rrdset_find_bytype("system", "processes");
-               if(unlikely(!st)) {
-                       st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
-
-                       rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                       rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
-               }
-               else rrdset_next(st);
-
-               rrddim_set(st, "running", running);
-               rrddim_set(st, "blocked", blocked);
-               rrdset_done(st);
-       }
-
-       return 0;
+    (void)dt;
+
+    static procfile *ff = NULL;
+    static int do_cpu = -1, do_cpu_cores = -1, do_interrupts = -1, do_context = -1, do_forks = -1, do_processes = -1;
+    static uint32_t hash_intr, hash_ctxt, hash_processes, hash_procs_running, hash_procs_blocked;
+
+    if(unlikely(do_cpu == -1)) {
+        do_cpu          = config_get_boolean("plugin:proc:/proc/stat", "cpu utilization", 1);
+        do_cpu_cores    = config_get_boolean("plugin:proc:/proc/stat", "per cpu core utilization", 1);
+        do_interrupts   = config_get_boolean("plugin:proc:/proc/stat", "cpu interrupts", 1);
+        do_context      = config_get_boolean("plugin:proc:/proc/stat", "context switches", 1);
+        do_forks        = config_get_boolean("plugin:proc:/proc/stat", "processes started", 1);
+        do_processes    = config_get_boolean("plugin:proc:/proc/stat", "processes running", 1);
+
+        hash_intr = simple_hash("intr");
+        hash_ctxt = simple_hash("ctxt");
+        hash_processes = simple_hash("processes");
+        hash_procs_running = simple_hash("procs_running");
+        hash_procs_blocked = simple_hash("procs_blocked");
+    }
+
+    if(unlikely(!ff)) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/stat");
+        ff = procfile_open(config_get("plugin:proc:/proc/stat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+        if(unlikely(!ff)) return 1;
+    }
+
+    ff = procfile_readall(ff);
+    if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time
+
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
+
+    unsigned long long processes = 0, running = 0 , blocked = 0;
+    RRDSET *st;
+
+    for(l = 0; l < lines ;l++) {
+        char *row_key = procfile_lineword(ff, l, 0);
+        uint32_t hash = simple_hash(row_key);
+
+        // faster strncmp(row_key, "cpu", 3) == 0
+        if(likely(row_key[0] == 'c' && row_key[1] == 'p' && row_key[2] == 'u')) {
+            words = procfile_linewords(ff, l);
+            if(unlikely(words < 9)) {
+                error("Cannot read /proc/stat cpu line. Expected 9 params, read %u.", words);
+                continue;
+            }
+
+            char *id;
+            unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guest_nice = 0;
+
+            id          = row_key;
+            user        = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+            nice        = strtoull(procfile_lineword(ff, l, 2), NULL, 10);
+            system      = strtoull(procfile_lineword(ff, l, 3), NULL, 10);
+            idle        = strtoull(procfile_lineword(ff, l, 4), NULL, 10);
+            iowait      = strtoull(procfile_lineword(ff, l, 5), NULL, 10);
+            irq         = strtoull(procfile_lineword(ff, l, 6), NULL, 10);
+            softirq     = strtoull(procfile_lineword(ff, l, 7), NULL, 10);
+            steal       = strtoull(procfile_lineword(ff, l, 8), NULL, 10);
+
+            guest       = strtoull(procfile_lineword(ff, l, 9), NULL, 10);
+            user -= guest;
+
+            guest_nice  = strtoull(procfile_lineword(ff, l, 10), NULL, 10);
+            nice -= guest_nice;
+
+            char *title, *type, *context, *family;
+            long priority;
+            int isthistotal;
+
+            if(unlikely(strcmp(id, "cpu")) == 0) {
+                title = "Total CPU utilization";
+                type = "system";
+                context = "system.cpu";
+                family = id;
+                priority = 100;
+                isthistotal = 1;
+            }
+            else {
+                title = "Core utilization";
+                type = "cpu";
+                context = "cpu.cpu";
+                family = "utilization";
+                priority = 1000;
+                isthistotal = 0;
+            }
+
+            if(likely((isthistotal && do_cpu) || (!isthistotal && do_cpu_cores))) {
+                st = rrdset_find_bytype(type, id);
+                if(unlikely(!st)) {
+                    st = rrdset_create(type, id, NULL, family, context, title, "percentage", priority, update_every, RRDSET_TYPE_STACKED);
+
+                    long multiplier = 1;
+                    long divisor = 1; // sysconf(_SC_CLK_TCK);
+
+                    rrddim_add(st, "guest_nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "guest", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "steal", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "softirq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "irq", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "user", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "system", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "nice", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_add(st, "iowait", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+
+                    rrddim_add(st, "idle", NULL, multiplier, divisor, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+                    rrddim_hide(st, "idle");
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "user", user);
+                rrddim_set(st, "nice", nice);
+                rrddim_set(st, "system", system);
+                rrddim_set(st, "idle", idle);
+                rrddim_set(st, "iowait", iowait);
+                rrddim_set(st, "irq", irq);
+                rrddim_set(st, "softirq", softirq);
+                rrddim_set(st, "steal", steal);
+                rrddim_set(st, "guest", guest);
+                rrddim_set(st, "guest_nice", guest_nice);
+                rrdset_done(st);
+            }
+        }
+        else if(hash == hash_intr && strcmp(row_key, "intr") == 0) {
+            unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+
+            // --------------------------------------------------------------------
+
+            if(likely(do_interrupts)) {
+                st = rrdset_find_bytype("system", "intr");
+                if(unlikely(!st)) {
+                    st = rrdset_create("system", "intr", NULL, "interrupts", NULL, "CPU Interrupts", "interrupts/s", 900, update_every, RRDSET_TYPE_LINE);
+                    st->isdetail = 1;
+
+                    rrddim_add(st, "interrupts", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "interrupts", value);
+                rrdset_done(st);
+            }
+        }
+        else if(hash == hash_ctxt && strcmp(row_key, "ctxt") == 0) {
+            unsigned long long value = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+
+            // --------------------------------------------------------------------
+
+            if(likely(do_context)) {
+                st = rrdset_find_bytype("system", "ctxt");
+                if(unlikely(!st)) {
+                    st = rrdset_create("system", "ctxt", NULL, "processes", NULL, "CPU Context Switches", "context switches/s", 800, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "switches", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "switches", value);
+                rrdset_done(st);
+            }
+        }
+        else if(hash == hash_processes && !processes && strcmp(row_key, "processes") == 0) {
+            processes = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+        }
+        else if(hash == hash_procs_running && !running && strcmp(row_key, "procs_running") == 0) {
+            running = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+        }
+        else if(hash == hash_procs_blocked && !blocked && strcmp(row_key, "procs_blocked") == 0) {
+            blocked = strtoull(procfile_lineword(ff, l, 1), NULL, 10);
+        }
+    }
+
+    // --------------------------------------------------------------------
+
+    if(likely(do_forks)) {
+        st = rrdset_find_bytype("system", "forks");
+        if(unlikely(!st)) {
+            st = rrdset_create("system", "forks", NULL, "processes", NULL, "Started Processes", "processes/s", 700, update_every, RRDSET_TYPE_LINE);
+            st->isdetail = 1;
+
+            rrddim_add(st, "started", NULL, 1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "started", processes);
+        rrdset_done(st);
+    }
+
+    // --------------------------------------------------------------------
+
+    if(likely(do_processes)) {
+        st = rrdset_find_bytype("system", "processes");
+        if(unlikely(!st)) {
+            st = rrdset_create("system", "processes", NULL, "processes", NULL, "System Processes", "processes", 600, update_every, RRDSET_TYPE_LINE);
+
+            rrddim_add(st, "running", NULL, 1, 1, RRDDIM_ABSOLUTE);
+            rrddim_add(st, "blocked", NULL, -1, 1, RRDDIM_ABSOLUTE);
+        }
+        else rrdset_next(st);
+
+        rrddim_set(st, "running", running);
+        rrddim_set(st, "blocked", blocked);
+        rrdset_done(st);
+    }
+
+    return 0;
 }
index d7d1e826110ef937f612eda3b2c4e7e38857976d..9515dad61693cf31d5372a7e160da1f55fcf2788 100644 (file)
@@ -1,41 +1,31 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 int do_proc_sys_kernel_random_entropy_avail(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
+    static procfile *ff = NULL;
 
-       if(dt) {} ;
+    if(dt) {} ;
 
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/kernel/random/entropy_avail");
-               ff = procfile_open(config_get("plugin:proc:/proc/sys/kernel/random/entropy_avail", "filename to monitor", filename), "", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/sys/kernel/random/entropy_avail");
+        ff = procfile_open(config_get("plugin:proc:/proc/sys/kernel/random/entropy_avail", "filename to monitor", filename), "", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
 
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
 
-       unsigned long long entropy = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
+    unsigned long long entropy = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
 
-       RRDSET *st = rrdset_find_bytype("system", "entropy");
-       if(!st) {
-               st = rrdset_create("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000, update_every, RRDSET_TYPE_LINE);
-               rrddim_add(st, "entropy", NULL, 1, 1, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(st);
+    RRDSET *st = rrdset_find_bytype("system", "entropy");
+    if(!st) {
+        st = rrdset_create("system", "entropy", NULL, "entropy", NULL, "Available Entropy", "entropy", 1000, update_every, RRDSET_TYPE_LINE);
+        rrddim_add(st, "entropy", NULL, 1, 1, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(st);
 
-       rrddim_set(st, "entropy", entropy);
-       rrdset_done(st);
+    rrddim_set(st, "entropy", entropy);
+    rrdset_done(st);
 
-       return 0;
+    return 0;
 }
index c69b389b6c875af18105087321046f2bfba41d47..f25d50c50b4ff1c8674a426403471d9c6cfaeb90 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
 int do_proc_vmstat(int update_every, unsigned long long dt) {
-       static procfile *ff = NULL;
-       static int do_swapio = -1, do_io = -1, do_pgfaults = -1, gen_hashes = -1;
+    static procfile *ff = NULL;
+    static int do_swapio = -1, do_io = -1, do_pgfaults = -1, gen_hashes = -1;
 
-       // static uint32_t hash_allocstall = -1;
-       // static uint32_t hash_compact_blocks_moved = -1;
-       // static uint32_t hash_compact_fail = -1;
-       // static uint32_t hash_compact_pagemigrate_failed = -1;
-       // static uint32_t hash_compact_pages_moved = -1;
-       // static uint32_t hash_compact_stall = -1;
-       // static uint32_t hash_compact_success = -1;
-       // static uint32_t hash_htlb_buddy_alloc_fail = -1;
-       // static uint32_t hash_htlb_buddy_alloc_success = -1;
-       // static uint32_t hash_kswapd_high_wmark_hit_quickly = -1;
-       // static uint32_t hash_kswapd_inodesteal = -1;
-       // static uint32_t hash_kswapd_low_wmark_hit_quickly = -1;
-       // static uint32_t hash_kswapd_skip_congestion_wait = -1;
-       // static uint32_t hash_nr_active_anon = -1;
-       // static uint32_t hash_nr_active_file = -1;
-       // static uint32_t hash_nr_anon_pages = -1;
-       // static uint32_t hash_nr_anon_transparent_hugepages = -1;
-       // static uint32_t hash_nr_bounce = -1;
-       // static uint32_t hash_nr_dirtied = -1;
-       // static uint32_t hash_nr_dirty = -1;
-       // static uint32_t hash_nr_dirty_background_threshold = -1;
-       // static uint32_t hash_nr_dirty_threshold = -1;
-       // static uint32_t hash_nr_file_pages = -1;
-       // static uint32_t hash_nr_free_pages = -1;
-       // static uint32_t hash_nr_inactive_anon = -1;
-       // static uint32_t hash_nr_inactive_file = -1;
-       // static uint32_t hash_nr_isolated_anon = -1;
-       // static uint32_t hash_nr_isolated_file = -1;
-       // static uint32_t hash_nr_kernel_stack = -1;
-       // static uint32_t hash_nr_mapped = -1;
-       // static uint32_t hash_nr_mlock = -1;
-       // static uint32_t hash_nr_page_table_pages = -1;
-       // static uint32_t hash_nr_shmem = -1;
-       // static uint32_t hash_nr_slab_reclaimable = -1;
-       // static uint32_t hash_nr_slab_unreclaimable = -1;
-       // static uint32_t hash_nr_unevictable = -1;
-       // static uint32_t hash_nr_unstable = -1;
-       // static uint32_t hash_nr_vmscan_immediate_reclaim = -1;
-       // static uint32_t hash_nr_vmscan_write = -1;
-       // static uint32_t hash_nr_writeback = -1;
-       // static uint32_t hash_nr_writeback_temp = -1;
-       // static uint32_t hash_nr_written = -1;
-       // static uint32_t hash_pageoutrun = -1;
-       // static uint32_t hash_pgactivate = -1;
-       // static uint32_t hash_pgalloc_dma = -1;
-       // static uint32_t hash_pgalloc_dma32 = -1;
-       // static uint32_t hash_pgalloc_movable = -1;
-       // static uint32_t hash_pgalloc_normal = -1;
-       // static uint32_t hash_pgdeactivate = -1;
-       static uint32_t hash_pgfault = -1;
-       // static uint32_t hash_pgfree = -1;
-       // static uint32_t hash_pginodesteal = -1;
-       static uint32_t hash_pgmajfault = -1;
-       static uint32_t hash_pgpgin = -1;
-       static uint32_t hash_pgpgout = -1;
-       // static uint32_t hash_pgrefill_dma = -1;
-       // static uint32_t hash_pgrefill_dma32 = -1;
-       // static uint32_t hash_pgrefill_movable = -1;
-       // static uint32_t hash_pgrefill_normal = -1;
-       // static uint32_t hash_pgrotated = -1;
-       // static uint32_t hash_pgscan_direct_dma = -1;
-       // static uint32_t hash_pgscan_direct_dma32 = -1;
-       // static uint32_t hash_pgscan_direct_movable = -1;
-       // static uint32_t hash_pgscan_direct_normal = -1;
-       // static uint32_t hash_pgscan_kswapd_dma = -1;
-       // static uint32_t hash_pgscan_kswapd_dma32 = -1;
-       // static uint32_t hash_pgscan_kswapd_movable = -1;
-       // static uint32_t hash_pgscan_kswapd_normal = -1;
-       // static uint32_t hash_pgsteal_direct_dma = -1;
-       // static uint32_t hash_pgsteal_direct_dma32 = -1;
-       // static uint32_t hash_pgsteal_direct_movable = -1;
-       // static uint32_t hash_pgsteal_direct_normal = -1;
-       // static uint32_t hash_pgsteal_kswapd_dma = -1;
-       // static uint32_t hash_pgsteal_kswapd_dma32 = -1;
-       // static uint32_t hash_pgsteal_kswapd_movable = -1;
-       // static uint32_t hash_pgsteal_kswapd_normal = -1;
-       static uint32_t hash_pswpin = -1;
-       static uint32_t hash_pswpout = -1;
-       // static uint32_t hash_slabs_scanned = -1;
-       // static uint32_t hash_thp_collapse_alloc = -1;
-       // static uint32_t hash_thp_collapse_alloc_failed = -1;
-       // static uint32_t hash_thp_fault_alloc = -1;
-       // static uint32_t hash_thp_fault_fallback = -1;
-       // static uint32_t hash_thp_split = -1;
-       // static uint32_t hash_unevictable_pgs_cleared = -1;
-       // static uint32_t hash_unevictable_pgs_culled = -1;
-       // static uint32_t hash_unevictable_pgs_mlocked = -1;
-       // static uint32_t hash_unevictable_pgs_mlockfreed = -1;
-       // static uint32_t hash_unevictable_pgs_munlocked = -1;
-       // static uint32_t hash_unevictable_pgs_rescued = -1;
-       // static uint32_t hash_unevictable_pgs_scanned = -1;
-       // static uint32_t hash_unevictable_pgs_stranded = -1;
+    // static uint32_t hash_allocstall = -1;
+    // static uint32_t hash_compact_blocks_moved = -1;
+    // static uint32_t hash_compact_fail = -1;
+    // static uint32_t hash_compact_pagemigrate_failed = -1;
+    // static uint32_t hash_compact_pages_moved = -1;
+    // static uint32_t hash_compact_stall = -1;
+    // static uint32_t hash_compact_success = -1;
+    // static uint32_t hash_htlb_buddy_alloc_fail = -1;
+    // static uint32_t hash_htlb_buddy_alloc_success = -1;
+    // static uint32_t hash_kswapd_high_wmark_hit_quickly = -1;
+    // static uint32_t hash_kswapd_inodesteal = -1;
+    // static uint32_t hash_kswapd_low_wmark_hit_quickly = -1;
+    // static uint32_t hash_kswapd_skip_congestion_wait = -1;
+    // static uint32_t hash_nr_active_anon = -1;
+    // static uint32_t hash_nr_active_file = -1;
+    // static uint32_t hash_nr_anon_pages = -1;
+    // static uint32_t hash_nr_anon_transparent_hugepages = -1;
+    // static uint32_t hash_nr_bounce = -1;
+    // static uint32_t hash_nr_dirtied = -1;
+    // static uint32_t hash_nr_dirty = -1;
+    // static uint32_t hash_nr_dirty_background_threshold = -1;
+    // static uint32_t hash_nr_dirty_threshold = -1;
+    // static uint32_t hash_nr_file_pages = -1;
+    // static uint32_t hash_nr_free_pages = -1;
+    // static uint32_t hash_nr_inactive_anon = -1;
+    // static uint32_t hash_nr_inactive_file = -1;
+    // static uint32_t hash_nr_isolated_anon = -1;
+    // static uint32_t hash_nr_isolated_file = -1;
+    // static uint32_t hash_nr_kernel_stack = -1;
+    // static uint32_t hash_nr_mapped = -1;
+    // static uint32_t hash_nr_mlock = -1;
+    // static uint32_t hash_nr_page_table_pages = -1;
+    // static uint32_t hash_nr_shmem = -1;
+    // static uint32_t hash_nr_slab_reclaimable = -1;
+    // static uint32_t hash_nr_slab_unreclaimable = -1;
+    // static uint32_t hash_nr_unevictable = -1;
+    // static uint32_t hash_nr_unstable = -1;
+    // static uint32_t hash_nr_vmscan_immediate_reclaim = -1;
+    // static uint32_t hash_nr_vmscan_write = -1;
+    // static uint32_t hash_nr_writeback = -1;
+    // static uint32_t hash_nr_writeback_temp = -1;
+    // static uint32_t hash_nr_written = -1;
+    // static uint32_t hash_pageoutrun = -1;
+    // static uint32_t hash_pgactivate = -1;
+    // static uint32_t hash_pgalloc_dma = -1;
+    // static uint32_t hash_pgalloc_dma32 = -1;
+    // static uint32_t hash_pgalloc_movable = -1;
+    // static uint32_t hash_pgalloc_normal = -1;
+    // static uint32_t hash_pgdeactivate = -1;
+    static uint32_t hash_pgfault = -1;
+    // static uint32_t hash_pgfree = -1;
+    // static uint32_t hash_pginodesteal = -1;
+    static uint32_t hash_pgmajfault = -1;
+    static uint32_t hash_pgpgin = -1;
+    static uint32_t hash_pgpgout = -1;
+    // static uint32_t hash_pgrefill_dma = -1;
+    // static uint32_t hash_pgrefill_dma32 = -1;
+    // static uint32_t hash_pgrefill_movable = -1;
+    // static uint32_t hash_pgrefill_normal = -1;
+    // static uint32_t hash_pgrotated = -1;
+    // static uint32_t hash_pgscan_direct_dma = -1;
+    // static uint32_t hash_pgscan_direct_dma32 = -1;
+    // static uint32_t hash_pgscan_direct_movable = -1;
+    // static uint32_t hash_pgscan_direct_normal = -1;
+    // static uint32_t hash_pgscan_kswapd_dma = -1;
+    // static uint32_t hash_pgscan_kswapd_dma32 = -1;
+    // static uint32_t hash_pgscan_kswapd_movable = -1;
+    // static uint32_t hash_pgscan_kswapd_normal = -1;
+    // static uint32_t hash_pgsteal_direct_dma = -1;
+    // static uint32_t hash_pgsteal_direct_dma32 = -1;
+    // static uint32_t hash_pgsteal_direct_movable = -1;
+    // static uint32_t hash_pgsteal_direct_normal = -1;
+    // static uint32_t hash_pgsteal_kswapd_dma = -1;
+    // static uint32_t hash_pgsteal_kswapd_dma32 = -1;
+    // static uint32_t hash_pgsteal_kswapd_movable = -1;
+    // static uint32_t hash_pgsteal_kswapd_normal = -1;
+    static uint32_t hash_pswpin = -1;
+    static uint32_t hash_pswpout = -1;
+    // static uint32_t hash_slabs_scanned = -1;
+    // static uint32_t hash_thp_collapse_alloc = -1;
+    // static uint32_t hash_thp_collapse_alloc_failed = -1;
+    // static uint32_t hash_thp_fault_alloc = -1;
+    // static uint32_t hash_thp_fault_fallback = -1;
+    // static uint32_t hash_thp_split = -1;
+    // static uint32_t hash_unevictable_pgs_cleared = -1;
+    // static uint32_t hash_unevictable_pgs_culled = -1;
+    // static uint32_t hash_unevictable_pgs_mlocked = -1;
+    // static uint32_t hash_unevictable_pgs_mlockfreed = -1;
+    // static uint32_t hash_unevictable_pgs_munlocked = -1;
+    // static uint32_t hash_unevictable_pgs_rescued = -1;
+    // static uint32_t hash_unevictable_pgs_scanned = -1;
+    // static uint32_t hash_unevictable_pgs_stranded = -1;
 
-       if(gen_hashes != 1) {
-               gen_hashes = 1;
-               // hash_allocstall = simple_hash("allocstall");
-               // hash_compact_blocks_moved = simple_hash("compact_blocks_moved");
-               // hash_compact_fail = simple_hash("compact_fail");
-               // hash_compact_pagemigrate_failed = simple_hash("compact_pagemigrate_failed");
-               // hash_compact_pages_moved = simple_hash("compact_pages_moved");
-               // hash_compact_stall = simple_hash("compact_stall");
-               // hash_compact_success = simple_hash("compact_success");
-               // hash_htlb_buddy_alloc_fail = simple_hash("htlb_buddy_alloc_fail");
-               // hash_htlb_buddy_alloc_success = simple_hash("htlb_buddy_alloc_success");
-               // hash_kswapd_high_wmark_hit_quickly = simple_hash("kswapd_high_wmark_hit_quickly");
-               // hash_kswapd_inodesteal = simple_hash("kswapd_inodesteal");
-               // hash_kswapd_low_wmark_hit_quickly = simple_hash("kswapd_low_wmark_hit_quickly");
-               // hash_kswapd_skip_congestion_wait = simple_hash("kswapd_skip_congestion_wait");
-               // hash_nr_active_anon = simple_hash("nr_active_anon");
-               // hash_nr_active_file = simple_hash("nr_active_file");
-               // hash_nr_anon_pages = simple_hash("nr_anon_pages");
-               // hash_nr_anon_transparent_hugepages = simple_hash("nr_anon_transparent_hugepages");
-               // hash_nr_bounce = simple_hash("nr_bounce");
-               // hash_nr_dirtied = simple_hash("nr_dirtied");
-               // hash_nr_dirty = simple_hash("nr_dirty");
-               // hash_nr_dirty_background_threshold = simple_hash("nr_dirty_background_threshold");
-               // hash_nr_dirty_threshold = simple_hash("nr_dirty_threshold");
-               // hash_nr_file_pages = simple_hash("nr_file_pages");
-               // hash_nr_free_pages = simple_hash("nr_free_pages");
-               // hash_nr_inactive_anon = simple_hash("nr_inactive_anon");
-               // hash_nr_inactive_file = simple_hash("nr_inactive_file");
-               // hash_nr_isolated_anon = simple_hash("nr_isolated_anon");
-               // hash_nr_isolated_file = simple_hash("nr_isolated_file");
-               // hash_nr_kernel_stack = simple_hash("nr_kernel_stack");
-               // hash_nr_mapped = simple_hash("nr_mapped");
-               // hash_nr_mlock = simple_hash("nr_mlock");
-               // hash_nr_page_table_pages = simple_hash("nr_page_table_pages");
-               // hash_nr_shmem = simple_hash("nr_shmem");
-               // hash_nr_slab_reclaimable = simple_hash("nr_slab_reclaimable");
-               // hash_nr_slab_unreclaimable = simple_hash("nr_slab_unreclaimable");
-               // hash_nr_unevictable = simple_hash("nr_unevictable");
-               // hash_nr_unstable = simple_hash("nr_unstable");
-               // hash_nr_vmscan_immediate_reclaim = simple_hash("nr_vmscan_immediate_reclaim");
-               // hash_nr_vmscan_write = simple_hash("nr_vmscan_write");
-               // hash_nr_writeback = simple_hash("nr_writeback");
-               // hash_nr_writeback_temp = simple_hash("nr_writeback_temp");
-               // hash_nr_written = simple_hash("nr_written");
-               // hash_pageoutrun = simple_hash("pageoutrun");
-               // hash_pgactivate = simple_hash("pgactivate");
-               // hash_pgalloc_dma = simple_hash("pgalloc_dma");
-               // hash_pgalloc_dma32 = simple_hash("pgalloc_dma32");
-               // hash_pgalloc_movable = simple_hash("pgalloc_movable");
-               // hash_pgalloc_normal = simple_hash("pgalloc_normal");
-               // hash_pgdeactivate = simple_hash("pgdeactivate");
-               hash_pgfault = simple_hash("pgfault");
-               // hash_pgfree = simple_hash("pgfree");
-               // hash_pginodesteal = simple_hash("pginodesteal");
-               hash_pgmajfault = simple_hash("pgmajfault");
-               hash_pgpgin = simple_hash("pgpgin");
-               hash_pgpgout = simple_hash("pgpgout");
-               // hash_pgrefill_dma = simple_hash("pgrefill_dma");
-               // hash_pgrefill_dma32 = simple_hash("pgrefill_dma32");
-               // hash_pgrefill_movable = simple_hash("pgrefill_movable");
-               // hash_pgrefill_normal = simple_hash("pgrefill_normal");
-               // hash_pgrotated = simple_hash("pgrotated");
-               // hash_pgscan_direct_dma = simple_hash("pgscan_direct_dma");
-               // hash_pgscan_direct_dma32 = simple_hash("pgscan_direct_dma32");
-               // hash_pgscan_direct_movable = simple_hash("pgscan_direct_movable");
-               // hash_pgscan_direct_normal = simple_hash("pgscan_direct_normal");
-               // hash_pgscan_kswapd_dma = simple_hash("pgscan_kswapd_dma");
-               // hash_pgscan_kswapd_dma32 = simple_hash("pgscan_kswapd_dma32");
-               // hash_pgscan_kswapd_movable = simple_hash("pgscan_kswapd_movable");
-               // hash_pgscan_kswapd_normal = simple_hash("pgscan_kswapd_normal");
-               // hash_pgsteal_direct_dma = simple_hash("pgsteal_direct_dma");
-               // hash_pgsteal_direct_dma32 = simple_hash("pgsteal_direct_dma32");
-               // hash_pgsteal_direct_movable = simple_hash("pgsteal_direct_movable");
-               // hash_pgsteal_direct_normal = simple_hash("pgsteal_direct_normal");
-               // hash_pgsteal_kswapd_dma = simple_hash("pgsteal_kswapd_dma");
-               // hash_pgsteal_kswapd_dma32 = simple_hash("pgsteal_kswapd_dma32");
-               // hash_pgsteal_kswapd_movable = simple_hash("pgsteal_kswapd_movable");
-               // hash_pgsteal_kswapd_normal = simple_hash("pgsteal_kswapd_normal");
-               hash_pswpin = simple_hash("pswpin");
-               hash_pswpout = simple_hash("pswpout");
-               // hash_slabs_scanned = simple_hash("slabs_scanned");
-               // hash_thp_collapse_alloc = simple_hash("thp_collapse_alloc");
-               // hash_thp_collapse_alloc_failed = simple_hash("thp_collapse_alloc_failed");
-               // hash_thp_fault_alloc = simple_hash("thp_fault_alloc");
-               // hash_thp_fault_fallback = simple_hash("thp_fault_fallback");
-               // hash_thp_split = simple_hash("thp_split");
-               // hash_unevictable_pgs_cleared = simple_hash("unevictable_pgs_cleared");
-               // hash_unevictable_pgs_culled = simple_hash("unevictable_pgs_culled");
-               // hash_unevictable_pgs_mlocked = simple_hash("unevictable_pgs_mlocked");
-               // hash_unevictable_pgs_mlockfreed = simple_hash("unevictable_pgs_mlockfreed");
-               // hash_unevictable_pgs_munlocked = simple_hash("unevictable_pgs_munlocked");
-               // hash_unevictable_pgs_rescued = simple_hash("unevictable_pgs_rescued");
-               // hash_unevictable_pgs_scanned = simple_hash("unevictable_pgs_scanned");
-               // hash_unevictable_pgs_stranded = simple_hash("unevictable_pgs_stranded");
-       }
+    if(gen_hashes != 1) {
+        gen_hashes = 1;
+        // hash_allocstall = simple_hash("allocstall");
+        // hash_compact_blocks_moved = simple_hash("compact_blocks_moved");
+        // hash_compact_fail = simple_hash("compact_fail");
+        // hash_compact_pagemigrate_failed = simple_hash("compact_pagemigrate_failed");
+        // hash_compact_pages_moved = simple_hash("compact_pages_moved");
+        // hash_compact_stall = simple_hash("compact_stall");
+        // hash_compact_success = simple_hash("compact_success");
+        // hash_htlb_buddy_alloc_fail = simple_hash("htlb_buddy_alloc_fail");
+        // hash_htlb_buddy_alloc_success = simple_hash("htlb_buddy_alloc_success");
+        // hash_kswapd_high_wmark_hit_quickly = simple_hash("kswapd_high_wmark_hit_quickly");
+        // hash_kswapd_inodesteal = simple_hash("kswapd_inodesteal");
+        // hash_kswapd_low_wmark_hit_quickly = simple_hash("kswapd_low_wmark_hit_quickly");
+        // hash_kswapd_skip_congestion_wait = simple_hash("kswapd_skip_congestion_wait");
+        // hash_nr_active_anon = simple_hash("nr_active_anon");
+        // hash_nr_active_file = simple_hash("nr_active_file");
+        // hash_nr_anon_pages = simple_hash("nr_anon_pages");
+        // hash_nr_anon_transparent_hugepages = simple_hash("nr_anon_transparent_hugepages");
+        // hash_nr_bounce = simple_hash("nr_bounce");
+        // hash_nr_dirtied = simple_hash("nr_dirtied");
+        // hash_nr_dirty = simple_hash("nr_dirty");
+        // hash_nr_dirty_background_threshold = simple_hash("nr_dirty_background_threshold");
+        // hash_nr_dirty_threshold = simple_hash("nr_dirty_threshold");
+        // hash_nr_file_pages = simple_hash("nr_file_pages");
+        // hash_nr_free_pages = simple_hash("nr_free_pages");
+        // hash_nr_inactive_anon = simple_hash("nr_inactive_anon");
+        // hash_nr_inactive_file = simple_hash("nr_inactive_file");
+        // hash_nr_isolated_anon = simple_hash("nr_isolated_anon");
+        // hash_nr_isolated_file = simple_hash("nr_isolated_file");
+        // hash_nr_kernel_stack = simple_hash("nr_kernel_stack");
+        // hash_nr_mapped = simple_hash("nr_mapped");
+        // hash_nr_mlock = simple_hash("nr_mlock");
+        // hash_nr_page_table_pages = simple_hash("nr_page_table_pages");
+        // hash_nr_shmem = simple_hash("nr_shmem");
+        // hash_nr_slab_reclaimable = simple_hash("nr_slab_reclaimable");
+        // hash_nr_slab_unreclaimable = simple_hash("nr_slab_unreclaimable");
+        // hash_nr_unevictable = simple_hash("nr_unevictable");
+        // hash_nr_unstable = simple_hash("nr_unstable");
+        // hash_nr_vmscan_immediate_reclaim = simple_hash("nr_vmscan_immediate_reclaim");
+        // hash_nr_vmscan_write = simple_hash("nr_vmscan_write");
+        // hash_nr_writeback = simple_hash("nr_writeback");
+        // hash_nr_writeback_temp = simple_hash("nr_writeback_temp");
+        // hash_nr_written = simple_hash("nr_written");
+        // hash_pageoutrun = simple_hash("pageoutrun");
+        // hash_pgactivate = simple_hash("pgactivate");
+        // hash_pgalloc_dma = simple_hash("pgalloc_dma");
+        // hash_pgalloc_dma32 = simple_hash("pgalloc_dma32");
+        // hash_pgalloc_movable = simple_hash("pgalloc_movable");
+        // hash_pgalloc_normal = simple_hash("pgalloc_normal");
+        // hash_pgdeactivate = simple_hash("pgdeactivate");
+        hash_pgfault = simple_hash("pgfault");
+        // hash_pgfree = simple_hash("pgfree");
+        // hash_pginodesteal = simple_hash("pginodesteal");
+        hash_pgmajfault = simple_hash("pgmajfault");
+        hash_pgpgin = simple_hash("pgpgin");
+        hash_pgpgout = simple_hash("pgpgout");
+        // hash_pgrefill_dma = simple_hash("pgrefill_dma");
+        // hash_pgrefill_dma32 = simple_hash("pgrefill_dma32");
+        // hash_pgrefill_movable = simple_hash("pgrefill_movable");
+        // hash_pgrefill_normal = simple_hash("pgrefill_normal");
+        // hash_pgrotated = simple_hash("pgrotated");
+        // hash_pgscan_direct_dma = simple_hash("pgscan_direct_dma");
+        // hash_pgscan_direct_dma32 = simple_hash("pgscan_direct_dma32");
+        // hash_pgscan_direct_movable = simple_hash("pgscan_direct_movable");
+        // hash_pgscan_direct_normal = simple_hash("pgscan_direct_normal");
+        // hash_pgscan_kswapd_dma = simple_hash("pgscan_kswapd_dma");
+        // hash_pgscan_kswapd_dma32 = simple_hash("pgscan_kswapd_dma32");
+        // hash_pgscan_kswapd_movable = simple_hash("pgscan_kswapd_movable");
+        // hash_pgscan_kswapd_normal = simple_hash("pgscan_kswapd_normal");
+        // hash_pgsteal_direct_dma = simple_hash("pgsteal_direct_dma");
+        // hash_pgsteal_direct_dma32 = simple_hash("pgsteal_direct_dma32");
+        // hash_pgsteal_direct_movable = simple_hash("pgsteal_direct_movable");
+        // hash_pgsteal_direct_normal = simple_hash("pgsteal_direct_normal");
+        // hash_pgsteal_kswapd_dma = simple_hash("pgsteal_kswapd_dma");
+        // hash_pgsteal_kswapd_dma32 = simple_hash("pgsteal_kswapd_dma32");
+        // hash_pgsteal_kswapd_movable = simple_hash("pgsteal_kswapd_movable");
+        // hash_pgsteal_kswapd_normal = simple_hash("pgsteal_kswapd_normal");
+        hash_pswpin = simple_hash("pswpin");
+        hash_pswpout = simple_hash("pswpout");
+        // hash_slabs_scanned = simple_hash("slabs_scanned");
+        // hash_thp_collapse_alloc = simple_hash("thp_collapse_alloc");
+        // hash_thp_collapse_alloc_failed = simple_hash("thp_collapse_alloc_failed");
+        // hash_thp_fault_alloc = simple_hash("thp_fault_alloc");
+        // hash_thp_fault_fallback = simple_hash("thp_fault_fallback");
+        // hash_thp_split = simple_hash("thp_split");
+        // hash_unevictable_pgs_cleared = simple_hash("unevictable_pgs_cleared");
+        // hash_unevictable_pgs_culled = simple_hash("unevictable_pgs_culled");
+        // hash_unevictable_pgs_mlocked = simple_hash("unevictable_pgs_mlocked");
+        // hash_unevictable_pgs_mlockfreed = simple_hash("unevictable_pgs_mlockfreed");
+        // hash_unevictable_pgs_munlocked = simple_hash("unevictable_pgs_munlocked");
+        // hash_unevictable_pgs_rescued = simple_hash("unevictable_pgs_rescued");
+        // hash_unevictable_pgs_scanned = simple_hash("unevictable_pgs_scanned");
+        // hash_unevictable_pgs_stranded = simple_hash("unevictable_pgs_stranded");
+    }
 
-       if(do_swapio == -1)     do_swapio = config_get_boolean("plugin:proc:/proc/vmstat", "swap i/o", 1);
-       if(do_io == -1)         do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
-       if(do_pgfaults == -1)   do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
+    if(do_swapio == -1) do_swapio = config_get_boolean("plugin:proc:/proc/vmstat", "swap i/o", 1);
+    if(do_io == -1)     do_io = config_get_boolean("plugin:proc:/proc/vmstat", "disk i/o", 1);
+    if(do_pgfaults == -1)   do_pgfaults = config_get_boolean("plugin:proc:/proc/vmstat", "memory page faults", 1);
 
-       if(dt) {};
+    if(dt) {};
 
-       if(!ff) {
-               char filename[FILENAME_MAX + 1];
-               snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/vmstat");
-               ff = procfile_open(config_get("plugin:proc:/proc/vmstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
-       }
-       if(!ff) return 1;
+    if(!ff) {
+        char filename[FILENAME_MAX + 1];
+        snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/vmstat");
+        ff = procfile_open(config_get("plugin:proc:/proc/vmstat", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT);
+    }
+    if(!ff) return 1;
 
-       ff = procfile_readall(ff);
-       if(!ff) return 0; // we return 0, so that we will retry to open it next time
+    ff = procfile_readall(ff);
+    if(!ff) return 0; // we return 0, so that we will retry to open it next time
 
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words;
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words;
 
-       // unsigned long long allocstall = 0ULL;
-       // unsigned long long compact_blocks_moved = 0ULL;
-       // unsigned long long compact_fail = 0ULL;
-       // unsigned long long compact_pagemigrate_failed = 0ULL;
-       // unsigned long long compact_pages_moved = 0ULL;
-       // unsigned long long compact_stall = 0ULL;
-       // unsigned long long compact_success = 0ULL;
-       // unsigned long long htlb_buddy_alloc_fail = 0ULL;
-       // unsigned long long htlb_buddy_alloc_success = 0ULL;
-       // unsigned long long kswapd_high_wmark_hit_quickly = 0ULL;
-       // unsigned long long kswapd_inodesteal = 0ULL;
-       // unsigned long long kswapd_low_wmark_hit_quickly = 0ULL;
-       // unsigned long long kswapd_skip_congestion_wait = 0ULL;
-       // unsigned long long nr_active_anon = 0ULL;
-       // unsigned long long nr_active_file = 0ULL;
-       // unsigned long long nr_anon_pages = 0ULL;
-       // unsigned long long nr_anon_transparent_hugepages = 0ULL;
-       // unsigned long long nr_bounce = 0ULL;
-       // unsigned long long nr_dirtied = 0ULL;
-       // unsigned long long nr_dirty = 0ULL;
-       // unsigned long long nr_dirty_background_threshold = 0ULL;
-       // unsigned long long nr_dirty_threshold = 0ULL;
-       // unsigned long long nr_file_pages = 0ULL;
-       // unsigned long long nr_free_pages = 0ULL;
-       // unsigned long long nr_inactive_anon = 0ULL;
-       // unsigned long long nr_inactive_file = 0ULL;
-       // unsigned long long nr_isolated_anon = 0ULL;
-       // unsigned long long nr_isolated_file = 0ULL;
-       // unsigned long long nr_kernel_stack = 0ULL;
-       // unsigned long long nr_mapped = 0ULL;
-       // unsigned long long nr_mlock = 0ULL;
-       // unsigned long long nr_page_table_pages = 0ULL;
-       // unsigned long long nr_shmem = 0ULL;
-       // unsigned long long nr_slab_reclaimable = 0ULL;
-       // unsigned long long nr_slab_unreclaimable = 0ULL;
-       // unsigned long long nr_unevictable = 0ULL;
-       // unsigned long long nr_unstable = 0ULL;
-       // unsigned long long nr_vmscan_immediate_reclaim = 0ULL;
-       // unsigned long long nr_vmscan_write = 0ULL;
-       // unsigned long long nr_writeback = 0ULL;
-       // unsigned long long nr_writeback_temp = 0ULL;
-       // unsigned long long nr_written = 0ULL;
-       // unsigned long long pageoutrun = 0ULL;
-       // unsigned long long pgactivate = 0ULL;
-       // unsigned long long pgalloc_dma = 0ULL;
-       // unsigned long long pgalloc_dma32 = 0ULL;
-       // unsigned long long pgalloc_movable = 0ULL;
-       // unsigned long long pgalloc_normal = 0ULL;
-       // unsigned long long pgdeactivate = 0ULL;
-       unsigned long long pgfault = 0ULL;
-       // unsigned long long pgfree = 0ULL;
-       // unsigned long long pginodesteal = 0ULL;
-       unsigned long long pgmajfault = 0ULL;
-       unsigned long long pgpgin = 0ULL;
-       unsigned long long pgpgout = 0ULL;
-       // unsigned long long pgrefill_dma = 0ULL;
-       // unsigned long long pgrefill_dma32 = 0ULL;
-       // unsigned long long pgrefill_movable = 0ULL;
-       // unsigned long long pgrefill_normal = 0ULL;
-       // unsigned long long pgrotated = 0ULL;
-       // unsigned long long pgscan_direct_dma = 0ULL;
-       // unsigned long long pgscan_direct_dma32 = 0ULL;
-       // unsigned long long pgscan_direct_movable = 0ULL;
-       // unsigned long long pgscan_direct_normal = 0ULL;
-       // unsigned long long pgscan_kswapd_dma = 0ULL;
-       // unsigned long long pgscan_kswapd_dma32 = 0ULL;
-       // unsigned long long pgscan_kswapd_movable = 0ULL;
-       // unsigned long long pgscan_kswapd_normal = 0ULL;
-       // unsigned long long pgsteal_direct_dma = 0ULL;
-       // unsigned long long pgsteal_direct_dma32 = 0ULL;
-       // unsigned long long pgsteal_direct_movable = 0ULL;
-       // unsigned long long pgsteal_direct_normal = 0ULL;
-       // unsigned long long pgsteal_kswapd_dma = 0ULL;
-       // unsigned long long pgsteal_kswapd_dma32 = 0ULL;
-       // unsigned long long pgsteal_kswapd_movable = 0ULL;
-       // unsigned long long pgsteal_kswapd_normal = 0ULL;
-       unsigned long long pswpin = 0ULL;
-       unsigned long long pswpout = 0ULL;
-       // unsigned long long slabs_scanned = 0ULL;
-       // unsigned long long thp_collapse_alloc = 0ULL;
-       // unsigned long long thp_collapse_alloc_failed = 0ULL;
-       // unsigned long long thp_fault_alloc = 0ULL;
-       // unsigned long long thp_fault_fallback = 0ULL;
-       // unsigned long long thp_split = 0ULL;
-       // unsigned long long unevictable_pgs_cleared = 0ULL;
-       // unsigned long long unevictable_pgs_culled = 0ULL;
-       // unsigned long long unevictable_pgs_mlocked = 0ULL;
-       // unsigned long long unevictable_pgs_mlockfreed = 0ULL;
-       // unsigned long long unevictable_pgs_munlocked = 0ULL;
-       // unsigned long long unevictable_pgs_rescued = 0ULL;
-       // unsigned long long unevictable_pgs_scanned = 0ULL;
-       // unsigned long long unevictable_pgs_stranded = 0ULL;
+    // unsigned long long allocstall = 0ULL;
+    // unsigned long long compact_blocks_moved = 0ULL;
+    // unsigned long long compact_fail = 0ULL;
+    // unsigned long long compact_pagemigrate_failed = 0ULL;
+    // unsigned long long compact_pages_moved = 0ULL;
+    // unsigned long long compact_stall = 0ULL;
+    // unsigned long long compact_success = 0ULL;
+    // unsigned long long htlb_buddy_alloc_fail = 0ULL;
+    // unsigned long long htlb_buddy_alloc_success = 0ULL;
+    // unsigned long long kswapd_high_wmark_hit_quickly = 0ULL;
+    // unsigned long long kswapd_inodesteal = 0ULL;
+    // unsigned long long kswapd_low_wmark_hit_quickly = 0ULL;
+    // unsigned long long kswapd_skip_congestion_wait = 0ULL;
+    // unsigned long long nr_active_anon = 0ULL;
+    // unsigned long long nr_active_file = 0ULL;
+    // unsigned long long nr_anon_pages = 0ULL;
+    // unsigned long long nr_anon_transparent_hugepages = 0ULL;
+    // unsigned long long nr_bounce = 0ULL;
+    // unsigned long long nr_dirtied = 0ULL;
+    // unsigned long long nr_dirty = 0ULL;
+    // unsigned long long nr_dirty_background_threshold = 0ULL;
+    // unsigned long long nr_dirty_threshold = 0ULL;
+    // unsigned long long nr_file_pages = 0ULL;
+    // unsigned long long nr_free_pages = 0ULL;
+    // unsigned long long nr_inactive_anon = 0ULL;
+    // unsigned long long nr_inactive_file = 0ULL;
+    // unsigned long long nr_isolated_anon = 0ULL;
+    // unsigned long long nr_isolated_file = 0ULL;
+    // unsigned long long nr_kernel_stack = 0ULL;
+    // unsigned long long nr_mapped = 0ULL;
+    // unsigned long long nr_mlock = 0ULL;
+    // unsigned long long nr_page_table_pages = 0ULL;
+    // unsigned long long nr_shmem = 0ULL;
+    // unsigned long long nr_slab_reclaimable = 0ULL;
+    // unsigned long long nr_slab_unreclaimable = 0ULL;
+    // unsigned long long nr_unevictable = 0ULL;
+    // unsigned long long nr_unstable = 0ULL;
+    // unsigned long long nr_vmscan_immediate_reclaim = 0ULL;
+    // unsigned long long nr_vmscan_write = 0ULL;
+    // unsigned long long nr_writeback = 0ULL;
+    // unsigned long long nr_writeback_temp = 0ULL;
+    // unsigned long long nr_written = 0ULL;
+    // unsigned long long pageoutrun = 0ULL;
+    // unsigned long long pgactivate = 0ULL;
+    // unsigned long long pgalloc_dma = 0ULL;
+    // unsigned long long pgalloc_dma32 = 0ULL;
+    // unsigned long long pgalloc_movable = 0ULL;
+    // unsigned long long pgalloc_normal = 0ULL;
+    // unsigned long long pgdeactivate = 0ULL;
+    unsigned long long pgfault = 0ULL;
+    // unsigned long long pgfree = 0ULL;
+    // unsigned long long pginodesteal = 0ULL;
+    unsigned long long pgmajfault = 0ULL;
+    unsigned long long pgpgin = 0ULL;
+    unsigned long long pgpgout = 0ULL;
+    // unsigned long long pgrefill_dma = 0ULL;
+    // unsigned long long pgrefill_dma32 = 0ULL;
+    // unsigned long long pgrefill_movable = 0ULL;
+    // unsigned long long pgrefill_normal = 0ULL;
+    // unsigned long long pgrotated = 0ULL;
+    // unsigned long long pgscan_direct_dma = 0ULL;
+    // unsigned long long pgscan_direct_dma32 = 0ULL;
+    // unsigned long long pgscan_direct_movable = 0ULL;
+    // unsigned long long pgscan_direct_normal = 0ULL;
+    // unsigned long long pgscan_kswapd_dma = 0ULL;
+    // unsigned long long pgscan_kswapd_dma32 = 0ULL;
+    // unsigned long long pgscan_kswapd_movable = 0ULL;
+    // unsigned long long pgscan_kswapd_normal = 0ULL;
+    // unsigned long long pgsteal_direct_dma = 0ULL;
+    // unsigned long long pgsteal_direct_dma32 = 0ULL;
+    // unsigned long long pgsteal_direct_movable = 0ULL;
+    // unsigned long long pgsteal_direct_normal = 0ULL;
+    // unsigned long long pgsteal_kswapd_dma = 0ULL;
+    // unsigned long long pgsteal_kswapd_dma32 = 0ULL;
+    // unsigned long long pgsteal_kswapd_movable = 0ULL;
+    // unsigned long long pgsteal_kswapd_normal = 0ULL;
+    unsigned long long pswpin = 0ULL;
+    unsigned long long pswpout = 0ULL;
+    // unsigned long long slabs_scanned = 0ULL;
+    // unsigned long long thp_collapse_alloc = 0ULL;
+    // unsigned long long thp_collapse_alloc_failed = 0ULL;
+    // unsigned long long thp_fault_alloc = 0ULL;
+    // unsigned long long thp_fault_fallback = 0ULL;
+    // unsigned long long thp_split = 0ULL;
+    // unsigned long long unevictable_pgs_cleared = 0ULL;
+    // unsigned long long unevictable_pgs_culled = 0ULL;
+    // unsigned long long unevictable_pgs_mlocked = 0ULL;
+    // unsigned long long unevictable_pgs_mlockfreed = 0ULL;
+    // unsigned long long unevictable_pgs_munlocked = 0ULL;
+    // unsigned long long unevictable_pgs_rescued = 0ULL;
+    // unsigned long long unevictable_pgs_scanned = 0ULL;
+    // unsigned long long unevictable_pgs_stranded = 0ULL;
 
-       for(l = 0; l < lines ;l++) {
-               words = procfile_linewords(ff, l);
-               if(words < 2) {
-                       if(words) error("Cannot read /proc/vmstat line %u. Expected 2 params, read %u.", l, words);
-                       continue;
-               }
+    for(l = 0; l < lines ;l++) {
+        words = procfile_linewords(ff, l);
+        if(words < 2) {
+            if(words) error("Cannot read /proc/vmstat line %u. Expected 2 params, read %u.", l, words);
+            continue;
+        }
 
-               char *name = procfile_lineword(ff, l, 0);
-               char * value = procfile_lineword(ff, l, 1);
-               if(!name || !*name || !value || !*value) continue;
+        char *name = procfile_lineword(ff, l, 0);
+        char * value = procfile_lineword(ff, l, 1);
+        if(!name || !*name || !value || !*value) continue;
 
-               uint32_t hash = simple_hash(name);
+        uint32_t hash = simple_hash(name);
 
-               if(0) ;
-               // else if(hash == hash_allocstall && strcmp(name, "allocstall") == 0) allocstall = strtoull(value, NULL, 10);
-               // else if(hash == hash_compact_blocks_moved && strcmp(name, "compact_blocks_moved") == 0) compact_blocks_moved = strtoull(value, NULL, 10);
-               // else if(hash == hash_compact_fail && strcmp(name, "compact_fail") == 0) compact_fail = strtoull(value, NULL, 10);
-               // else if(hash == hash_compact_pagemigrate_failed && strcmp(name, "compact_pagemigrate_failed") == 0) compact_pagemigrate_failed = strtoull(value, NULL, 10);
-               // else if(hash == hash_compact_pages_moved && strcmp(name, "compact_pages_moved") == 0) compact_pages_moved = strtoull(value, NULL, 10);
-               // else if(hash == hash_compact_stall && strcmp(name, "compact_stall") == 0) compact_stall = strtoull(value, NULL, 10);
-               // else if(hash == hash_compact_success && strcmp(name, "compact_success") == 0) compact_success = strtoull(value, NULL, 10);
-               // else if(hash == hash_htlb_buddy_alloc_fail && strcmp(name, "htlb_buddy_alloc_fail") == 0) htlb_buddy_alloc_fail = strtoull(value, NULL, 10);
-               // else if(hash == hash_htlb_buddy_alloc_success && strcmp(name, "htlb_buddy_alloc_success") == 0) htlb_buddy_alloc_success = strtoull(value, NULL, 10);
-               // else if(hash == hash_kswapd_high_wmark_hit_quickly && strcmp(name, "kswapd_high_wmark_hit_quickly") == 0) kswapd_high_wmark_hit_quickly = strtoull(value, NULL, 10);
-               // else if(hash == hash_kswapd_inodesteal && strcmp(name, "kswapd_inodesteal") == 0) kswapd_inodesteal = strtoull(value, NULL, 10);
-               // else if(hash == hash_kswapd_low_wmark_hit_quickly && strcmp(name, "kswapd_low_wmark_hit_quickly") == 0) kswapd_low_wmark_hit_quickly = strtoull(value, NULL, 10);
-               // else if(hash == hash_kswapd_skip_congestion_wait && strcmp(name, "kswapd_skip_congestion_wait") == 0) kswapd_skip_congestion_wait = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_active_anon && strcmp(name, "nr_active_anon") == 0) nr_active_anon = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_active_file && strcmp(name, "nr_active_file") == 0) nr_active_file = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_anon_pages && strcmp(name, "nr_anon_pages") == 0) nr_anon_pages = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_anon_transparent_hugepages && strcmp(name, "nr_anon_transparent_hugepages") == 0) nr_anon_transparent_hugepages = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_bounce && strcmp(name, "nr_bounce") == 0) nr_bounce = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_dirtied && strcmp(name, "nr_dirtied") == 0) nr_dirtied = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_dirty && strcmp(name, "nr_dirty") == 0) nr_dirty = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_dirty_background_threshold && strcmp(name, "nr_dirty_background_threshold") == 0) nr_dirty_background_threshold = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_dirty_threshold && strcmp(name, "nr_dirty_threshold") == 0) nr_dirty_threshold = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_file_pages && strcmp(name, "nr_file_pages") == 0) nr_file_pages = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_free_pages && strcmp(name, "nr_free_pages") == 0) nr_free_pages = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_inactive_anon && strcmp(name, "nr_inactive_anon") == 0) nr_inactive_anon = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_inactive_file && strcmp(name, "nr_inactive_file") == 0) nr_inactive_file = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_isolated_anon && strcmp(name, "nr_isolated_anon") == 0) nr_isolated_anon = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_isolated_file && strcmp(name, "nr_isolated_file") == 0) nr_isolated_file = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_kernel_stack && strcmp(name, "nr_kernel_stack") == 0) nr_kernel_stack = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_mapped && strcmp(name, "nr_mapped") == 0) nr_mapped = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_mlock && strcmp(name, "nr_mlock") == 0) nr_mlock = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_page_table_pages && strcmp(name, "nr_page_table_pages") == 0) nr_page_table_pages = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_shmem && strcmp(name, "nr_shmem") == 0) nr_shmem = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_slab_reclaimable && strcmp(name, "nr_slab_reclaimable") == 0) nr_slab_reclaimable = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_slab_unreclaimable && strcmp(name, "nr_slab_unreclaimable") == 0) nr_slab_unreclaimable = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_unevictable && strcmp(name, "nr_unevictable") == 0) nr_unevictable = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_unstable && strcmp(name, "nr_unstable") == 0) nr_unstable = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_vmscan_immediate_reclaim && strcmp(name, "nr_vmscan_immediate_reclaim") == 0) nr_vmscan_immediate_reclaim = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_vmscan_write && strcmp(name, "nr_vmscan_write") == 0) nr_vmscan_write = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_writeback && strcmp(name, "nr_writeback") == 0) nr_writeback = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_writeback_temp && strcmp(name, "nr_writeback_temp") == 0) nr_writeback_temp = strtoull(value, NULL, 10);
-               // else if(hash == hash_nr_written && strcmp(name, "nr_written") == 0) nr_written = strtoull(value, NULL, 10);
-               // else if(hash == hash_pageoutrun && strcmp(name, "pageoutrun") == 0) pageoutrun = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgactivate && strcmp(name, "pgactivate") == 0) pgactivate = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgalloc_dma && strcmp(name, "pgalloc_dma") == 0) pgalloc_dma = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgalloc_dma32 && strcmp(name, "pgalloc_dma32") == 0) pgalloc_dma32 = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgalloc_movable && strcmp(name, "pgalloc_movable") == 0) pgalloc_movable = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgalloc_normal && strcmp(name, "pgalloc_normal") == 0) pgalloc_normal = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgdeactivate && strcmp(name, "pgdeactivate") == 0) pgdeactivate = strtoull(value, NULL, 10);
-               else if(hash == hash_pgfault && strcmp(name, "pgfault") == 0) pgfault = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgfree && strcmp(name, "pgfree") == 0) pgfree = strtoull(value, NULL, 10);
-               // else if(hash == hash_pginodesteal && strcmp(name, "pginodesteal") == 0) pginodesteal = strtoull(value, NULL, 10);
-               else if(hash == hash_pgmajfault && strcmp(name, "pgmajfault") == 0) pgmajfault = strtoull(value, NULL, 10);
-               else if(hash == hash_pgpgin && strcmp(name, "pgpgin") == 0) pgpgin = strtoull(value, NULL, 10);
-               else if(hash == hash_pgpgout && strcmp(name, "pgpgout") == 0) pgpgout = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgrefill_dma && strcmp(name, "pgrefill_dma") == 0) pgrefill_dma = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgrefill_dma32 && strcmp(name, "pgrefill_dma32") == 0) pgrefill_dma32 = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgrefill_movable && strcmp(name, "pgrefill_movable") == 0) pgrefill_movable = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgrefill_normal && strcmp(name, "pgrefill_normal") == 0) pgrefill_normal = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgrotated && strcmp(name, "pgrotated") == 0) pgrotated = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_direct_dma && strcmp(name, "pgscan_direct_dma") == 0) pgscan_direct_dma = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_direct_dma32 && strcmp(name, "pgscan_direct_dma32") == 0) pgscan_direct_dma32 = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_direct_movable && strcmp(name, "pgscan_direct_movable") == 0) pgscan_direct_movable = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_direct_normal && strcmp(name, "pgscan_direct_normal") == 0) pgscan_direct_normal = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_kswapd_dma && strcmp(name, "pgscan_kswapd_dma") == 0) pgscan_kswapd_dma = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_kswapd_dma32 && strcmp(name, "pgscan_kswapd_dma32") == 0) pgscan_kswapd_dma32 = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_kswapd_movable && strcmp(name, "pgscan_kswapd_movable") == 0) pgscan_kswapd_movable = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgscan_kswapd_normal && strcmp(name, "pgscan_kswapd_normal") == 0) pgscan_kswapd_normal = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_direct_dma && strcmp(name, "pgsteal_direct_dma") == 0) pgsteal_direct_dma = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_direct_dma32 && strcmp(name, "pgsteal_direct_dma32") == 0) pgsteal_direct_dma32 = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_direct_movable && strcmp(name, "pgsteal_direct_movable") == 0) pgsteal_direct_movable = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_direct_normal && strcmp(name, "pgsteal_direct_normal") == 0) pgsteal_direct_normal = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_kswapd_dma && strcmp(name, "pgsteal_kswapd_dma") == 0) pgsteal_kswapd_dma = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_kswapd_dma32 && strcmp(name, "pgsteal_kswapd_dma32") == 0) pgsteal_kswapd_dma32 = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_kswapd_movable && strcmp(name, "pgsteal_kswapd_movable") == 0) pgsteal_kswapd_movable = strtoull(value, NULL, 10);
-               // else if(hash == hash_pgsteal_kswapd_normal && strcmp(name, "pgsteal_kswapd_normal") == 0) pgsteal_kswapd_normal = strtoull(value, NULL, 10);
-               else if(hash == hash_pswpin && strcmp(name, "pswpin") == 0) pswpin = strtoull(value, NULL, 10);
-               else if(hash == hash_pswpout && strcmp(name, "pswpout") == 0) pswpout = strtoull(value, NULL, 10);
-               // else if(hash == hash_slabs_scanned && strcmp(name, "slabs_scanned") == 0) slabs_scanned = strtoull(value, NULL, 10);
-               // else if(hash == hash_thp_collapse_alloc && strcmp(name, "thp_collapse_alloc") == 0) thp_collapse_alloc = strtoull(value, NULL, 10);
-               // else if(hash == hash_thp_collapse_alloc_failed && strcmp(name, "thp_collapse_alloc_failed") == 0) thp_collapse_alloc_failed = strtoull(value, NULL, 10);
-               // else if(hash == hash_thp_fault_alloc && strcmp(name, "thp_fault_alloc") == 0) thp_fault_alloc = strtoull(value, NULL, 10);
-               // else if(hash == hash_thp_fault_fallback && strcmp(name, "thp_fault_fallback") == 0) thp_fault_fallback = strtoull(value, NULL, 10);
-               // else if(hash == hash_thp_split && strcmp(name, "thp_split") == 0) thp_split = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_cleared && strcmp(name, "unevictable_pgs_cleared") == 0) unevictable_pgs_cleared = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_culled && strcmp(name, "unevictable_pgs_culled") == 0) unevictable_pgs_culled = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_mlocked && strcmp(name, "unevictable_pgs_mlocked") == 0) unevictable_pgs_mlocked = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_mlockfreed && strcmp(name, "unevictable_pgs_mlockfreed") == 0) unevictable_pgs_mlockfreed = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_munlocked && strcmp(name, "unevictable_pgs_munlocked") == 0) unevictable_pgs_munlocked = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_rescued && strcmp(name, "unevictable_pgs_rescued") == 0) unevictable_pgs_rescued = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_scanned && strcmp(name, "unevictable_pgs_scanned") == 0) unevictable_pgs_scanned = strtoull(value, NULL, 10);
-               // else if(hash == hash_unevictable_pgs_stranded && strcmp(name, "unevictable_pgs_stranded") == 0) unevictable_pgs_stranded = strtoull(value, NULL, 10);
-       }
+        if(0) ;
+        // else if(hash == hash_allocstall && strcmp(name, "allocstall") == 0) allocstall = strtoull(value, NULL, 10);
+        // else if(hash == hash_compact_blocks_moved && strcmp(name, "compact_blocks_moved") == 0) compact_blocks_moved = strtoull(value, NULL, 10);
+        // else if(hash == hash_compact_fail && strcmp(name, "compact_fail") == 0) compact_fail = strtoull(value, NULL, 10);
+        // else if(hash == hash_compact_pagemigrate_failed && strcmp(name, "compact_pagemigrate_failed") == 0) compact_pagemigrate_failed = strtoull(value, NULL, 10);
+        // else if(hash == hash_compact_pages_moved && strcmp(name, "compact_pages_moved") == 0) compact_pages_moved = strtoull(value, NULL, 10);
+        // else if(hash == hash_compact_stall && strcmp(name, "compact_stall") == 0) compact_stall = strtoull(value, NULL, 10);
+        // else if(hash == hash_compact_success && strcmp(name, "compact_success") == 0) compact_success = strtoull(value, NULL, 10);
+        // else if(hash == hash_htlb_buddy_alloc_fail && strcmp(name, "htlb_buddy_alloc_fail") == 0) htlb_buddy_alloc_fail = strtoull(value, NULL, 10);
+        // else if(hash == hash_htlb_buddy_alloc_success && strcmp(name, "htlb_buddy_alloc_success") == 0) htlb_buddy_alloc_success = strtoull(value, NULL, 10);
+        // else if(hash == hash_kswapd_high_wmark_hit_quickly && strcmp(name, "kswapd_high_wmark_hit_quickly") == 0) kswapd_high_wmark_hit_quickly = strtoull(value, NULL, 10);
+        // else if(hash == hash_kswapd_inodesteal && strcmp(name, "kswapd_inodesteal") == 0) kswapd_inodesteal = strtoull(value, NULL, 10);
+        // else if(hash == hash_kswapd_low_wmark_hit_quickly && strcmp(name, "kswapd_low_wmark_hit_quickly") == 0) kswapd_low_wmark_hit_quickly = strtoull(value, NULL, 10);
+        // else if(hash == hash_kswapd_skip_congestion_wait && strcmp(name, "kswapd_skip_congestion_wait") == 0) kswapd_skip_congestion_wait = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_active_anon && strcmp(name, "nr_active_anon") == 0) nr_active_anon = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_active_file && strcmp(name, "nr_active_file") == 0) nr_active_file = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_anon_pages && strcmp(name, "nr_anon_pages") == 0) nr_anon_pages = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_anon_transparent_hugepages && strcmp(name, "nr_anon_transparent_hugepages") == 0) nr_anon_transparent_hugepages = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_bounce && strcmp(name, "nr_bounce") == 0) nr_bounce = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_dirtied && strcmp(name, "nr_dirtied") == 0) nr_dirtied = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_dirty && strcmp(name, "nr_dirty") == 0) nr_dirty = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_dirty_background_threshold && strcmp(name, "nr_dirty_background_threshold") == 0) nr_dirty_background_threshold = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_dirty_threshold && strcmp(name, "nr_dirty_threshold") == 0) nr_dirty_threshold = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_file_pages && strcmp(name, "nr_file_pages") == 0) nr_file_pages = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_free_pages && strcmp(name, "nr_free_pages") == 0) nr_free_pages = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_inactive_anon && strcmp(name, "nr_inactive_anon") == 0) nr_inactive_anon = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_inactive_file && strcmp(name, "nr_inactive_file") == 0) nr_inactive_file = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_isolated_anon && strcmp(name, "nr_isolated_anon") == 0) nr_isolated_anon = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_isolated_file && strcmp(name, "nr_isolated_file") == 0) nr_isolated_file = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_kernel_stack && strcmp(name, "nr_kernel_stack") == 0) nr_kernel_stack = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_mapped && strcmp(name, "nr_mapped") == 0) nr_mapped = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_mlock && strcmp(name, "nr_mlock") == 0) nr_mlock = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_page_table_pages && strcmp(name, "nr_page_table_pages") == 0) nr_page_table_pages = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_shmem && strcmp(name, "nr_shmem") == 0) nr_shmem = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_slab_reclaimable && strcmp(name, "nr_slab_reclaimable") == 0) nr_slab_reclaimable = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_slab_unreclaimable && strcmp(name, "nr_slab_unreclaimable") == 0) nr_slab_unreclaimable = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_unevictable && strcmp(name, "nr_unevictable") == 0) nr_unevictable = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_unstable && strcmp(name, "nr_unstable") == 0) nr_unstable = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_vmscan_immediate_reclaim && strcmp(name, "nr_vmscan_immediate_reclaim") == 0) nr_vmscan_immediate_reclaim = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_vmscan_write && strcmp(name, "nr_vmscan_write") == 0) nr_vmscan_write = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_writeback && strcmp(name, "nr_writeback") == 0) nr_writeback = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_writeback_temp && strcmp(name, "nr_writeback_temp") == 0) nr_writeback_temp = strtoull(value, NULL, 10);
+        // else if(hash == hash_nr_written && strcmp(name, "nr_written") == 0) nr_written = strtoull(value, NULL, 10);
+        // else if(hash == hash_pageoutrun && strcmp(name, "pageoutrun") == 0) pageoutrun = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgactivate && strcmp(name, "pgactivate") == 0) pgactivate = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgalloc_dma && strcmp(name, "pgalloc_dma") == 0) pgalloc_dma = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgalloc_dma32 && strcmp(name, "pgalloc_dma32") == 0) pgalloc_dma32 = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgalloc_movable && strcmp(name, "pgalloc_movable") == 0) pgalloc_movable = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgalloc_normal && strcmp(name, "pgalloc_normal") == 0) pgalloc_normal = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgdeactivate && strcmp(name, "pgdeactivate") == 0) pgdeactivate = strtoull(value, NULL, 10);
+        else if(hash == hash_pgfault && strcmp(name, "pgfault") == 0) pgfault = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgfree && strcmp(name, "pgfree") == 0) pgfree = strtoull(value, NULL, 10);
+        // else if(hash == hash_pginodesteal && strcmp(name, "pginodesteal") == 0) pginodesteal = strtoull(value, NULL, 10);
+        else if(hash == hash_pgmajfault && strcmp(name, "pgmajfault") == 0) pgmajfault = strtoull(value, NULL, 10);
+        else if(hash == hash_pgpgin && strcmp(name, "pgpgin") == 0) pgpgin = strtoull(value, NULL, 10);
+        else if(hash == hash_pgpgout && strcmp(name, "pgpgout") == 0) pgpgout = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgrefill_dma && strcmp(name, "pgrefill_dma") == 0) pgrefill_dma = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgrefill_dma32 && strcmp(name, "pgrefill_dma32") == 0) pgrefill_dma32 = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgrefill_movable && strcmp(name, "pgrefill_movable") == 0) pgrefill_movable = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgrefill_normal && strcmp(name, "pgrefill_normal") == 0) pgrefill_normal = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgrotated && strcmp(name, "pgrotated") == 0) pgrotated = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_direct_dma && strcmp(name, "pgscan_direct_dma") == 0) pgscan_direct_dma = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_direct_dma32 && strcmp(name, "pgscan_direct_dma32") == 0) pgscan_direct_dma32 = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_direct_movable && strcmp(name, "pgscan_direct_movable") == 0) pgscan_direct_movable = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_direct_normal && strcmp(name, "pgscan_direct_normal") == 0) pgscan_direct_normal = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_kswapd_dma && strcmp(name, "pgscan_kswapd_dma") == 0) pgscan_kswapd_dma = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_kswapd_dma32 && strcmp(name, "pgscan_kswapd_dma32") == 0) pgscan_kswapd_dma32 = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_kswapd_movable && strcmp(name, "pgscan_kswapd_movable") == 0) pgscan_kswapd_movable = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgscan_kswapd_normal && strcmp(name, "pgscan_kswapd_normal") == 0) pgscan_kswapd_normal = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_direct_dma && strcmp(name, "pgsteal_direct_dma") == 0) pgsteal_direct_dma = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_direct_dma32 && strcmp(name, "pgsteal_direct_dma32") == 0) pgsteal_direct_dma32 = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_direct_movable && strcmp(name, "pgsteal_direct_movable") == 0) pgsteal_direct_movable = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_direct_normal && strcmp(name, "pgsteal_direct_normal") == 0) pgsteal_direct_normal = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_kswapd_dma && strcmp(name, "pgsteal_kswapd_dma") == 0) pgsteal_kswapd_dma = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_kswapd_dma32 && strcmp(name, "pgsteal_kswapd_dma32") == 0) pgsteal_kswapd_dma32 = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_kswapd_movable && strcmp(name, "pgsteal_kswapd_movable") == 0) pgsteal_kswapd_movable = strtoull(value, NULL, 10);
+        // else if(hash == hash_pgsteal_kswapd_normal && strcmp(name, "pgsteal_kswapd_normal") == 0) pgsteal_kswapd_normal = strtoull(value, NULL, 10);
+        else if(hash == hash_pswpin && strcmp(name, "pswpin") == 0) pswpin = strtoull(value, NULL, 10);
+        else if(hash == hash_pswpout && strcmp(name, "pswpout") == 0) pswpout = strtoull(value, NULL, 10);
+        // else if(hash == hash_slabs_scanned && strcmp(name, "slabs_scanned") == 0) slabs_scanned = strtoull(value, NULL, 10);
+        // else if(hash == hash_thp_collapse_alloc && strcmp(name, "thp_collapse_alloc") == 0) thp_collapse_alloc = strtoull(value, NULL, 10);
+        // else if(hash == hash_thp_collapse_alloc_failed && strcmp(name, "thp_collapse_alloc_failed") == 0) thp_collapse_alloc_failed = strtoull(value, NULL, 10);
+        // else if(hash == hash_thp_fault_alloc && strcmp(name, "thp_fault_alloc") == 0) thp_fault_alloc = strtoull(value, NULL, 10);
+        // else if(hash == hash_thp_fault_fallback && strcmp(name, "thp_fault_fallback") == 0) thp_fault_fallback = strtoull(value, NULL, 10);
+        // else if(hash == hash_thp_split && strcmp(name, "thp_split") == 0) thp_split = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_cleared && strcmp(name, "unevictable_pgs_cleared") == 0) unevictable_pgs_cleared = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_culled && strcmp(name, "unevictable_pgs_culled") == 0) unevictable_pgs_culled = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_mlocked && strcmp(name, "unevictable_pgs_mlocked") == 0) unevictable_pgs_mlocked = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_mlockfreed && strcmp(name, "unevictable_pgs_mlockfreed") == 0) unevictable_pgs_mlockfreed = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_munlocked && strcmp(name, "unevictable_pgs_munlocked") == 0) unevictable_pgs_munlocked = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_rescued && strcmp(name, "unevictable_pgs_rescued") == 0) unevictable_pgs_rescued = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_scanned && strcmp(name, "unevictable_pgs_scanned") == 0) unevictable_pgs_scanned = strtoull(value, NULL, 10);
+        // else if(hash == hash_unevictable_pgs_stranded && strcmp(name, "unevictable_pgs_stranded") == 0) unevictable_pgs_stranded = strtoull(value, NULL, 10);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_swapio) {
-               static RRDSET *st_swapio = NULL;
-               if(!st_swapio) {
-                       st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
+    if(do_swapio) {
+        static RRDSET *st_swapio = NULL;
+        if(!st_swapio) {
+            st_swapio = rrdset_create("system", "swapio", NULL, "swap", NULL, "Swap I/O", "kilobytes/s", 250, update_every, RRDSET_TYPE_AREA);
 
-                       rrddim_add(st_swapio, "in",  NULL, sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
-                       rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st_swapio);
+            rrddim_add(st_swapio, "in",  NULL, sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
+            rrddim_add(st_swapio, "out", NULL, -sysconf(_SC_PAGESIZE), 1024, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st_swapio);
 
-               rrddim_set(st_swapio, "in", pswpin);
-               rrddim_set(st_swapio, "out", pswpout);
-               rrdset_done(st_swapio);
-       }
+        rrddim_set(st_swapio, "in", pswpin);
+        rrddim_set(st_swapio, "out", pswpout);
+        rrdset_done(st_swapio);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_io) {
-               static RRDSET *st_io = NULL;
-               if(!st_io) {
-                       st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
+    if(do_io) {
+        static RRDSET *st_io = NULL;
+        if(!st_io) {
+            st_io = rrdset_create("system", "io", NULL, "disk", NULL, "Disk I/O", "kilobytes/s", 150, update_every, RRDSET_TYPE_AREA);
 
-                       rrddim_add(st_io, "in",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st_io, "out", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st_io);
+            rrddim_add(st_io, "in",  NULL,  1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st_io, "out", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st_io);
 
-               rrddim_set(st_io, "in", pgpgin);
-               rrddim_set(st_io, "out", pgpgout);
-               rrdset_done(st_io);
-       }
+        rrddim_set(st_io, "in", pgpgin);
+        rrddim_set(st_io, "out", pgpgout);
+        rrdset_done(st_io);
+    }
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       if(do_pgfaults) {
-               static RRDSET *st_pgfaults = NULL;
-               if(!st_pgfaults) {
-                       st_pgfaults = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
-                       st_pgfaults->isdetail = 1;
+    if(do_pgfaults) {
+        static RRDSET *st_pgfaults = NULL;
+        if(!st_pgfaults) {
+            st_pgfaults = rrdset_create("mem", "pgfaults", NULL, "system", NULL, "Memory Page Faults", "page faults/s", 500, update_every, RRDSET_TYPE_LINE);
+            st_pgfaults->isdetail = 1;
 
-                       rrddim_add(st_pgfaults, "minor",  NULL,  1, 1, RRDDIM_INCREMENTAL);
-                       rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRDDIM_INCREMENTAL);
-               }
-               else rrdset_next(st_pgfaults);
+            rrddim_add(st_pgfaults, "minor",  NULL,  1, 1, RRDDIM_INCREMENTAL);
+            rrddim_add(st_pgfaults, "major", NULL, -1, 1, RRDDIM_INCREMENTAL);
+        }
+        else rrdset_next(st_pgfaults);
 
-               rrddim_set(st_pgfaults, "minor", pgfault);
-               rrddim_set(st_pgfaults, "major", pgmajfault);
-               rrdset_done(st_pgfaults);
-       }
+        rrddim_set(st_pgfaults, "minor", pgfault);
+        rrddim_set(st_pgfaults, "major", pgmajfault);
+        rrdset_done(st_pgfaults);
+    }
 
-       return 0;
+    return 0;
 }
 
index 25f9d2e749639077cd7f879c6f819f66600ac6a9..e2aa605820e4197dc1d9cb1809bcced5f53f21ed 100644 (file)
@@ -1,26 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <malloc.h>
-#include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
 #include "common.h"
-#include "log.h"
-#include "procfile.h"
-#include "../config.h"
 
 #define PF_PREFIX "PROCFILE"
 
@@ -41,48 +19,40 @@ size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER;
 
 
 pfwords *pfwords_add(pfwords *fw, char *str) {
-       // debug(D_PROCFILE, PF_PREFIX ":       adding word No %d: '%s'", fw->len, str);
+    // debug(D_PROCFILE, PF_PREFIX ":   adding word No %d: '%s'", fw->len, str);
 
-       if(unlikely(fw->len == fw->size)) {
-               // debug(D_PROCFILE, PF_PREFIX ":       expanding words");
+    if(unlikely(fw->len == fw->size)) {
+        // debug(D_PROCFILE, PF_PREFIX ":   expanding words");
 
-               pfwords *new = realloc(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
-               if(unlikely(!new)) {
-                       error(PF_PREFIX ":      failed to expand words");
-                       free(fw);
-                       return NULL;
-               }
-               fw = new;
-               fw->size += PFWORDS_INCREASE_STEP;
-       }
+        fw = reallocz(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
+        fw->size += PFWORDS_INCREASE_STEP;
+    }
 
-       fw->words[fw->len++] = str;
+    fw->words[fw->len++] = str;
 
-       return fw;
+    return fw;
 }
 
 pfwords *pfwords_new(void) {
-       // debug(D_PROCFILE, PF_PREFIX ":       initializing words");
-
-       uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
+    // debug(D_PROCFILE, PF_PREFIX ":   initializing words");
 
-       pfwords *new = malloc(sizeof(pfwords) + size * sizeof(char *));
-       if(unlikely(!new)) return NULL;
+    uint32_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;
 
-       new->len = 0;
-       new->size = size;
-       return new;
+    pfwords *new = mallocz(sizeof(pfwords) + size * sizeof(char *));
+    new->len = 0;
+    new->size = size;
+    return new;
 }
 
 void pfwords_reset(pfwords *fw) {
-       // debug(D_PROCFILE, PF_PREFIX ":       reseting words");
-       fw->len = 0;
+    // debug(D_PROCFILE, PF_PREFIX ":   reseting words");
+    fw->len = 0;
 }
 
 void pfwords_free(pfwords *fw) {
-       // debug(D_PROCFILE, PF_PREFIX ":       freeing words");
+    // debug(D_PROCFILE, PF_PREFIX ":   freeing words");
 
-       free(fw);
+    freez(fw);
 }
 
 
@@ -90,422 +60,402 @@ void pfwords_free(pfwords *fw) {
 // An array of lines
 
 pflines *pflines_add(pflines *fl, uint32_t first_word) {
-       // debug(D_PROCFILE, PF_PREFIX ":       adding line %d at word %d", fl->len, first_word);
+    // debug(D_PROCFILE, PF_PREFIX ":   adding line %d at word %d", fl->len, first_word);
 
-       if(unlikely(fl->len == fl->size)) {
-               // debug(D_PROCFILE, PF_PREFIX ":       expanding lines");
+    if(unlikely(fl->len == fl->size)) {
+        // debug(D_PROCFILE, PF_PREFIX ":   expanding lines");
 
-               pflines *new = realloc(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
-               if(unlikely(!new)) {
-                       error(PF_PREFIX ":      failed to expand lines");
-                       free(fl);
-                       return NULL;
-               }
-               fl = new;
-               fl->size += PFLINES_INCREASE_STEP;
-       }
+        fl = reallocz(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
+        fl->size += PFLINES_INCREASE_STEP;
+    }
 
-       fl->lines[fl->len].words = 0;
-       fl->lines[fl->len++].first = first_word;
+    fl->lines[fl->len].words = 0;
+    fl->lines[fl->len++].first = first_word;
 
-       return fl;
+    return fl;
 }
 
 pflines *pflines_new(void) {
-       // debug(D_PROCFILE, PF_PREFIX ":       initializing lines");
-
-       uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
+    // debug(D_PROCFILE, PF_PREFIX ":   initializing lines");
 
-       pflines *new = malloc(sizeof(pflines) + size * sizeof(ffline));
-       if(unlikely(!new)) return NULL;
+    uint32_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;
 
-       new->len = 0;
-       new->size = size;
-       return new;
+    pflines *new = mallocz(sizeof(pflines) + size * sizeof(ffline));
+    new->len = 0;
+    new->size = size;
+    return new;
 }
 
 void pflines_reset(pflines *fl) {
-       // debug(D_PROCFILE, PF_PREFIX ":       reseting lines");
+    // debug(D_PROCFILE, PF_PREFIX ":   reseting lines");
 
-       fl->len = 0;
+    fl->len = 0;
 }
 
 void pflines_free(pflines *fl) {
-       // debug(D_PROCFILE, PF_PREFIX ":       freeing lines");
+    // debug(D_PROCFILE, PF_PREFIX ":   freeing lines");
 
-       free(fl);
+    freez(fl);
 }
 
 
 // ----------------------------------------------------------------------------
 // The procfile
 
-#define PF_CHAR_IS_SEPARATOR   ' '
-#define PF_CHAR_IS_NEWLINE             'N'
-#define PF_CHAR_IS_WORD                        'W'
+#define PF_CHAR_IS_SEPARATOR    ' '
+#define PF_CHAR_IS_NEWLINE      'N'
+#define PF_CHAR_IS_WORD         'W'
 #define PF_CHAR_IS_QUOTE        'Q'
 #define PF_CHAR_IS_OPEN         'O'
 #define PF_CHAR_IS_CLOSE        'C'
 
 void procfile_close(procfile *ff) {
-       debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
+    debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
 
-       if(likely(ff->lines)) pflines_free(ff->lines);
-       if(likely(ff->words)) pfwords_free(ff->words);
+    if(likely(ff->lines)) pflines_free(ff->lines);
+    if(likely(ff->words)) pfwords_free(ff->words);
 
-       if(likely(ff->fd != -1)) close(ff->fd);
-       free(ff);
+    if(likely(ff->fd != -1)) close(ff->fd);
+    freez(ff);
 }
 
 procfile *procfile_parser(procfile *ff) {
-       debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
-
-       char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0;
-       uint32_t l = 0, w = 0;
-       int opened = 0;
-
-       ff->lines = pflines_add(ff->lines, w);
-       if(unlikely(!ff->lines)) goto cleanup;
-
-       while(likely(s < e)) {
-               // we are not at the end
-
-               switch(ff->separators[(uint8_t)(*s)]) {
-                       case PF_CHAR_IS_OPEN:
-                               if(s == t) {
-                                       opened++;
-                                       t = ++s;
-                               }
-                               else if(opened) {
-                                       opened++;
-                                       s++;
-                               }
-                               else
-                                       s++;
-                               continue;
-
-                       case PF_CHAR_IS_CLOSE:
-                               if(opened) {
-                                       opened--;
-
-                                       if(!opened) {
-                                               *s = '\0';
-                                               ff->words = pfwords_add(ff->words, t);
-                                               if(unlikely(!ff->words)) goto cleanup;
-
-                                               ff->lines->lines[l].words++;
-                                               w++;
-
-                                               t = ++s;
-                                       }
-                                       else
-                                               s++;
-                               }
-                               else
-                                       s++;
-                               continue;
-
-                       case PF_CHAR_IS_QUOTE:
-                               if(unlikely(!quote && s == t)) {
-                                       // quote opened at the beginning
-                                       quote = *s;
-                                       t = ++s;
-                               }
-                               else if(unlikely(quote && quote == *s)) {
-                                       // quote closed
-                                       quote = 0;
-
-                                       *s = '\0';
-                                       ff->words = pfwords_add(ff->words, t);
-                                       if(unlikely(!ff->words)) goto cleanup;
-
-                                       ff->lines->lines[l].words++;
-                                       w++;
-
-                                       t = ++s;
-                               }
-                               else
-                                       s++;
-                               continue;
-
-                       case PF_CHAR_IS_SEPARATOR:
-                               if(unlikely(quote || opened)) {
-                                       // we are inside a quote
-                                       s++;
-                                       continue;
-                               }
-
-                               if(unlikely(s == t)) {
-                                       // skip all leading white spaces
-                                       t = ++s;
-                                       continue;
-                               }
-
-                               // end of word
-                               *s = '\0';
-
-                               ff->words = pfwords_add(ff->words, t);
-                               if(unlikely(!ff->words)) goto cleanup;
-
-                               ff->lines->lines[l].words++;
-                               w++;
-
-                               t = ++s;
-                               continue;
-
-                       case PF_CHAR_IS_NEWLINE:
-                               // end of line
-                               *s = '\0';
-
-                               ff->words = pfwords_add(ff->words, t);
-                               if(unlikely(!ff->words)) goto cleanup;
-
-                               ff->lines->lines[l].words++;
-                               w++;
-
-                               // debug(D_PROCFILE, PF_PREFIX ":       ended line %d with %d words", l, ff->lines->lines[l].words);
-
-                               ff->lines = pflines_add(ff->lines, w);
-                               if(unlikely(!ff->lines)) goto cleanup;
-                               l++;
-
-                               t = ++s;
-                               continue;
-
-                       default:
-                               s++;
-                               continue;
-               }
-       }
-
-       if(likely(s > t && t < e)) {
-               // the last word
-               if(likely(ff->len < ff->size))
-                       *s = '\0';
-               else {
-                       // we are going to loose the last byte
-                       ff->data[ff->size - 1] = '\0';
-               }
-
-               ff->words = pfwords_add(ff->words, t);
-               if(unlikely(!ff->words)) goto cleanup;
-
-               ff->lines->lines[l].words++;
-               w++;
-       }
-
-       return ff;
+    debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
+
+    char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0;
+    uint32_t l = 0, w = 0;
+    int opened = 0;
+
+    ff->lines = pflines_add(ff->lines, w);
+    if(unlikely(!ff->lines)) goto cleanup;
+
+    while(likely(s < e)) {
+        // we are not at the end
+
+        switch(ff->separators[(uint8_t)(*s)]) {
+            case PF_CHAR_IS_OPEN:
+                if(s == t) {
+                    opened++;
+                    t = ++s;
+                }
+                else if(opened) {
+                    opened++;
+                    s++;
+                }
+                else
+                    s++;
+                continue;
+
+            case PF_CHAR_IS_CLOSE:
+                if(opened) {
+                    opened--;
+
+                    if(!opened) {
+                        *s = '\0';
+                        ff->words = pfwords_add(ff->words, t);
+                        if(unlikely(!ff->words)) goto cleanup;
+
+                        ff->lines->lines[l].words++;
+                        w++;
+
+                        t = ++s;
+                    }
+                    else
+                        s++;
+                }
+                else
+                    s++;
+                continue;
+
+            case PF_CHAR_IS_QUOTE:
+                if(unlikely(!quote && s == t)) {
+                    // quote opened at the beginning
+                    quote = *s;
+                    t = ++s;
+                }
+                else if(unlikely(quote && quote == *s)) {
+                    // quote closed
+                    quote = 0;
+
+                    *s = '\0';
+                    ff->words = pfwords_add(ff->words, t);
+                    if(unlikely(!ff->words)) goto cleanup;
+
+                    ff->lines->lines[l].words++;
+                    w++;
+
+                    t = ++s;
+                }
+                else
+                    s++;
+                continue;
+
+            case PF_CHAR_IS_SEPARATOR:
+                if(unlikely(quote || opened)) {
+                    // we are inside a quote
+                    s++;
+                    continue;
+                }
+
+                if(unlikely(s == t)) {
+                    // skip all leading white spaces
+                    t = ++s;
+                    continue;
+                }
+
+                // end of word
+                *s = '\0';
+
+                ff->words = pfwords_add(ff->words, t);
+                if(unlikely(!ff->words)) goto cleanup;
+
+                ff->lines->lines[l].words++;
+                w++;
+
+                t = ++s;
+                continue;
+
+            case PF_CHAR_IS_NEWLINE:
+                // end of line
+                *s = '\0';
+
+                ff->words = pfwords_add(ff->words, t);
+                if(unlikely(!ff->words)) goto cleanup;
+
+                ff->lines->lines[l].words++;
+                w++;
+
+                // debug(D_PROCFILE, PF_PREFIX ":   ended line %d with %d words", l, ff->lines->lines[l].words);
+
+                ff->lines = pflines_add(ff->lines, w);
+                if(unlikely(!ff->lines)) goto cleanup;
+                l++;
+
+                t = ++s;
+                continue;
+
+            default:
+                s++;
+                continue;
+        }
+    }
+
+    if(likely(s > t && t < e)) {
+        // the last word
+        if(likely(ff->len < ff->size))
+            *s = '\0';
+        else {
+            // we are going to loose the last byte
+            ff->data[ff->size - 1] = '\0';
+        }
+
+        ff->words = pfwords_add(ff->words, t);
+        if(unlikely(!ff->words)) goto cleanup;
+
+        ff->lines->lines[l].words++;
+        w++;
+    }
+
+    return ff;
 
 cleanup:
-       error(PF_PREFIX ": Failed to parse file '%s'", ff->filename);
-       procfile_close(ff);
-       return NULL;
+    error(PF_PREFIX ": Failed to parse file '%s'", ff->filename);
+    procfile_close(ff);
+    return NULL;
 }
 
 procfile *procfile_readall(procfile *ff) {
-       debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
-
-       ssize_t s, r = 1, x;
-       ff->len = 0;
-
-       while(likely(r > 0)) {
-               s = ff->len;
-               x = ff->size - s;
-
-               if(!x) {
-                       debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
-
-                       procfile *new = realloc(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
-                       if(unlikely(!new)) {
-                               error(PF_PREFIX ": Cannot allocate memory for file '%s'", ff->filename);
-                               procfile_close(ff);
-                               return NULL;
-                       }
-                       ff = new;
-                       ff->size += PROCFILE_INCREMENT_BUFFER;
-               }
-
-               debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", ff->filename, s, ff->size - s);
-               r = read(ff->fd, &ff->data[s], ff->size - s);
-               if(unlikely(r == -1)) {
-                       if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
-                       procfile_close(ff);
-                       return NULL;
-               }
-
-               ff->len += r;
-       }
-
-       debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
-       if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
-               if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
-               procfile_close(ff);
-               return NULL;
-       }
-
-       pflines_reset(ff->lines);
-       pfwords_reset(ff->words);
-
-       ff = procfile_parser(ff);
-
-       if(unlikely(procfile_adaptive_initial_allocation)) {
-               if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
-               if(unlikely(ff->lines->len > procfile_max_lines)) procfile_max_lines = ff->lines->len;
-               if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
-       }
-
-       debug(D_PROCFILE, "File '%s' updated.", ff->filename);
-       return ff;
+    debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
+
+    ssize_t s, r = 1, x;
+    ff->len = 0;
+
+    while(likely(r > 0)) {
+        s = ff->len;
+        x = ff->size - s;
+
+        if(!x) {
+            debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
+
+            ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
+            ff->size += PROCFILE_INCREMENT_BUFFER;
+        }
+
+        debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", ff->filename, s, ff->size - s);
+        r = read(ff->fd, &ff->data[s], ff->size - s);
+        if(unlikely(r == -1)) {
+            if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
+            procfile_close(ff);
+            return NULL;
+        }
+
+        ff->len += r;
+    }
+
+    debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
+    if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
+        if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
+        procfile_close(ff);
+        return NULL;
+    }
+
+    pflines_reset(ff->lines);
+    pfwords_reset(ff->words);
+
+    ff = procfile_parser(ff);
+
+    if(unlikely(procfile_adaptive_initial_allocation)) {
+        if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
+        if(unlikely(ff->lines->len > procfile_max_lines)) procfile_max_lines = ff->lines->len;
+        if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
+    }
+
+    debug(D_PROCFILE, "File '%s' updated.", ff->filename);
+    return ff;
 }
 
 static void procfile_set_separators(procfile *ff, const char *separators) {
-       static char def[256] = { [0 ... 255] = 0 };
-       int i;
-
-       if(unlikely(!def[255])) {
-               // this is thread safe
-               // we check that the last byte is non-zero
-               // if it is zero, multiple threads may be executing this at the same time
-               // setting in def[] the exact same values
-               for(i = 0; likely(i < 256) ;i++) {
-                       if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
-                       else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
-                       else def[i] = PF_CHAR_IS_WORD;
-               }
-       }
-
-       // copy the default
-       char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
-       while(likely(ffd != ffe)) *ffs++ = *ffd++;
-
-       // set the separators
-       if(unlikely(!separators))
-               separators = " \t=|";
-
-       ffs = ff->separators;
-       const char *s = separators;
-       while(likely(*s))
-               ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
+    static char def[256] = { [0 ... 255] = 0 };
+    int i;
+
+    if(unlikely(!def[255])) {
+        // this is thread safe
+        // we check that the last byte is non-zero
+        // if it is zero, multiple threads may be executing this at the same time
+        // setting in def[] the exact same values
+        for(i = 0; likely(i < 256) ;i++) {
+            if(unlikely(i == '\n' || i == '\r')) def[i] = PF_CHAR_IS_NEWLINE;
+            else if(unlikely(isspace(i) || !isprint(i))) def[i] = PF_CHAR_IS_SEPARATOR;
+            else def[i] = PF_CHAR_IS_WORD;
+        }
+    }
+
+    // copy the default
+    char *ffs = ff->separators, *ffd = def, *ffe = &def[256];
+    while(likely(ffd != ffe)) *ffs++ = *ffd++;
+
+    // set the separators
+    if(unlikely(!separators))
+        separators = " \t=|";
+
+    ffs = ff->separators;
+    const char *s = separators;
+    while(likely(*s))
+        ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
 }
 
 void procfile_set_quotes(procfile *ff, const char *quotes) {
-       // remove all quotes
-       int i;
-       for(i = 0; i < 256 ; i++)
-               if(ff->separators[i] == PF_CHAR_IS_QUOTE)
-                       ff->separators[i] = PF_CHAR_IS_WORD;
-
-       // if nothing given, return
-       if(unlikely(!quotes || !*quotes))
-               return;
-
-       // set the quotes
-       char *ffs = ff->separators;
-       const char *s = quotes;
-       while(likely(*s))
-               ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
+    // remove all quotes
+    int i;
+    for(i = 0; i < 256 ; i++)
+        if(ff->separators[i] == PF_CHAR_IS_QUOTE)
+            ff->separators[i] = PF_CHAR_IS_WORD;
+
+    // if nothing given, return
+    if(unlikely(!quotes || !*quotes))
+        return;
+
+    // set the quotes
+    char *ffs = ff->separators;
+    const char *s = quotes;
+    while(likely(*s))
+        ffs[(int)*s++] = PF_CHAR_IS_QUOTE;
 }
 
 void procfile_set_open_close(procfile *ff, const char *open, const char *close) {
-       // remove all open/close
-       int i;
-       for(i = 0; i < 256 ; i++)
-               if(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE)
-                       ff->separators[i] = PF_CHAR_IS_WORD;
-
-       // if nothing given, return
-       if(unlikely(!open || !*open || !close || !*close))
-               return;
-
-       // set the openings
-       char *ffs = ff->separators;
-       const char *s = open;
-       while(likely(*s))
-               ffs[(int)*s++] = PF_CHAR_IS_OPEN;
-
-       s = close;
-       while(likely(*s))
-               ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
+    // remove all open/close
+    int i;
+    for(i = 0; i < 256 ; i++)
+        if(ff->separators[i] == PF_CHAR_IS_OPEN || ff->separators[i] == PF_CHAR_IS_CLOSE)
+            ff->separators[i] = PF_CHAR_IS_WORD;
+
+    // if nothing given, return
+    if(unlikely(!open || !*open || !close || !*close))
+        return;
+
+    // set the openings
+    char *ffs = ff->separators;
+    const char *s = open;
+    while(likely(*s))
+        ffs[(int)*s++] = PF_CHAR_IS_OPEN;
+
+    s = close;
+    while(likely(*s))
+        ffs[(int)*s++] = PF_CHAR_IS_CLOSE;
 }
 
 procfile *procfile_open(const char *filename, const char *separators, uint32_t flags) {
-       debug(D_PROCFILE, PF_PREFIX ": Opening file '%s'", filename);
-
-       int fd = open(filename, O_RDONLY, 0666);
-       if(unlikely(fd == -1)) {
-               if(unlikely(!(flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot open file '%s'", filename);
-               return NULL;
-       }
-
-       size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
-       procfile *ff = malloc(sizeof(procfile) + size);
-       if(unlikely(!ff)) {
-               error(PF_PREFIX ": Cannot allocate memory for file '%s'", filename);
-               close(fd);
-               return NULL;
-       }
-
-       strncpyz(ff->filename, filename, FILENAME_MAX);
-
-       ff->fd = fd;
-       ff->size = size;
-       ff->len = 0;
-       ff->flags = flags;
-
-       ff->lines = pflines_new();
-       ff->words = pfwords_new();
-
-       if(unlikely(!ff->lines || !ff->words)) {
-               error(PF_PREFIX ": Cannot initialize parser for file '%s'", filename);
-               procfile_close(ff);
-               return NULL;
-       }
-
-       procfile_set_separators(ff, separators);
-
-       debug(D_PROCFILE, "File '%s' opened.", filename);
-       return ff;
+    debug(D_PROCFILE, PF_PREFIX ": Opening file '%s'", filename);
+
+    int fd = open(filename, O_RDONLY, 0666);
+    if(unlikely(fd == -1)) {
+        if(unlikely(!(flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot open file '%s'", filename);
+        return NULL;
+    }
+
+    size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
+    procfile *ff = mallocz(sizeof(procfile) + size);
+    strncpyz(ff->filename, filename, FILENAME_MAX);
+
+    ff->fd = fd;
+    ff->size = size;
+    ff->len = 0;
+    ff->flags = flags;
+
+    ff->lines = pflines_new();
+    ff->words = pfwords_new();
+
+    if(unlikely(!ff->lines || !ff->words)) {
+        error(PF_PREFIX ": Cannot initialize parser for file '%s'", filename);
+        procfile_close(ff);
+        return NULL;
+    }
+
+    procfile_set_separators(ff, separators);
+
+    debug(D_PROCFILE, "File '%s' opened.", filename);
+    return ff;
 }
 
 procfile *procfile_reopen(procfile *ff, const char *filename, const char *separators, uint32_t flags) {
-       if(unlikely(!ff)) return procfile_open(filename, separators, flags);
+    if(unlikely(!ff)) return procfile_open(filename, separators, flags);
 
-       if(likely(ff->fd != -1)) close(ff->fd);
+    if(likely(ff->fd != -1)) close(ff->fd);
 
-       ff->fd = open(filename, O_RDONLY, 0666);
-       if(unlikely(ff->fd == -1)) {
-               procfile_close(ff);
-               return NULL;
-       }
+    ff->fd = open(filename, O_RDONLY, 0666);
+    if(unlikely(ff->fd == -1)) {
+        procfile_close(ff);
+        return NULL;
+    }
 
-       strncpyz(ff->filename, filename, FILENAME_MAX);
+    strncpyz(ff->filename, filename, FILENAME_MAX);
 
-       ff->flags = flags;
+    ff->flags = flags;
 
-       // do not do the separators again if NULL is given
-       if(likely(separators)) procfile_set_separators(ff, separators);
+    // do not do the separators again if NULL is given
+    if(likely(separators)) procfile_set_separators(ff, separators);
 
-       return ff;
+    return ff;
 }
 
 // ----------------------------------------------------------------------------
 // example parsing of procfile data
 
 void procfile_print(procfile *ff) {
-       uint32_t lines = procfile_lines(ff), l;
-       uint32_t words, w;
-       char *s;
+    uint32_t lines = procfile_lines(ff), l;
+    uint32_t words, w;
+    char *s;
 
-       debug(D_PROCFILE, "File '%s' with %u lines and %u words", ff->filename, ff->lines->len, ff->words->len);
+    debug(D_PROCFILE, "File '%s' with %u lines and %u words", ff->filename, ff->lines->len, ff->words->len);
 
-       for(l = 0; likely(l < lines) ;l++) {
-               words = procfile_linewords(ff, l);
+    for(l = 0; likely(l < lines) ;l++) {
+        words = procfile_linewords(ff, l);
 
-               debug(D_PROCFILE, "     line %u starts at word %u and has %u words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
+        debug(D_PROCFILE, " line %u starts at word %u and has %u words", l, ff->lines->lines[l].first, ff->lines->lines[l].words);
 
-               for(w = 0; likely(w < words) ;w++) {
-                       s = procfile_lineword(ff, l, w);
-                       debug(D_PROCFILE, "             [%u.%u] '%s'", l, w, s);
-               }
-       }
+        for(w = 0; likely(w < words) ;w++) {
+            s = procfile_lineword(ff, l, w);
+            debug(D_PROCFILE, "     [%u.%u] '%s'", l, w, s);
+        }
+    }
 }
index 122e153f1eb7c76bc320fa98ca90b38afaf8d34e..5e00b25842613e205aba7052647965d6c17694cf 100644 (file)
@@ -30,9 +30,9 @@
 // An array of words
 
 typedef struct {
-       uint32_t len;   // used entries
-       uint32_t size;  // capacity
-       char *words[];  // array of pointers
+    uint32_t len;   // used entries
+    uint32_t size;  // capacity
+    char *words[];  // array of pointers
 } pfwords;
 
 
@@ -40,15 +40,15 @@ typedef struct {
 // An array of lines
 
 typedef struct {
-       uint32_t words;         // how many words this line has
-       uint32_t first;         // the id of the first word of this line
-                               // in the words array
+    uint32_t words;     // how many words this line has
+    uint32_t first;     // the id of the first word of this line
+                // in the words array
 } ffline;
 
 typedef struct {
-       uint32_t len;           // used entries
-       uint32_t size;          // capacity
-       ffline lines[];         // array of lines
+    uint32_t len;       // used entries
+    uint32_t size;      // capacity
+    ffline lines[];     // array of lines
 } pflines;
 
 
@@ -59,15 +59,15 @@ typedef struct {
 #define PROCFILE_FLAG_NO_ERROR_ON_FILE_IO 0x00000001
 
 typedef struct {
-       char filename[FILENAME_MAX + 1];
-       uint32_t flags;
-       int fd;                 // the file desriptor
-       size_t len;             // the bytes we have placed into data
-       size_t size;            // the bytes we have allocated for data
-       pflines *lines;
-       pfwords *words;
-       char separators[256];
-       char data[];            // allocated buffer to keep file contents
+    char filename[FILENAME_MAX + 1];
+    uint32_t flags;
+    int fd;         // the file desriptor
+    size_t len;     // the bytes we have placed into data
+    size_t size;        // the bytes we have allocated for data
+    pflines *lines;
+    pfwords *words;
+    char separators[256];
+    char data[];        // allocated buffer to keep file contents
 } procfile;
 
 // close the proc file and free all related memory
index 26004d4d9076d5596bbf4c78336105c57ce0ea8d..e43c16b1b759fea61f0c679810d07fd31aeb2a1e 100644 (file)
@@ -1,28 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <uuid/uuid.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "log.h"
 #include "common.h"
-#include "dictionary.h"
-#include "appconfig.h"
-
-#include "web_client.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "registry.h"
-
 
 // ----------------------------------------------------------------------------
 // TODO
 // COMMON structures
 
 struct registry {
-       int enabled;
-
-       char machine_guid[36 + 1];
-
-       // entries counters / statistics
-       unsigned long long persons_count;
-       unsigned long long machines_count;
-       unsigned long long usages_count;
-       unsigned long long urls_count;
-       unsigned long long persons_urls_count;
-       unsigned long long machines_urls_count;
-       unsigned long long log_count;
-
-       // memory counters / statistics
-       unsigned long long persons_memory;
-       unsigned long long machines_memory;
-       unsigned long long urls_memory;
-       unsigned long long persons_urls_memory;
-       unsigned long long machines_urls_memory;
-
-       // configuration
-       unsigned long long save_registry_every_entries;
-       char *registry_domain;
-       char *hostname;
-       char *registry_to_announce;
-       time_t persons_expiration; // seconds to expire idle persons
-       int verify_cookies_redirects;
-
-       size_t max_url_length;
-       size_t max_name_length;
-
-       // file/path names
-       char *pathname;
-       char *db_filename;
-       char *log_filename;
-       char *machine_guid_filename;
-
-       // open files
-       FILE *log_fp;
-
-       // the database
-       DICTIONARY *persons;    // dictionary of PERSON *, with key the PERSON.guid
-       DICTIONARY *machines;   // dictionary of MACHINE *, with key the MACHINE.guid
-       DICTIONARY *urls;               // dictionary of URL *, with key the URL.url
-
-       // concurrency locking
-       // we keep different locks for different things
-       // so that many tasks can be completed in parallel
-       pthread_mutex_t persons_lock;
-       pthread_mutex_t machines_lock;
-       pthread_mutex_t urls_lock;
-       pthread_mutex_t person_urls_lock;
-       pthread_mutex_t machine_urls_lock;
-       pthread_mutex_t log_lock;
+    int enabled;
+
+    char machine_guid[36 + 1];
+
+    // entries counters / statistics
+    unsigned long long persons_count;
+    unsigned long long machines_count;
+    unsigned long long usages_count;
+    unsigned long long urls_count;
+    unsigned long long persons_urls_count;
+    unsigned long long machines_urls_count;
+    unsigned long long log_count;
+
+    // memory counters / statistics
+    unsigned long long persons_memory;
+    unsigned long long machines_memory;
+    unsigned long long urls_memory;
+    unsigned long long persons_urls_memory;
+    unsigned long long machines_urls_memory;
+
+    // configuration
+    unsigned long long save_registry_every_entries;
+    char *registry_domain;
+    char *hostname;
+    char *registry_to_announce;
+    time_t persons_expiration; // seconds to expire idle persons
+    int verify_cookies_redirects;
+
+    size_t max_url_length;
+    size_t max_name_length;
+
+    // file/path names
+    char *pathname;
+    char *db_filename;
+    char *log_filename;
+    char *machine_guid_filename;
+
+    // open files
+    FILE *log_fp;
+
+    // the database
+    DICTIONARY *persons;    // dictionary of PERSON *, with key the PERSON.guid
+    DICTIONARY *machines;   // dictionary of MACHINE *, with key the MACHINE.guid
+    DICTIONARY *urls;       // dictionary of URL *, with key the URL.url
+
+    // concurrency locking
+    // we keep different locks for different things
+    // so that many tasks can be completed in parallel
+    pthread_mutex_t persons_lock;
+    pthread_mutex_t machines_lock;
+    pthread_mutex_t urls_lock;
+    pthread_mutex_t person_urls_lock;
+    pthread_mutex_t machine_urls_lock;
+    pthread_mutex_t log_lock;
 } registry;
 
 
@@ -127,9 +103,9 @@ struct registry {
 // we store them here and we keep pointers elsewhere
 
 struct url {
-       uint32_t links; // the number of links to this URL - when none is left, we free it
-       uint16_t len;   // the length of the URL in bytes
-       char url[1];    // the URL - dynamically allocated to more size
+    uint32_t links; // the number of links to this URL - when none is left, we free it
+    uint16_t len;   // the length of the URL in bytes
+    char url[1];    // the URL - dynamically allocated to more size
 };
 typedef struct url URL;
 
@@ -139,27 +115,27 @@ typedef struct url URL;
 
 // For each MACHINE-URL pair we keep this
 struct machine_url {
-       URL *url;                                       // de-duplicated URL
-//     DICTIONARY *persons;            // dictionary of PERSON *
+    URL *url;                   // de-duplicated URL
+//  DICTIONARY *persons;        // dictionary of PERSON *
 
-       uint8_t flags;
-       uint32_t first_t;                       // the first time we saw this
-       uint32_t last_t;                        // the last time we saw this
-       uint32_t usages;                        // how many times this has been accessed
+    uint8_t flags;
+    uint32_t first_t;           // the first time we saw this
+    uint32_t last_t;            // the last time we saw this
+    uint32_t usages;            // how many times this has been accessed
 };
 typedef struct machine_url MACHINE_URL;
 
 // A machine
 struct machine {
-       char guid[36 + 1];                      // the GUID
+    char guid[36 + 1];          // the GUID
 
-       uint32_t links;                         // the number of PERSON_URLs linked to this machine
+    uint32_t links;             // the number of PERSON_URLs linked to this machine
 
-       DICTIONARY *urls;                       // MACHINE_URL *
+    DICTIONARY *urls;           // MACHINE_URL *
 
-       uint32_t first_t;                       // the first time we saw this
-       uint32_t last_t;                        // the last time we saw this
-       uint32_t usages;                        // how many times this has been accessed
+    uint32_t first_t;           // the first time we saw this
+    uint32_t last_t;            // the last time we saw this
+    uint32_t usages;            // how many times this has been accessed
 };
 typedef struct machine MACHINE;
 
@@ -169,28 +145,28 @@ typedef struct machine MACHINE;
 
 // for each PERSON-URL pair we keep this
 struct person_url {
-       URL *url;                                       // de-duplicated URL
-       MACHINE *machine;                       // link the MACHINE of this URL
+    URL *url;                   // de-duplicated URL
+    MACHINE *machine;           // link the MACHINE of this URL
 
-       uint8_t flags;
-       uint32_t first_t;                       // the first time we saw this
-       uint32_t last_t;                        // the last time we saw this
-       uint32_t usages;                        // how many times this has been accessed
+    uint8_t flags;
+    uint32_t first_t;           // the first time we saw this
+    uint32_t last_t;            // the last time we saw this
+    uint32_t usages;            // how many times this has been accessed
 
-       char name[1];                           // the name of the URL, as known by the user
-                                                               // dynamically allocated to fit properly
+    char name[1];               // the name of the URL, as known by the user
+                                // dynamically allocated to fit properly
 };
 typedef struct person_url PERSON_URL;
 
 // A person
 struct person {
-       char guid[36 + 1];                      // the person GUID
+    char guid[36 + 1];          // the person GUID
 
-       DICTIONARY *urls;                       // dictionary of PERSON_URL *
+    DICTIONARY *urls;           // dictionary of PERSON_URL *
 
-       uint32_t first_t;                       // the first time we saw this
-       uint32_t last_t;                        // the last time we saw this
-       uint32_t usages;                        // how many times this has been accessed
+    uint32_t first_t;           // the first time we saw this
+    uint32_t last_t;            // the last time we saw this
+    uint32_t usages;            // how many times this has been accessed
 };
 typedef struct person PERSON;
 
@@ -199,27 +175,27 @@ typedef struct person PERSON;
 // REGISTRY concurrency locking
 
 static inline void registry_persons_lock(void) {
-       pthread_mutex_lock(&registry.persons_lock);
+    pthread_mutex_lock(&registry.persons_lock);
 }
 
 static inline void registry_persons_unlock(void) {
-       pthread_mutex_unlock(&registry.persons_lock);
+    pthread_mutex_unlock(&registry.persons_lock);
 }
 
 static inline void registry_machines_lock(void) {
-       pthread_mutex_lock(&registry.machines_lock);
+    pthread_mutex_lock(&registry.machines_lock);
 }
 
 static inline void registry_machines_unlock(void) {
-       pthread_mutex_unlock(&registry.machines_lock);
+    pthread_mutex_unlock(&registry.machines_lock);
 }
 
 static inline void registry_urls_lock(void) {
-       pthread_mutex_lock(&registry.urls_lock);
+    pthread_mutex_lock(&registry.urls_lock);
 }
 
 static inline void registry_urls_unlock(void) {
-       pthread_mutex_unlock(&registry.urls_lock);
+    pthread_mutex_unlock(&registry.urls_lock);
 }
 
 // ideally, we should not lock the whole registry for
@@ -227,13 +203,13 @@ static inline void registry_urls_unlock(void) {
 // however, to save the memory required for keeping a
 // mutex (40 bytes) per person, we do...
 static inline void registry_person_urls_lock(PERSON *p) {
-       (void)p;
-       pthread_mutex_lock(&registry.person_urls_lock);
+    (void)p;
+    pthread_mutex_lock(&registry.person_urls_lock);
 }
 
 static inline void registry_person_urls_unlock(PERSON *p) {
-       (void)p;
-       pthread_mutex_unlock(&registry.person_urls_lock);
+    (void)p;
+    pthread_mutex_unlock(&registry.person_urls_lock);
 }
 
 // ideally, we should not lock the whole registry for
@@ -241,21 +217,21 @@ static inline void registry_person_urls_unlock(PERSON *p) {
 // however, to save the memory required for keeping a
 // mutex (40 bytes) per machine, we do...
 static inline void registry_machine_urls_lock(MACHINE *m) {
-       (void)m;
-       pthread_mutex_lock(&registry.machine_urls_lock);
+    (void)m;
+    pthread_mutex_lock(&registry.machine_urls_lock);
 }
 
 static inline void registry_machine_urls_unlock(MACHINE *m) {
-       (void)m;
-       pthread_mutex_unlock(&registry.machine_urls_lock);
+    (void)m;
+    pthread_mutex_unlock(&registry.machine_urls_lock);
 }
 
 static inline void registry_log_lock(void) {
-       pthread_mutex_lock(&registry.log_lock);
+    pthread_mutex_lock(&registry.log_lock);
 }
 
 static inline void registry_log_unlock(void) {
-       pthread_mutex_unlock(&registry.log_lock);
+    pthread_mutex_unlock(&registry.log_lock);
 }
 
 
@@ -265,58 +241,58 @@ static inline void registry_log_unlock(void) {
 // parse a GUID and re-generated to be always lower case
 // this is used as a protection against the variations of GUIDs
 static inline int registry_regenerate_guid(const char *guid, char *result) {
-       uuid_t uuid;
-       if(unlikely(uuid_parse(guid, uuid) == -1)) {
-               info("Registry: GUID '%s' is not a valid GUID.", guid);
-               return -1;
-       }
-       else {
-               uuid_unparse_lower(uuid, result);
+    uuid_t uuid;
+    if(unlikely(uuid_parse(guid, uuid) == -1)) {
+        info("Registry: GUID '%s' is not a valid GUID.", guid);
+        return -1;
+    }
+    else {
+        uuid_unparse_lower(uuid, result);
 
 #ifdef NETDATA_INTERNAL_CHECKS
-               if(strcmp(guid, result))
-                       info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
+        if(strcmp(guid, result))
+            info("Registry: source GUID '%s' and re-generated GUID '%s' differ!", guid, result);
 #endif /* NETDATA_INTERNAL_CHECKS */
-       }
+    }
 
-       return 0;
+    return 0;
 }
 
 // make sure the names of the machines / URLs do not contain any tabs
 // (which are used as our separator in the database files)
 // and are properly trimmed (before and after)
 static inline char *registry_fix_machine_name(char *name, size_t *len) {
-       char *s = name?name:"";
+    char *s = name?name:"";
 
-       // skip leading spaces
-       while(*s && isspace(*s)) s++;
+    // skip leading spaces
+    while(*s && isspace(*s)) s++;
 
-       // make sure all spaces are a SPACE
-       char *t = s;
-       while(*t) {
-               if(unlikely(isspace(*t)))
-                       *t = ' ';
+    // make sure all spaces are a SPACE
+    char *t = s;
+    while(*t) {
+        if(unlikely(isspace(*t)))
+            *t = ' ';
 
-               t++;
-       }
+        t++;
+    }
 
-       // remove trailing spaces
-       while(--t >= s) {
-               if(*t == ' ')
-                       *t = '\0';
-               else
-                       break;
-       }
-       t++;
+    // remove trailing spaces
+    while(--t >= s) {
+        if(*t == ' ')
+            *t = '\0';
+        else
+            break;
+    }
+    t++;
 
-       if(likely(len))
-               *len = (t - s);
+    if(likely(len))
+        *len = (t - s);
 
-       return s;
+    return s;
 }
 
 static inline char *registry_fix_url(char *url, size_t *len) {
-       return registry_fix_machine_name(url, len);
+    return registry_fix_machine_name(url, len);
 }
 
 
@@ -331,59 +307,57 @@ extern PERSON *registry_request_delete(char *person_guid, char *machine_guid, ch
 // URL
 
 static inline URL *registry_url_allocate_nolock(const char *url, size_t urllen) {
-       // protection from too big URLs
-       if(urllen > registry.max_url_length)
-               urllen = registry.max_url_length;
-
-       debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen);
-       URL *u = malloc(sizeof(URL) + urllen);
-       if(!u) fatal("Cannot allocate %zu bytes for URL '%s'", sizeof(URL) + urllen, url);
+    // protection from too big URLs
+    if(urllen > registry.max_url_length)
+        urllen = registry.max_url_length;
 
-       // a simple strcpy() should do the job
-       // but I prefer to be safe, since the caller specified urllen
-       strncpyz(u->url, url, urllen);
+    debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): allocating %zu bytes", url, sizeof(URL) + urllen);
+    URL *u = mallocz(sizeof(URL) + urllen);
 
-       u->len = urllen;
-       u->links = 0;
+    // a simple strcpy() should do the job
+    // but I prefer to be safe, since the caller specified urllen
+    u->len = (uint16_t)urllen;
+    strncpyz(u->url, url, u->len);
+    u->links = 0;
 
-       registry.urls_memory += sizeof(URL) + urllen;
+    registry.urls_memory += sizeof(URL) + urllen;
 
-       debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url);
-       dictionary_set(registry.urls, u->url, u, sizeof(URL));
+    debug(D_REGISTRY, "Registry: registry_url_allocate_nolock('%s'): indexing it", url);
+    dictionary_set(registry.urls, u->url, u, sizeof(URL));
 
-       return u;
+    return u;
 }
 
 static inline URL *registry_url_get(const char *url, size_t urllen) {
-       debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
+    debug(D_REGISTRY, "Registry: registry_url_get('%s')", url);
 
-       registry_urls_lock();
+    registry_urls_lock();
 
-       URL *u = dictionary_get(registry.urls, url);
-       if(!u) {
-               u = registry_url_allocate_nolock(url, urllen);
-               registry.urls_count++;
-       }
+    URL *u = dictionary_get(registry.urls, url);
+    if(!u) {
+        u = registry_url_allocate_nolock(url, urllen);
+        registry.urls_count++;
+    }
 
-       registry_urls_unlock();
+    registry_urls_unlock();
 
-       return u;
+    return u;
 }
 
 static inline void registry_url_link_nolock(URL *u) {
-       u->links++;
-       debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links);
+    u->links++;
+    debug(D_REGISTRY, "Registry: registry_url_link_nolock('%s'): URL has now %u links", u->url, u->links);
 }
 
 static inline void registry_url_unlink_nolock(URL *u) {
-       u->links--;
-       if(!u->links) {
-               debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
-               dictionary_del(registry.urls, u->url);
-               free(u);
-       }
-       else
-               debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
+    u->links--;
+    if(!u->links) {
+        debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): No more links for this URL", u->url);
+        dictionary_del(registry.urls, u->url);
+        freez(u);
+    }
+    else
+        debug(D_REGISTRY, "Registry: registry_url_unlink_nolock('%s'): URL has %u links left", u->url, u->links);
 }
 
 
@@ -391,78 +365,76 @@ static inline void registry_url_unlink_nolock(URL *u) {
 // MACHINE
 
 static inline MACHINE *registry_machine_find(const char *machine_guid) {
-       debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
-       return dictionary_get(registry.machines, machine_guid);
+    debug(D_REGISTRY, "Registry: registry_machine_find('%s')", machine_guid);
+    return dictionary_get(registry.machines, machine_guid);
 }
 
 static inline MACHINE_URL *registry_machine_url_allocate(MACHINE *m, URL *u, time_t when) {
-       debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(MACHINE_URL));
+    debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): allocating %zu bytes", m->guid, u->url, sizeof(MACHINE_URL));
 
-       MACHINE_URL *mu = malloc(sizeof(MACHINE_URL));
-       if(!mu) fatal("registry_machine_link_to_url('%s', '%s'): cannot allocate %zu bytes.", m->guid, u->url, sizeof(MACHINE_URL));
+    MACHINE_URL *mu = mallocz(sizeof(MACHINE_URL));
 
-       // mu->persons = dictionary_create(DICTIONARY_FLAGS);
-       // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
+    // mu->persons = dictionary_create(DICTIONARY_FLAGS);
+    // dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
 
-       mu->first_t = mu->last_t = when;
-       mu->usages = 1;
-       mu->url = u;
-       mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
+    mu->first_t = mu->last_t = (uint32_t)when;
+    mu->usages = 1;
+    mu->url = u;
+    mu->flags = REGISTRY_URL_FLAGS_DEFAULT;
 
-       registry.machines_urls_memory += sizeof(MACHINE_URL);
+    registry.machines_urls_memory += sizeof(MACHINE_URL);
 
-       debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url);
-       dictionary_set(m->urls, u->url, mu, sizeof(MACHINE_URL));
-       registry_url_link_nolock(u);
+    debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s'): indexing URL in machine", m->guid, u->url);
+    dictionary_set(m->urls, u->url, mu, sizeof(MACHINE_URL));
+    registry_url_link_nolock(u);
 
-       return mu;
+    return mu;
 }
 
 static inline MACHINE *registry_machine_allocate(const char *machine_guid, time_t when) {
-       debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(MACHINE));
+    debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating new machine, sizeof(MACHINE)=%zu", machine_guid, sizeof(MACHINE));
 
-       MACHINE *m = malloc(sizeof(MACHINE));
-       if(!m) fatal("Registry: cannot allocate memory for new machine '%s'", machine_guid);
+    MACHINE *m = mallocz(sizeof(MACHINE));
 
-       strncpyz(m->guid, machine_guid, 36);
+    strncpyz(m->guid, machine_guid, 36);
 
-       debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
-       m->urls = dictionary_create(DICTIONARY_FLAGS);
+    debug(D_REGISTRY, "Registry: registry_machine_allocate('%s'): creating dictionary of urls", machine_guid);
+    m->urls = dictionary_create(DICTIONARY_FLAGS);
 
-       m->first_t = m->last_t = when;
-       m->usages = 0;
+    m->first_t = m->last_t = (uint32_t)when;
+    m->usages = 0;
 
-       registry.machines_memory += sizeof(MACHINE);
+    registry.machines_memory += sizeof(MACHINE);
 
-       registry.machines_count++;
-       dictionary_set(registry.machines, m->guid, m, sizeof(MACHINE));
+    registry.machines_count++;
+    dictionary_set(registry.machines, m->guid, m, sizeof(MACHINE));
 
-       return m;
+    return m;
 }
 
 // 1. validate machine GUID
 // 2. if it is valid, find it or create it and return it
 // 3. if it is not valid, return NULL
 static inline MACHINE *registry_machine_get(const char *machine_guid, time_t when) {
-       MACHINE *m = NULL;
+    MACHINE *m = NULL;
 
-       registry_machines_lock();
+    registry_machines_lock();
 
-       if(likely(machine_guid && *machine_guid)) {
-               // validate it is a GUID
-               char buf[36 + 1];
-               if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
-                       info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
-               else {
-                       machine_guid = buf;
-                       m = registry_machine_find(machine_guid);
-                       if(!m) m = registry_machine_allocate(machine_guid, when);
-               }
-       }
+    if(likely(machine_guid && *machine_guid)) {
+        // validate it is a GUID
+        char buf[36 + 1];
+        if(unlikely(registry_regenerate_guid(machine_guid, buf) == -1))
+            info("Registry: machine guid '%s' is not a valid guid. Ignoring it.", machine_guid);
+        else {
+            machine_guid = buf;
+            m = registry_machine_find(machine_guid);
+            if(!m) m = registry_machine_allocate(machine_guid, when);
+        }
+    }
 
-       registry_machines_unlock();
+    registry_machines_unlock();
 
-       return m;
+    return m;
 }
 
 
@@ -470,101 +442,99 @@ static inline MACHINE *registry_machine_get(const char *machine_guid, time_t whe
 // PERSON
 
 static inline PERSON *registry_person_find(const char *person_guid) {
-       debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
-       return dictionary_get(registry.persons, person_guid);
+    debug(D_REGISTRY, "Registry: registry_person_find('%s')", person_guid);
+    return dictionary_get(registry.persons, person_guid);
 }
 
 static inline PERSON_URL *registry_person_url_allocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) {
-       // protection from too big names
-       if(namelen > registry.max_name_length)
-               namelen = registry.max_name_length;
+    // protection from too big names
+    if(namelen > registry.max_name_length)
+        namelen = registry.max_name_length;
 
-       debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
-                 sizeof(PERSON_URL) + namelen);
+    debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
+          sizeof(PERSON_URL) + namelen);
 
-       PERSON_URL *pu = malloc(sizeof(PERSON_URL) + namelen);
-       if(!pu) fatal("registry_person_url_allocate('%s', '%s', '%s'): cannot allocate %zu bytes.", p->guid, m->guid, u->url, sizeof(PERSON_URL) + namelen);
+    PERSON_URL *pu = mallocz(sizeof(PERSON_URL) + namelen);
 
-       // a simple strcpy() should do the job
-       // but I prefer to be safe, since the caller specified urllen
-       strncpyz(pu->name, name, namelen);
+    // a simple strcpy() should do the job
+    // but I prefer to be safe, since the caller specified urllen
+    strncpyz(pu->name, name, namelen);
 
-       pu->machine = m;
-       pu->first_t = pu->last_t = when;
-       pu->usages = 1;
-       pu->url = u;
-       pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
-       m->links++;
+    pu->machine = m;
+    pu->first_t = pu->last_t = when;
+    pu->usages = 1;
+    pu->url = u;
+    pu->flags = REGISTRY_URL_FLAGS_DEFAULT;
+    m->links++;
 
-       registry.persons_urls_memory += sizeof(PERSON_URL) + namelen;
+    registry.persons_urls_memory += sizeof(PERSON_URL) + namelen;
 
-       debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
-       dictionary_set(p->urls, u->url, pu, sizeof(PERSON_URL));
-       registry_url_link_nolock(u);
+    debug(D_REGISTRY, "registry_person_url_allocate('%s', '%s', '%s'): indexing URL in person", p->guid, m->guid, u->url);
+    dictionary_set(p->urls, u->url, pu, sizeof(PERSON_URL));
+    registry_url_link_nolock(u);
 
-       return pu;
+    return pu;
 }
 
 static inline PERSON_URL *registry_person_url_reallocate(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when, PERSON_URL *pu) {
-       // this function is needed to change the name of a PERSON_URL
+    // this function is needed to change the name of a PERSON_URL
 
-       debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
-                 sizeof(PERSON_URL) + namelen);
+    debug(D_REGISTRY, "registry_person_url_reallocate('%s', '%s', '%s'): allocating %zu bytes", p->guid, m->guid, u->url,
+          sizeof(PERSON_URL) + namelen);
 
-       PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
-       tpu->first_t = pu->first_t;
-       tpu->last_t = pu->last_t;
-       tpu->usages = pu->usages;
+    PERSON_URL *tpu = registry_person_url_allocate(p, m, u, name, namelen, when);
+    tpu->first_t = pu->first_t;
+    tpu->last_t = pu->last_t;
+    tpu->usages = pu->usages;
 
-       // ok, these are a hack - since the registry_person_url_allocate() is
-       // adding these, we have to subtract them
-       tpu->machine->links--;
-       registry.persons_urls_memory -= sizeof(PERSON_URL) + strlen(pu->name);
-       registry_url_unlink_nolock(u);
+    // ok, these are a hack - since the registry_person_url_allocate() is
+    // adding these, we have to subtract them
+    tpu->machine->links--;
+    registry.persons_urls_memory -= sizeof(PERSON_URL) + strlen(pu->name);
+    registry_url_unlink_nolock(u);
 
-       free(pu);
+    freez(pu);
 
-       return tpu;
+    return tpu;
 }
 
 static inline PERSON *registry_person_allocate(const char *person_guid, time_t when) {
-       PERSON *p = NULL;
+    PERSON *p = NULL;
 
-       debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(PERSON));
+    debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): allocating new person, sizeof(PERSON)=%zu", (person_guid)?person_guid:"", sizeof(PERSON));
 
-       p = malloc(sizeof(PERSON));
-       if(!p) fatal("Registry: cannot allocate memory for new person.");
+    p = mallocz(sizeof(PERSON));
 
-       if(!person_guid) {
-               for (; ;) {
-                       uuid_t uuid;
-                       uuid_generate(uuid);
-                       uuid_unparse_lower(uuid, p->guid);
+    if(!person_guid) {
+        for (; ;) {
+            uuid_t uuid;
+            uuid_generate(uuid);
+            uuid_unparse_lower(uuid, p->guid);
 
-                       debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid);
-                       if (!dictionary_get(registry.persons, p->guid)) {
-                               debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid);
-                               break;
-                       }
-                       else
-                               info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid);
-               }
-       }
-       else
-               strncpyz(p->guid, person_guid, 36);
+            debug(D_REGISTRY, "Registry: Checking if the generated person guid '%s' is unique", p->guid);
+            if (!dictionary_get(registry.persons, p->guid)) {
+                debug(D_REGISTRY, "Registry: generated person guid '%s' is unique", p->guid);
+                break;
+            }
+            else
+                info("Registry: generated person guid '%s' found in the registry. Retrying...", p->guid);
+        }
+    }
+    else
+        strncpyz(p->guid, person_guid, 36);
 
-       debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
-       p->urls = dictionary_create(DICTIONARY_FLAGS);
+    debug(D_REGISTRY, "Registry: registry_person_allocate('%s'): creating dictionary of urls", p->guid);
+    p->urls = dictionary_create(DICTIONARY_FLAGS);
 
-       p->first_t = p->last_t = when;
-       p->usages = 0;
+    p->first_t = p->last_t = when;
+    p->usages = 0;
 
-       registry.persons_memory += sizeof(PERSON);
+    registry.persons_memory += sizeof(PERSON);
 
-       registry.persons_count++;
-       dictionary_set(registry.persons, p->guid, p, sizeof(PERSON));
+    registry.persons_count++;
+    dictionary_set(registry.persons, p->guid, p, sizeof(PERSON));
 
-       return p;
+    return p;
 }
 
 
@@ -573,260 +543,260 @@ static inline PERSON *registry_person_allocate(const char *person_guid, time_t w
 // 3. if it is not valid, create a new one
 // 4. return it
 static inline PERSON *registry_person_get(const char *person_guid, time_t when) {
-       PERSON *p = NULL;
+    PERSON *p = NULL;
 
-       registry_persons_lock();
+    registry_persons_lock();
 
-       if(person_guid && *person_guid) {
-               char buf[36 + 1];
-               // validate it is a GUID
-               if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
-                       info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
-               else {
-                       person_guid = buf;
-                       p = registry_person_find(person_guid);
-                       if(!p) person_guid = NULL;
-               }
-       }
+    if(person_guid && *person_guid) {
+        char buf[36 + 1];
+        // validate it is a GUID
+        if(unlikely(registry_regenerate_guid(person_guid, buf) == -1))
+            info("Registry: person guid '%s' is not a valid guid. Ignoring it.", person_guid);
+        else {
+            person_guid = buf;
+            p = registry_person_find(person_guid);
+            if(!p) person_guid = NULL;
+        }
+    }
 
-       if(!p) p = registry_person_allocate(NULL, when);
+    if(!p) p = registry_person_allocate(NULL, when);
 
-       registry_persons_unlock();
+    registry_persons_unlock();
 
-       return p;
+    return p;
 }
 
 // ----------------------------------------------------------------------------
 // LINKING OF OBJECTS
 
 static inline PERSON_URL *registry_person_link_to_url(PERSON *p, MACHINE *m, URL *u, char *name, size_t namelen, time_t when) {
-       debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
-
-       registry_person_urls_lock(p);
-
-       PERSON_URL *pu = dictionary_get(p->urls, u->url);
-       if(!pu) {
-               debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
-               pu = registry_person_url_allocate(p, m, u, name, namelen, when);
-               registry.persons_urls_count++;
-       }
-       else {
-               debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
-               pu->usages++;
-               if(likely(pu->last_t < (uint32_t)when)) pu->last_t = when;
-
-               if(pu->machine != m) {
-                       MACHINE_URL *mu = dictionary_get(pu->machine->urls, u->url);
-                       if(mu) {
-                               info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
-                                        p->guid, m->guid, u->url, pu->machine->guid);
-                               mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
-                       }
-                       else {
-                               info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
-                                        p->guid, m->guid, u->url, pu->machine->guid);
-                       }
-
-                       pu->machine->links--;
-                       pu->machine = m;
-               }
-
-               if(strcmp(pu->name, name)) {
-                       // the name of the PERSON_URL has changed !
-                       pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
-               }
-       }
-
-       p->usages++;
-       if(likely(p->last_t < (uint32_t)when)) p->last_t = when;
-
-       if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
-               info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
-               pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
-       }
-
-       registry_person_urls_unlock(p);
-
-       return pu;
+    debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): searching for URL in person", p->guid, m->guid, u->url);
+
+    registry_person_urls_lock(p);
+
+    PERSON_URL *pu = dictionary_get(p->urls, u->url);
+    if(!pu) {
+        debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
+        pu = registry_person_url_allocate(p, m, u, name, namelen, when);
+        registry.persons_urls_count++;
+    }
+    else {
+        debug(D_REGISTRY, "registry_person_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
+        pu->usages++;
+        if(likely(pu->last_t < (uint32_t)when)) pu->last_t = when;
+
+        if(pu->machine != m) {
+            MACHINE_URL *mu = dictionary_get(pu->machine->urls, u->url);
+            if(mu) {
+                info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - expiring it from previous machine.",
+                     p->guid, m->guid, u->url, pu->machine->guid);
+                mu->flags |= REGISTRY_URL_FLAGS_EXPIRED;
+            }
+            else {
+                info("registry_person_link_to_url('%s', '%s', '%s'): URL switched machines (old was '%s') - but the URL is not linked to the old machine.",
+                     p->guid, m->guid, u->url, pu->machine->guid);
+            }
+
+            pu->machine->links--;
+            pu->machine = m;
+        }
+
+        if(strcmp(pu->name, name)) {
+            // the name of the PERSON_URL has changed !
+            pu = registry_person_url_reallocate(p, m, u, name, namelen, when, pu);
+        }
+    }
+
+    p->usages++;
+    if(likely(p->last_t < (uint32_t)when)) p->last_t = when;
+
+    if(pu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
+        info("registry_person_link_to_url('%s', '%s', '%s'): accessing an expired URL. Re-enabling URL.", p->guid, m->guid, u->url);
+        pu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
+    }
+
+    registry_person_urls_unlock(p);
+
+    return pu;
 }
 
 static inline MACHINE_URL *registry_machine_link_to_url(PERSON *p, MACHINE *m, URL *u, time_t when) {
-       debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url);
+    debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): searching for URL in machine", p->guid, m->guid, u->url);
 
-       registry_machine_urls_lock(m);
+    registry_machine_urls_lock(m);
 
-       MACHINE_URL *mu = dictionary_get(m->urls, u->url);
-       if(!mu) {
-               debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
-               mu = registry_machine_url_allocate(m, u, when);
-               registry.machines_urls_count++;
-       }
-       else {
-               debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
-               mu->usages++;
-               if(likely(mu->last_t < (uint32_t)when)) mu->last_t = when;
-       }
+    MACHINE_URL *mu = dictionary_get(m->urls, u->url);
+    if(!mu) {
+        debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): not found", p->guid, m->guid, u->url);
+        mu = registry_machine_url_allocate(m, u, when);
+        registry.machines_urls_count++;
+    }
+    else {
+        debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): found", p->guid, m->guid, u->url);
+        mu->usages++;
+        if(likely(mu->last_t < (uint32_t)when)) mu->last_t = when;
+    }
 
-       //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url);
-       //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
+    //debug(D_REGISTRY, "registry_machine_link_to_url('%s', '%s', '%s'): indexing person in machine", p->guid, m->guid, u->url);
+    //dictionary_set(mu->persons, p->guid, p, sizeof(PERSON));
 
-       m->usages++;
-       if(likely(m->last_t < (uint32_t)when)) m->last_t = when;
+    m->usages++;
+    if(likely(m->last_t < (uint32_t)when)) m->last_t = when;
 
-       if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
-               info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
-               mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
-       }
+    if(mu->flags & REGISTRY_URL_FLAGS_EXPIRED) {
+        info("registry_machine_link_to_url('%s', '%s', '%s'): accessing an expired URL.", p->guid, m->guid, u->url);
+        mu->flags &= ~REGISTRY_URL_FLAGS_EXPIRED;
+    }
 
-       registry_machine_urls_unlock(m);
+    registry_machine_urls_unlock(m);
 
-       return mu;
+    return mu;
 }
 
 // ----------------------------------------------------------------------------
 // REGISTRY LOG LOAD/SAVE
 
 static inline int registry_should_save_db(void) {
-       debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
-       return registry.log_count > registry.save_registry_every_entries;
+    debug(D_REGISTRY, "log entries %llu, max %llu", registry.log_count, registry.save_registry_every_entries);
+    return registry.log_count > registry.save_registry_every_entries;
 }
 
 static inline void registry_log(const char action, PERSON *p, MACHINE *m, URL *u, char *name) {
-       if(likely(registry.log_fp)) {
-               // we lock only if the file is open
-               // to allow replaying the log at registry_log_load()
-               registry_log_lock();
-
-               if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
-                               action,
-                               p->last_t,
-                               p->guid,
-                               m->guid,
-                               name,
-                               u->url) < 0))
-                       error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
-
-               // we increase the counter even on failures
-               // so that the registry will be saved periodically
-               registry.log_count++;
-
-               registry_log_unlock();
-
-               // this must be outside the log_lock(), or a deadlock will happen.
-               // registry_save() checks the same inside the log_lock, so only
-               // one thread will save the db
-               if(unlikely(registry_should_save_db()))
-                       registry_save();
-       }
+    if(likely(registry.log_fp)) {
+        // we lock only if the file is open
+        // to allow replaying the log at registry_log_load()
+        registry_log_lock();
+
+        if(unlikely(fprintf(registry.log_fp, "%c\t%08x\t%s\t%s\t%s\t%s\n",
+                action,
+                p->last_t,
+                p->guid,
+                m->guid,
+                name,
+                u->url) < 0))
+            error("Registry: failed to save log. Registry data may be lost in case of abnormal restart.");
+
+        // we increase the counter even on failures
+        // so that the registry will be saved periodically
+        registry.log_count++;
+
+        registry_log_unlock();
+
+        // this must be outside the log_lock(), or a deadlock will happen.
+        // registry_save() checks the same inside the log_lock, so only
+        // one thread will save the db
+        if(unlikely(registry_should_save_db()))
+            registry_save();
+    }
 }
 
 static inline int registry_log_open_nolock(void) {
-       if(registry.log_fp)
-               fclose(registry.log_fp);
+    if(registry.log_fp)
+        fclose(registry.log_fp);
 
-       registry.log_fp = fopen(registry.log_filename, "a");
+    registry.log_fp = fopen(registry.log_filename, "a");
 
-       if(registry.log_fp) {
-               if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0)
-                       error("Cannot set line buffering on registry log file.");
-               return 0;
-       }
+    if(registry.log_fp) {
+        if (setvbuf(registry.log_fp, NULL, _IOLBF, 0) != 0)
+            error("Cannot set line buffering on registry log file.");
+        return 0;
+    }
 
-       error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename);
-       return -1;
+    error("Cannot open registry log file '%s'. Registry data will be lost in case of netdata or server crash.", registry.log_filename);
+    return -1;
 }
 
 static inline void registry_log_close_nolock(void) {
-       if(registry.log_fp) {
-               fclose(registry.log_fp);
-               registry.log_fp = NULL;
-       }
+    if(registry.log_fp) {
+        fclose(registry.log_fp);
+        registry.log_fp = NULL;
+    }
 }
 
 static inline void registry_log_recreate_nolock(void) {
-       if(registry.log_fp != NULL) {
-               registry_log_close_nolock();
+    if(registry.log_fp != NULL) {
+        registry_log_close_nolock();
 
-               // open it with truncate
-               registry.log_fp = fopen(registry.log_filename, "w");
-               if(registry.log_fp) fclose(registry.log_fp);
-               else error("Cannot truncate registry log '%s'", registry.log_filename);
+        // open it with truncate
+        registry.log_fp = fopen(registry.log_filename, "w");
+        if(registry.log_fp) fclose(registry.log_fp);
+        else error("Cannot truncate registry log '%s'", registry.log_filename);
 
-               registry.log_fp = NULL;
+        registry.log_fp = NULL;
 
-               registry_log_open_nolock();
-       }
+        registry_log_open_nolock();
+    }
 }
 
 int registry_log_load(void) {
-       char *s, buf[4096 + 1];
-       size_t line = -1;
-
-       // closing the log is required here
-       // otherwise we will append to it the values we read
-       registry_log_close_nolock();
-
-       debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename);
-       FILE *fp = fopen(registry.log_filename, "r");
-       if(!fp)
-               error("Registry: cannot open registry file: %s", registry.log_filename);
-       else {
-               line = 0;
-               size_t len = 0;
-               while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
-                       line++;
-
-                       switch (s[0]) {
-                               case 'A': // accesses
-                               case 'D': // deletes
-
-                                       // verify it is valid
-                                       if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
-                                               error("Registry: log line %zu is wrong (len = %zu).", line, len);
-                                               continue;
-                                       }
-                                       s[1] = s[10] = s[47] = s[84] = '\0';
-
-                                       // get the variables
-                                       time_t when = strtoul(&s[2], NULL, 16);
-                                       char *person_guid = &s[11];
-                                       char *machine_guid = &s[48];
-                                       char *name = &s[85];
-
-                                       // skip the name to find the url
-                                       char *url = name;
-                                       while(*url && *url != '\t') url++;
-                                       if(!*url) {
-                                               error("Registry: log line %zu does not have a url.", line);
-                                               continue;
-                                       }
-                                       *url++ = '\0';
-
-                                       // make sure the person exists
-                                       // without this, a new person guid will be created
-                                       PERSON *p = registry_person_find(person_guid);
-                                       if(!p) p = registry_person_allocate(person_guid, when);
-
-                                       if(s[0] == 'A')
-                                               registry_request_access(p->guid, machine_guid, url, name, when);
-                                       else
-                                               registry_request_delete(p->guid, machine_guid, url, name, when);
-
-                                       break;
-
-                               default:
-                                       error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
-                                       break;
-                       }
-               }
-               
-               fclose(fp);
-       }
-
-       // open the log again
-       registry_log_open_nolock();
-
-       return line;
+    char *s, buf[4096 + 1];
+    size_t line = -1;
+
+    // closing the log is required here
+    // otherwise we will append to it the values we read
+    registry_log_close_nolock();
+
+    debug(D_REGISTRY, "Registry: loading active db from: %s", registry.log_filename);
+    FILE *fp = fopen(registry.log_filename, "r");
+    if(!fp)
+        error("Registry: cannot open registry file: %s", registry.log_filename);
+    else {
+        line = 0;
+        size_t len = 0;
+        while ((s = fgets_trim_len(buf, 4096, fp, &len))) {
+            line++;
+
+            switch (s[0]) {
+                case 'A': // accesses
+                case 'D': // deletes
+
+                    // verify it is valid
+                    if (unlikely(len < 85 || s[1] != '\t' || s[10] != '\t' || s[47] != '\t' || s[84] != '\t')) {
+                        error("Registry: log line %zu is wrong (len = %zu).", line, len);
+                        continue;
+                    }
+                    s[1] = s[10] = s[47] = s[84] = '\0';
+
+                    // get the variables
+                    time_t when = strtoul(&s[2], NULL, 16);
+                    char *person_guid = &s[11];
+                    char *machine_guid = &s[48];
+                    char *name = &s[85];
+
+                    // skip the name to find the url
+                    char *url = name;
+                    while(*url && *url != '\t') url++;
+                    if(!*url) {
+                        error("Registry: log line %zu does not have a url.", line);
+                        continue;
+                    }
+                    *url++ = '\0';
+
+                    // make sure the person exists
+                    // without this, a new person guid will be created
+                    PERSON *p = registry_person_find(person_guid);
+                    if(!p) p = registry_person_allocate(person_guid, when);
+
+                    if(s[0] == 'A')
+                        registry_request_access(p->guid, machine_guid, url, name, when);
+                    else
+                        registry_request_delete(p->guid, machine_guid, url, name, when);
+
+                    break;
+
+                default:
+                    error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.log_filename, s);
+                    break;
+            }
+        }
+        
+        fclose(fp);
+    }
+
+    // open the log again
+    registry_log_open_nolock();
+
+    return line;
 }
 
 
@@ -834,176 +804,176 @@ int registry_log_load(void) {
 // REGISTRY REQUESTS
 
 PERSON *registry_request_access(char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
-       debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
+    debug(D_REGISTRY, "registry_request_access('%s', '%s', '%s'): NEW REQUEST", (person_guid)?person_guid:"", machine_guid, url);
 
-       MACHINE *m = registry_machine_get(machine_guid, when);
-       if(!m) return NULL;
+    MACHINE *m = registry_machine_get(machine_guid, when);
+    if(!m) return NULL;
 
-       // make sure the name is valid
-       size_t namelen;
-       name = registry_fix_machine_name(name, &namelen);
+    // make sure the name is valid
+    size_t namelen;
+    name = registry_fix_machine_name(name, &namelen);
 
-       size_t urllen;
-       url = registry_fix_url(url, &urllen);
+    size_t urllen;
+    url = registry_fix_url(url, &urllen);
 
-       URL *u = registry_url_get(url, urllen);
-       PERSON *p = registry_person_get(person_guid, when);
+    URL *u = registry_url_get(url, urllen);
+    PERSON *p = registry_person_get(person_guid, when);
 
-       registry_person_link_to_url(p, m, u, name, namelen, when);
-       registry_machine_link_to_url(p, m, u, when);
+    registry_person_link_to_url(p, m, u, name, namelen, when);
+    registry_machine_link_to_url(p, m, u, when);
 
-       registry_log('A', p, m, u, name);
+    registry_log('A', p, m, u, name);
 
-       registry.usages_count++;
-       return p;
+    registry.usages_count++;
+    return p;
 }
 
 // verify the person, the machine and the URL exist in our DB
 PERSON_URL *registry_verify_request(char *person_guid, char *machine_guid, char *url, PERSON **pp, MACHINE **mm) {
-       char pbuf[36 + 1], mbuf[36 + 1];
-
-       if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
-               info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET");
-               return NULL;
-       }
-
-       // normalize the url
-       url = registry_fix_url(url, NULL);
-
-       // make sure the person GUID is valid
-       if(registry_regenerate_guid(person_guid, pbuf) == -1) {
-               info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
-               return NULL;
-       }
-       person_guid = pbuf;
-
-       // make sure the machine GUID is valid
-       if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
-               info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
-               return NULL;
-       }
-       machine_guid = mbuf;
-
-       // make sure the machine exists
-       MACHINE *m = registry_machine_find(machine_guid);
-       if(!m) {
-               info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
-               return NULL;
-       }
-       if(mm) *mm = m;
-
-       // make sure the person exist
-       PERSON *p = registry_person_find(person_guid);
-       if(!p) {
-               info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
-               return NULL;
-       }
-       if(pp) *pp = p;
-
-       PERSON_URL *pu = dictionary_get(p->urls, url);
-       if(!pu) {
-               info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
-               return NULL;
-       }
-       return pu;
+    char pbuf[36 + 1], mbuf[36 + 1];
+
+    if(!person_guid || !*person_guid || !machine_guid || !*machine_guid || !url || !*url) {
+        info("Registry Request Verification: invalid request! person: '%s', machine '%s', url '%s'", person_guid?person_guid:"UNSET", machine_guid?machine_guid:"UNSET", url?url:"UNSET");
+        return NULL;
+    }
+
+    // normalize the url
+    url = registry_fix_url(url, NULL);
+
+    // make sure the person GUID is valid
+    if(registry_regenerate_guid(person_guid, pbuf) == -1) {
+        info("Registry Request Verification: invalid person GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+        return NULL;
+    }
+    person_guid = pbuf;
+
+    // make sure the machine GUID is valid
+    if(registry_regenerate_guid(machine_guid, mbuf) == -1) {
+        info("Registry Request Verification: invalid machine GUID, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+        return NULL;
+    }
+    machine_guid = mbuf;
+
+    // make sure the machine exists
+    MACHINE *m = registry_machine_find(machine_guid);
+    if(!m) {
+        info("Registry Request Verification: machine not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+        return NULL;
+    }
+    if(mm) *mm = m;
+
+    // make sure the person exist
+    PERSON *p = registry_person_find(person_guid);
+    if(!p) {
+        info("Registry Request Verification: person not found, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+        return NULL;
+    }
+    if(pp) *pp = p;
+
+    PERSON_URL *pu = dictionary_get(p->urls, url);
+    if(!pu) {
+        info("Registry Request Verification: URL not found for person, person: '%s', machine '%s', url '%s'", person_guid, machine_guid, url);
+        return NULL;
+    }
+    return pu;
 }
 
 PERSON *registry_request_delete(char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
-       (void)when;
+    (void)when;
 
-       PERSON *p = NULL;
-       MACHINE *m = NULL;
-       PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
-       if(!pu || !p || !m) return NULL;
+    PERSON *p = NULL;
+    MACHINE *m = NULL;
+    PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
+    if(!pu || !p || !m) return NULL;
 
-       // normalize the url
-       delete_url = registry_fix_url(delete_url, NULL);
+    // normalize the url
+    delete_url = registry_fix_url(delete_url, NULL);
 
-       // make sure the user is not deleting the url it uses
-       if(!strcmp(delete_url, pu->url->url)) {
-               info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
-               return NULL;
-       }
+    // make sure the user is not deleting the url it uses
+    if(!strcmp(delete_url, pu->url->url)) {
+        info("Registry Delete Request: delete URL is the one currently accessed, person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
+        return NULL;
+    }
 
-       registry_person_urls_lock(p);
+    registry_person_urls_lock(p);
 
-       PERSON_URL *dpu = dictionary_get(p->urls, delete_url);
-       if(!dpu) {
-               info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
-               registry_person_urls_unlock(p);
-               return NULL;
-       }
+    PERSON_URL *dpu = dictionary_get(p->urls, delete_url);
+    if(!dpu) {
+        info("Registry Delete Request: URL not found for person: '%s', machine '%s', url '%s', delete url '%s'", p->guid, m->guid, pu->url->url, delete_url);
+        registry_person_urls_unlock(p);
+        return NULL;
+    }
 
-       registry_log('D', p, m, pu->url, dpu->url->url);
+    registry_log('D', p, m, pu->url, dpu->url->url);
 
-       dictionary_del(p->urls, dpu->url->url);
-       registry_url_unlink_nolock(dpu->url);
-       free(dpu);
+    dictionary_del(p->urls, dpu->url->url);
+    registry_url_unlink_nolock(dpu->url);
+    freez(dpu);
 
-       registry_person_urls_unlock(p);
-       return p;
+    registry_person_urls_unlock(p);
+    return p;
 }
 
 
 // a structure to pass to the dictionary_get_all() callback handler
 struct machine_request_callback_data {
-       MACHINE *find_this_machine;
-       PERSON_URL *result;
+    MACHINE *find_this_machine;
+    PERSON_URL *result;
 };
 
 // the callback function
 // this will be run for every PERSON_URL of this PERSON
 int machine_request_callback(void *entry, void *data) {
-       PERSON_URL *mypu = (PERSON_URL *)entry;
-       struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
+    PERSON_URL *mypu = (PERSON_URL *)entry;
+    struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
 
-       if(mypu->machine == myrdata->find_this_machine) {
-               myrdata->result = mypu;
-               return -1; // this will also stop the walk through
-       }
+    if(mypu->machine == myrdata->find_this_machine) {
+        myrdata->result = mypu;
+        return -1; // this will also stop the walk through
+    }
 
-       return 0; // continue
+    return 0; // continue
 }
 
 MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
-       (void)when;
+    (void)when;
 
-       char mbuf[36 + 1];
+    char mbuf[36 + 1];
 
-       PERSON *p = NULL;
-       MACHINE *m = NULL;
-       PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
-       if(!pu || !p || !m) return NULL;
+    PERSON *p = NULL;
+    MACHINE *m = NULL;
+    PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
+    if(!pu || !p || !m) return NULL;
 
-       // make sure the machine GUID is valid
-       if(registry_regenerate_guid(request_machine, mbuf) == -1) {
-               info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
-               return NULL;
-       }
-       request_machine = mbuf;
+    // make sure the machine GUID is valid
+    if(registry_regenerate_guid(request_machine, mbuf) == -1) {
+        info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+        return NULL;
+    }
+    request_machine = mbuf;
 
-       // make sure the machine exists
-       m = registry_machine_find(request_machine);
-       if(!m) {
-               info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
-               return NULL;
-       }
+    // make sure the machine exists
+    m = registry_machine_find(request_machine);
+    if(!m) {
+        info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, pu->url->url, request_machine);
+        return NULL;
+    }
 
-       // Verify the user has in the past accessed this machine
-       // We will walk through the PERSON_URLs to find the machine
-       // linking to our machine
+    // Verify the user has in the past accessed this machine
+    // We will walk through the PERSON_URLs to find the machine
+    // linking to our machine
 
-       // a structure to pass to the dictionary_get_all() callback handler
-       struct machine_request_callback_data rdata = { m, NULL };
+    // a structure to pass to the dictionary_get_all() callback handler
+    struct machine_request_callback_data rdata = { m, NULL };
 
-       // request a walk through on the dictionary
-       // no need for locking here, the underlying dictionary has its own
-       dictionary_get_all(p->urls, machine_request_callback, &rdata);
+    // request a walk through on the dictionary
+    // no need for locking here, the underlying dictionary has its own
+    dictionary_get_all(p->urls, machine_request_callback, &rdata);
 
-       if(rdata.result)
-               return m;
+    if(rdata.result)
+        return m;
 
-       return NULL;
+    return NULL;
 }
 
 
@@ -1015,242 +985,242 @@ MACHINE *registry_request_machine(char *person_guid, char *machine_guid, char *u
 #define REGISTRY_STATUS_DISABLED "disabled"
 
 int registry_verify_cookies_redirects(void) {
-       return registry.verify_cookies_redirects;
+    return registry.verify_cookies_redirects;
 }
 
 const char *registry_to_announce(void) {
-       return registry.registry_to_announce;
+    return registry.registry_to_announce;
 }
 
 void registry_set_cookie(struct web_client *w, const char *guid) {
-       char edate[100];
-       time_t et = time(NULL) + registry.persons_expiration;
-       struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
-       strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
+    char edate[100];
+    time_t et = time(NULL) + registry.persons_expiration;
+    struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
+    strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
 
-       snprintfz(w->cookie1, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", guid, edate);
+    snprintfz(w->cookie1, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s", guid, edate);
 
-       if(registry.registry_domain && registry.registry_domain[0])
-               snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate);
+    if(registry.registry_domain && registry.registry_domain[0])
+        snprintfz(w->cookie2, COOKIE_MAX, NETDATA_REGISTRY_COOKIE_NAME "=%s; Domain=%s; Expires=%s", guid, registry.registry_domain, edate);
 }
 
 static inline void registry_set_person_cookie(struct web_client *w, PERSON *p) {
-       registry_set_cookie(w, p->guid);
+    registry_set_cookie(w, p->guid);
 }
 
 static inline void registry_json_header(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);
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    buffer_sprintf(w->response.data, "{\n\t\"action\": \"%s\",\n\t\"status\": \"%s\",\n\t\"hostname\": \"%s\",\n\t\"machine_guid\": \"%s\"",
+                   action, status, registry.hostname, registry.machine_guid);
 }
 
 static inline void registry_json_footer(struct web_client *w) {
-       buffer_strcat(w->response.data, "\n}\n");
+    buffer_strcat(w->response.data, "\n}\n");
 }
 
 int registry_request_hello_json(struct web_client *w) {
-       registry_json_header(w, "hello", REGISTRY_STATUS_OK);
+    registry_json_header(w, "hello", REGISTRY_STATUS_OK);
 
-       buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
-                                  registry.registry_to_announce);
+    buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+                   registry.registry_to_announce);
 
-       registry_json_footer(w);
-       return 200;
+    registry_json_footer(w);
+    return 200;
 }
 
 static inline int registry_json_disabled(struct web_client *w, const char *action) {
-       registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
+    registry_json_header(w, action, REGISTRY_STATUS_DISABLED);
 
-       buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
-                                  registry.registry_to_announce);
+    buffer_sprintf(w->response.data, ",\n\t\"registry\": \"%s\"",
+                   registry.registry_to_announce);
 
-       registry_json_footer(w);
-       return 200;
+    registry_json_footer(w);
+    return 200;
 }
 
 // structure used be the callbacks below
 struct registry_json_walk_person_urls_callback {
-       PERSON *p;
-       MACHINE *m;
-       struct web_client *w;
-       int count;
+    PERSON *p;
+    MACHINE *m;
+    struct web_client *w;
+    int count;
 };
 
 // callback for rendering PERSON_URLs
 static inline int registry_json_person_url_callback(void *entry, void *data) {
-       PERSON_URL *pu = (PERSON_URL *)entry;
-       struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
-       struct web_client *w = c->w;
+    PERSON_URL *pu = (PERSON_URL *)entry;
+    struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
+    struct web_client *w = c->w;
 
-       if(unlikely(c->count++))
-               buffer_strcat(w->response.data, ",");
+    if(unlikely(c->count++))
+        buffer_strcat(w->response.data, ",");
 
-       buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
-                                  pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->name);
+    buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u, \"%s\" ]",
+                   pu->machine->guid, pu->url->url, pu->last_t, pu->usages, pu->name);
 
-       return 1;
+    return 1;
 }
 
 // callback for rendering MACHINE_URLs
 static inline int registry_json_machine_url_callback(void *entry, void *data) {
-       MACHINE_URL *mu = (MACHINE_URL *)entry;
-       struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
-       struct web_client *w = c->w;
-       MACHINE *m = c->m;
+    MACHINE_URL *mu = (MACHINE_URL *)entry;
+    struct registry_json_walk_person_urls_callback *c = (struct registry_json_walk_person_urls_callback *)data;
+    struct web_client *w = c->w;
+    MACHINE *m = c->m;
 
-       if(unlikely(c->count++))
-               buffer_strcat(w->response.data, ",");
+    if(unlikely(c->count++))
+        buffer_strcat(w->response.data, ",");
 
-       buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
-                                  m->guid, mu->url->url, mu->last_t, mu->usages);
+    buffer_sprintf(w->response.data, "\n\t\t[ \"%s\", \"%s\", %u000, %u ]",
+                   m->guid, mu->url->url, mu->last_t, mu->usages);
 
-       return 1;
+    return 1;
 }
 
 
 // the main method for registering an access
 int registry_request_access_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when) {
-       if(!registry.enabled)
-               return registry_json_disabled(w, "access");
+    if(!registry.enabled)
+        return registry_json_disabled(w, "access");
 
-       PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
-       if(!p) {
-               registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 412;
-       }
+    PERSON *p = registry_request_access(person_guid, machine_guid, url, name, when);
+    if(!p) {
+        registry_json_header(w, "access", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 412;
+    }
 
-       // set the cookie
-       registry_set_person_cookie(w, p);
+    // set the cookie
+    registry_set_person_cookie(w, p);
 
-       // generate the response
-       registry_json_header(w, "access", REGISTRY_STATUS_OK);
+    // generate the response
+    registry_json_header(w, "access", REGISTRY_STATUS_OK);
 
-       buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
-       struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
-       dictionary_get_all(p->urls, registry_json_person_url_callback, &c);
-       buffer_strcat(w->response.data, "\n\t]\n");
+    buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\",\n\t\"urls\": [", p->guid);
+    struct registry_json_walk_person_urls_callback c = { p, NULL, w, 0 };
+    dictionary_get_all(p->urls, registry_json_person_url_callback, &c);
+    buffer_strcat(w->response.data, "\n\t]\n");
 
-       registry_json_footer(w);
-       return 200;
+    registry_json_footer(w);
+    return 200;
 }
 
 // the main method for deleting a URL from a person
 int registry_request_delete_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when) {
-       if(!registry.enabled)
-               return registry_json_disabled(w, "delete");
+    if(!registry.enabled)
+        return registry_json_disabled(w, "delete");
 
-       PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
-       if(!p) {
-               registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 412;
-       }
+    PERSON *p = registry_request_delete(person_guid, machine_guid, url, delete_url, when);
+    if(!p) {
+        registry_json_header(w, "delete", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 412;
+    }
 
-       // generate the response
-       registry_json_header(w, "delete", REGISTRY_STATUS_OK);
-       registry_json_footer(w);
-       return 200;
+    // generate the response
+    registry_json_header(w, "delete", REGISTRY_STATUS_OK);
+    registry_json_footer(w);
+    return 200;
 }
 
 // the main method for searching the URLs of a netdata
 int registry_request_search_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
-       if(!registry.enabled)
-               return registry_json_disabled(w, "search");
+    if(!registry.enabled)
+        return registry_json_disabled(w, "search");
 
-       MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
-       if(!m) {
-               registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 404;
-       }
+    MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
+    if(!m) {
+        registry_json_header(w, "search", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 404;
+    }
 
-       registry_json_header(w, "search", REGISTRY_STATUS_OK);
+    registry_json_header(w, "search", REGISTRY_STATUS_OK);
 
-       buffer_strcat(w->response.data, ",\n\t\"urls\": [");
-       struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
-       dictionary_get_all(m->urls, registry_json_machine_url_callback, &c);
-       buffer_strcat(w->response.data, "\n\t]\n");
+    buffer_strcat(w->response.data, ",\n\t\"urls\": [");
+    struct registry_json_walk_person_urls_callback c = { NULL, m, w, 0 };
+    dictionary_get_all(m->urls, registry_json_machine_url_callback, &c);
+    buffer_strcat(w->response.data, "\n\t]\n");
 
-       registry_json_footer(w);
-       return 200;
+    registry_json_footer(w);
+    return 200;
 }
 
 // structure used be the callbacks below
 struct registry_person_url_callback_verify_machine_exists_data {
-       MACHINE *m;
-       int count;
+    MACHINE *m;
+    int count;
 };
 
 int registry_person_url_callback_verify_machine_exists(void *entry, void *data) {
-       struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
-       PERSON_URL *pu = (PERSON_URL *)entry;
-       MACHINE *m = d->m;
+    struct registry_person_url_callback_verify_machine_exists_data *d = (struct registry_person_url_callback_verify_machine_exists_data *)data;
+    PERSON_URL *pu = (PERSON_URL *)entry;
+    MACHINE *m = d->m;
 
-       if(pu->machine == m)
-               d->count++;
+    if(pu->machine == m)
+        d->count++;
 
-       return 0;
+    return 0;
 }
 
 // the main method for switching user identity
 int registry_request_switch_json(struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when) {
-       (void)url;
-       (void)when;
-
-       if(!registry.enabled)
-               return registry_json_disabled(w, "switch");
-
-       PERSON *op = registry_person_find(person_guid);
-       if(!op) {
-               registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 430;
-       }
-
-       PERSON *np = registry_person_find(new_person_guid);
-       if(!np) {
-               registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 431;
-       }
-
-       MACHINE *m = registry_machine_find(machine_guid);
-       if(!m) {
-               registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 432;
-       }
-
-       struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
-
-       // verify the old person has access to this machine
-       dictionary_get_all(op->urls, registry_person_url_callback_verify_machine_exists, &data);
-       if(!data.count) {
-               registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 433;
-       }
-
-       // verify the new person has access to this machine
-       data.count = 0;
-       dictionary_get_all(np->urls, registry_person_url_callback_verify_machine_exists, &data);
-       if(!data.count) {
-               registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
-               registry_json_footer(w);
-               return 434;
-       }
-
-       // set the cookie of the new person
-       // the user just switched identity
-       registry_set_person_cookie(w, np);
-
-       // generate the response
-       registry_json_header(w, "switch", REGISTRY_STATUS_OK);
-       buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
-       registry_json_footer(w);
-       return 200;
+    (void)url;
+    (void)when;
+
+    if(!registry.enabled)
+        return registry_json_disabled(w, "switch");
+
+    PERSON *op = registry_person_find(person_guid);
+    if(!op) {
+        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 430;
+    }
+
+    PERSON *np = registry_person_find(new_person_guid);
+    if(!np) {
+        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 431;
+    }
+
+    MACHINE *m = registry_machine_find(machine_guid);
+    if(!m) {
+        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 432;
+    }
+
+    struct registry_person_url_callback_verify_machine_exists_data data = { m, 0 };
+
+    // verify the old person has access to this machine
+    dictionary_get_all(op->urls, registry_person_url_callback_verify_machine_exists, &data);
+    if(!data.count) {
+        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 433;
+    }
+
+    // verify the new person has access to this machine
+    data.count = 0;
+    dictionary_get_all(np->urls, registry_person_url_callback_verify_machine_exists, &data);
+    if(!data.count) {
+        registry_json_header(w, "switch", REGISTRY_STATUS_FAILED);
+        registry_json_footer(w);
+        return 434;
+    }
+
+    // set the cookie of the new person
+    // the user just switched identity
+    registry_set_person_cookie(w, np);
+
+    // generate the response
+    registry_json_header(w, "switch", REGISTRY_STATUS_OK);
+    buffer_sprintf(w->response.data, ",\n\t\"person_guid\": \"%s\"", np->guid);
+    registry_json_footer(w);
+    return 200;
 }
 
 
@@ -1258,47 +1228,47 @@ int registry_request_switch_json(struct web_client *w, char *person_guid, char *
 // REGISTRY THIS MACHINE UNIQUE ID
 
 char *registry_get_this_machine_guid(void) {
-       if(likely(registry.machine_guid[0]))
-               return registry.machine_guid;
+    if(likely(registry.machine_guid[0]))
+        return registry.machine_guid;
 
-       // read it from disk
-       int fd = open(registry.machine_guid_filename, O_RDONLY);
-       if(fd != -1) {
-               char buf[36 + 1];
-               if(read(fd, buf, 36) != 36)
-                       error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
-               else {
-                       buf[36] = '\0';
-                       if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
-                               error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
-                                         buf, registry.machine_guid_filename);
+    // read it from disk
+    int fd = open(registry.machine_guid_filename, O_RDONLY);
+    if(fd != -1) {
+        char buf[36 + 1];
+        if(read(fd, buf, 36) != 36)
+            error("Failed to read machine GUID from '%s'", registry.machine_guid_filename);
+        else {
+            buf[36] = '\0';
+            if(registry_regenerate_guid(buf, registry.machine_guid) == -1) {
+                error("Failed to validate machine GUID '%s' from '%s'. Ignoring it - this might mean this netdata will appear as duplicate in the registry.",
+                      buf, registry.machine_guid_filename);
 
-                               registry.machine_guid[0] = '\0';
-                       }
-               }
-               close(fd);
-       }
+                registry.machine_guid[0] = '\0';
+            }
+        }
+        close(fd);
+    }
 
-       // generate a new one?
-       if(!registry.machine_guid[0]) {
-               uuid_t uuid;
+    // generate a new one?
+    if(!registry.machine_guid[0]) {
+        uuid_t uuid;
 
-               uuid_generate_time(uuid);
-               uuid_unparse_lower(uuid, registry.machine_guid);
-               registry.machine_guid[36] = '\0';
+        uuid_generate_time(uuid);
+        uuid_unparse_lower(uuid, registry.machine_guid);
+        registry.machine_guid[36] = '\0';
 
-               // save it
-               fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
-               if(fd == -1)
-                       fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
+        // save it
+        fd = open(registry.machine_guid_filename, O_WRONLY|O_CREAT|O_TRUNC, 444);
+        if(fd == -1)
+            fatal("Cannot create unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
 
-               if(write(fd, registry.machine_guid, 36) != 36)
-                       fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
+        if(write(fd, registry.machine_guid, 36) != 36)
+            fatal("Cannot write the unique machine id file '%s'. Please fix this.", registry.machine_guid_filename);
 
-               close(fd);
-       }
+        close(fd);
+    }
 
-       return registry.machine_guid;
+    return registry.machine_guid;
 }
 
 
@@ -1306,560 +1276,560 @@ char *registry_get_this_machine_guid(void) {
 // REGISTRY LOAD/SAVE
 
 int registry_machine_save_url(void *entry, void *file) {
-       MACHINE_URL *mu = entry;
-       FILE *fp = file;
+    MACHINE_URL *mu = entry;
+    FILE *fp = file;
 
-       debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
+    debug(D_REGISTRY, "Registry: registry_machine_save_url('%s')", mu->url->url);
 
-       int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
-                       mu->first_t,
-                       mu->last_t,
-                       mu->usages,
-                       mu->flags,
-                       mu->url->url
-       );
+    int ret = fprintf(fp, "V\t%08x\t%08x\t%08x\t%02x\t%s\n",
+            mu->first_t,
+            mu->last_t,
+            mu->usages,
+            mu->flags,
+            mu->url->url
+    );
 
-       // error handling is done at registry_save()
+    // error handling is done at registry_save()
 
-       return ret;
+    return ret;
 }
 
 int registry_machine_save(void *entry, void *file) {
-       MACHINE *m = entry;
-       FILE *fp = file;
+    MACHINE *m = entry;
+    FILE *fp = file;
 
-       debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
+    debug(D_REGISTRY, "Registry: registry_machine_save('%s')", m->guid);
 
-       int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
-                       m->first_t,
-                       m->last_t,
-                       m->usages,
-                       m->guid
-       );
+    int ret = fprintf(fp, "M\t%08x\t%08x\t%08x\t%s\n",
+            m->first_t,
+            m->last_t,
+            m->usages,
+            m->guid
+    );
 
-       if(ret >= 0) {
-               int ret2 = dictionary_get_all(m->urls, registry_machine_save_url, fp);
-               if(ret2 < 0) return ret2;
-               ret += ret2;
-       }
+    if(ret >= 0) {
+        int ret2 = dictionary_get_all(m->urls, registry_machine_save_url, fp);
+        if(ret2 < 0) return ret2;
+        ret += ret2;
+    }
 
-       // error handling is done at registry_save()
+    // error handling is done at registry_save()
 
-       return ret;
+    return ret;
 }
 
 static inline int registry_person_save_url(void *entry, void *file) {
-       PERSON_URL *pu = entry;
-       FILE *fp = file;
+    PERSON_URL *pu = entry;
+    FILE *fp = file;
 
-       debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
+    debug(D_REGISTRY, "Registry: registry_person_save_url('%s')", pu->url->url);
 
-       int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
-                       pu->first_t,
-                       pu->last_t,
-                       pu->usages,
-                       pu->flags,
-                       pu->machine->guid,
-                       pu->name,
-                       pu->url->url
-       );
+    int ret = fprintf(fp, "U\t%08x\t%08x\t%08x\t%02x\t%s\t%s\t%s\n",
+            pu->first_t,
+            pu->last_t,
+            pu->usages,
+            pu->flags,
+            pu->machine->guid,
+            pu->name,
+            pu->url->url
+    );
 
-       // error handling is done at registry_save()
+    // error handling is done at registry_save()
 
-       return ret;
+    return ret;
 }
 
 static inline int registry_person_save(void *entry, void *file) {
-       PERSON *p = entry;
-       FILE *fp = file;
+    PERSON *p = entry;
+    FILE *fp = file;
 
-       debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
+    debug(D_REGISTRY, "Registry: registry_person_save('%s')", p->guid);
 
-       int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
-                       p->first_t,
-                       p->last_t,
-                       p->usages,
-                       p->guid
-       );
+    int ret = fprintf(fp, "P\t%08x\t%08x\t%08x\t%s\n",
+            p->first_t,
+            p->last_t,
+            p->usages,
+            p->guid
+    );
 
-       if(ret >= 0) {
-               int ret2 = dictionary_get_all(p->urls, registry_person_save_url, fp);
-               if (ret2 < 0) return ret2;
-               ret += ret2;
-       }
+    if(ret >= 0) {
+        int ret2 = dictionary_get_all(p->urls, registry_person_save_url, fp);
+        if (ret2 < 0) return ret2;
+        ret += ret2;
+    }
 
-       // error handling is done at registry_save()
+    // error handling is done at registry_save()
 
-       return ret;
+    return ret;
 }
 
 int registry_save(void) {
-       if(!registry.enabled) return -1;
-
-       // make sure the log is not updated
-       registry_log_lock();
-
-       if(unlikely(!registry_should_save_db())) {
-               registry_log_unlock();
-               return -2;
-       }
-
-       char tmp_filename[FILENAME_MAX + 1];
-       char old_filename[FILENAME_MAX + 1];
-
-       snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
-       snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
-
-       debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
-       FILE *fp = fopen(tmp_filename, "w");
-       if(!fp) {
-               error("Registry: Cannot create file: %s", tmp_filename);
-               registry_log_unlock();
-               return -1;
-       }
-
-       // dictionary_get_all() has its own locking, so this is safe to do
-
-       debug(D_REGISTRY, "Saving all machines");
-       int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp);
-       if(bytes1 < 0) {
-               error("Registry: Cannot save registry machines - return value %d", bytes1);
-               fclose(fp);
-               registry_log_unlock();
-               return bytes1;
-       }
-       debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
-
-       debug(D_REGISTRY, "Saving all persons");
-       int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp);
-       if(bytes2 < 0) {
-               error("Registry: Cannot save registry persons - return value %d", bytes2);
-               fclose(fp);
-               registry_log_unlock();
-               return bytes2;
-       }
-       debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
-
-       // save the totals
-       fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
-                       registry.persons_count,
-                       registry.machines_count,
-                       registry.usages_count + 1, // this is required - it is lost on db rotation
-                       registry.urls_count,
-                       registry.persons_urls_count,
-                       registry.machines_urls_count
-       );
-
-       fclose(fp);
-
-       errno = 0;
-
-       // remove the .old db
-       debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
-       if(unlink(old_filename) == -1 && errno != ENOENT)
-               error("Registry: cannot remove old registry file '%s'", old_filename);
-
-       // rename the db to .old
-       debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
-       if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
-               error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, registry.db_filename);
-
-       else {
-               // remove the database (it is saved in .old)
-               debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
-               if (unlink(registry.db_filename) == -1 && errno != ENOENT)
-                       error("Registry: cannot remove old registry file '%s'", registry.db_filename);
-
-               // move the .tmp to make it active
-               debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
-               if (link(tmp_filename, registry.db_filename) == -1) {
-                       error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
-                                 registry.db_filename);
-
-                       // move the .old back
-                       debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
-                       if(link(old_filename, registry.db_filename) == -1)
-                               error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
-               }
-               else {
-                       debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
-                       if(unlink(tmp_filename) == -1)
-                               error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
-
-                       // it has been moved successfully
-                       // discard the current registry log
-                       registry_log_recreate_nolock();
-
-                       registry.log_count = 0;
-               }
-       }
-
-       // continue operations
-       registry_log_unlock();
-
-       return -1;
+    if(!registry.enabled) return -1;
+
+    // make sure the log is not updated
+    registry_log_lock();
+
+    if(unlikely(!registry_should_save_db())) {
+        registry_log_unlock();
+        return -2;
+    }
+
+    char tmp_filename[FILENAME_MAX + 1];
+    char old_filename[FILENAME_MAX + 1];
+
+    snprintfz(old_filename, FILENAME_MAX, "%s.old", registry.db_filename);
+    snprintfz(tmp_filename, FILENAME_MAX, "%s.tmp", registry.db_filename);
+
+    debug(D_REGISTRY, "Registry: Creating file '%s'", tmp_filename);
+    FILE *fp = fopen(tmp_filename, "w");
+    if(!fp) {
+        error("Registry: Cannot create file: %s", tmp_filename);
+        registry_log_unlock();
+        return -1;
+    }
+
+    // dictionary_get_all() has its own locking, so this is safe to do
+
+    debug(D_REGISTRY, "Saving all machines");
+    int bytes1 = dictionary_get_all(registry.machines, registry_machine_save, fp);
+    if(bytes1 < 0) {
+        error("Registry: Cannot save registry machines - return value %d", bytes1);
+        fclose(fp);
+        registry_log_unlock();
+        return bytes1;
+    }
+    debug(D_REGISTRY, "Registry: saving machines took %d bytes", bytes1);
+
+    debug(D_REGISTRY, "Saving all persons");
+    int bytes2 = dictionary_get_all(registry.persons, registry_person_save, fp);
+    if(bytes2 < 0) {
+        error("Registry: Cannot save registry persons - return value %d", bytes2);
+        fclose(fp);
+        registry_log_unlock();
+        return bytes2;
+    }
+    debug(D_REGISTRY, "Registry: saving persons took %d bytes", bytes2);
+
+    // save the totals
+    fprintf(fp, "T\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\t%016llx\n",
+            registry.persons_count,
+            registry.machines_count,
+            registry.usages_count + 1, // this is required - it is lost on db rotation
+            registry.urls_count,
+            registry.persons_urls_count,
+            registry.machines_urls_count
+    );
+
+    fclose(fp);
+
+    errno = 0;
+
+    // remove the .old db
+    debug(D_REGISTRY, "Registry: Removing old db '%s'", old_filename);
+    if(unlink(old_filename) == -1 && errno != ENOENT)
+        error("Registry: cannot remove old registry file '%s'", old_filename);
+
+    // rename the db to .old
+    debug(D_REGISTRY, "Registry: Link current db '%s' to .old: '%s'", registry.db_filename, old_filename);
+    if(link(registry.db_filename, old_filename) == -1 && errno != ENOENT)
+        error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename, registry.db_filename);
+
+    else {
+        // remove the database (it is saved in .old)
+        debug(D_REGISTRY, "Registry: removing db '%s'", registry.db_filename);
+        if (unlink(registry.db_filename) == -1 && errno != ENOENT)
+            error("Registry: cannot remove old registry file '%s'", registry.db_filename);
+
+        // move the .tmp to make it active
+        debug(D_REGISTRY, "Registry: linking tmp db '%s' to active db '%s'", tmp_filename, registry.db_filename);
+        if (link(tmp_filename, registry.db_filename) == -1) {
+            error("Registry: cannot move file '%s' to '%s'. Saving registry DB failed!", tmp_filename,
+                  registry.db_filename);
+
+            // move the .old back
+            debug(D_REGISTRY, "Registry: linking old db '%s' to active db '%s'", old_filename, registry.db_filename);
+            if(link(old_filename, registry.db_filename) == -1)
+                error("Registry: cannot move file '%s' to '%s'. Recovering the old registry DB failed!", old_filename, registry.db_filename);
+        }
+        else {
+            debug(D_REGISTRY, "Registry: removing tmp db '%s'", tmp_filename);
+            if(unlink(tmp_filename) == -1)
+                error("Registry: cannot remove tmp registry file '%s'", tmp_filename);
+
+            // it has been moved successfully
+            // discard the current registry log
+            registry_log_recreate_nolock();
+
+            registry.log_count = 0;
+        }
+    }
+
+    // continue operations
+    registry_log_unlock();
+
+    return -1;
 }
 
 static inline size_t registry_load(void) {
-       char *s, buf[4096 + 1];
-       PERSON *p = NULL;
-       MACHINE *m = NULL;
-       URL *u = NULL;
-       size_t line = 0;
-
-       debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
-       FILE *fp = fopen(registry.db_filename, "r");
-       if(!fp) {
-               error("Registry: cannot open registry file: '%s'", registry.db_filename);
-               return 0;
-       }
-
-       size_t len = 0;
-       buf[4096] = '\0';
-       while((s = fgets_trim_len(buf, 4096, fp, &len))) {
-               line++;
-
-               debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
-               switch(*s) {
-                       case 'T': // totals
-                               if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
-                                       error("Registry totals line %zu is wrong (len = %zu).", line, len);
-                                       continue;
-                               }
-                               registry.persons_count = strtoull(&s[2], NULL, 16);
-                               registry.machines_count = strtoull(&s[19], NULL, 16);
-                               registry.usages_count = strtoull(&s[36], NULL, 16);
-                               registry.urls_count = strtoull(&s[53], NULL, 16);
-                               registry.persons_urls_count = strtoull(&s[70], NULL, 16);
-                               registry.machines_urls_count = strtoull(&s[87], NULL, 16);
-                               break;
-
-                       case 'P': // person
-                               m = NULL;
-                               // verify it is valid
-                               if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
-                                       error("Registry person line %zu is wrong (len = %zu).", line, len);
-                                       continue;
-                               }
-
-                               s[1] = s[10] = s[19] = s[28] = '\0';
-                               p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
-                               p->last_t = strtoul(&s[11], NULL, 16);
-                               p->usages = strtoul(&s[20], NULL, 16);
-                               debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
-                               break;
-
-                       case 'M': // machine
-                               p = NULL;
-                               // verify it is valid
-                               if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
-                                       error("Registry person line %zu is wrong (len = %zu).", line, len);
-                                       continue;
-                               }
-
-                               s[1] = s[10] = s[19] = s[28] = '\0';
-                               m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
-                               m->last_t = strtoul(&s[11], NULL, 16);
-                               m->usages = strtoul(&s[20], NULL, 16);
-                               debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
-                               break;
-
-                       case 'U': // person URL
-                               if(unlikely(!p)) {
-                                       error("Registry: ignoring line %zu, no person loaded: %s", line, s);
-                                       continue;
-                               }
-
-                               // verify it is valid
-                               if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
-                                       error("Registry person URL line %zu is wrong (len = %zu).", line, len);
-                                       continue;
-                               }
-
-                               s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
-
-                               // skip the name to find the url
-                               char *url = &s[69];
-                               while(*url && *url != '\t') url++;
-                               if(!*url) {
-                                       error("Registry person URL line %zu does not have a url.", line);
-                                       continue;
-                               }
-                               *url++ = '\0';
-
-                               u = registry_url_allocate_nolock(url, strlen(url));
-
-                               time_t first_t = strtoul(&s[2], NULL, 16);
-
-                               m = registry_machine_find(&s[32]);
-                               if(!m) m = registry_machine_allocate(&s[32], first_t);
-
-                               PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
-                               pu->last_t = strtoul(&s[11], NULL, 16);
-                               pu->usages = strtoul(&s[20], NULL, 16);
-                               pu->flags = strtoul(&s[29], NULL, 16);
-                               debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
-                               break;
-
-                       case 'V': // machine URL
-                               if(unlikely(!m)) {
-                                       error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
-                                       continue;
-                               }
-
-                               // verify it is valid
-                               if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
-                                       error("Registry person URL line %zu is wrong (len = %zu).", line, len);
-                                       continue;
-                               }
-
-                               s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
-                               u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
-
-                               MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
-                               mu->last_t = strtoul(&s[11], NULL, 16);
-                               mu->usages = strtoul(&s[20], NULL, 16);
-                               mu->flags = strtoul(&s[29], NULL, 16);
-                               debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
-                               break;
-
-                       default:
-                               error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
-                               break;
-               }
-       }
-       fclose(fp);
-
-       return line;
+    char *s, buf[4096 + 1];
+    PERSON *p = NULL;
+    MACHINE *m = NULL;
+    URL *u = NULL;
+    size_t line = 0;
+
+    debug(D_REGISTRY, "Registry: loading active db from: '%s'", registry.db_filename);
+    FILE *fp = fopen(registry.db_filename, "r");
+    if(!fp) {
+        error("Registry: cannot open registry file: '%s'", registry.db_filename);
+        return 0;
+    }
+
+    size_t len = 0;
+    buf[4096] = '\0';
+    while((s = fgets_trim_len(buf, 4096, fp, &len))) {
+        line++;
+
+        debug(D_REGISTRY, "Registry: read line %zu to length %zu: %s", line, len, s);
+        switch(*s) {
+            case 'T': // totals
+                if(unlikely(len != 103 || s[1] != '\t' || s[18] != '\t' || s[35] != '\t' || s[52] != '\t' || s[69] != '\t' || s[86] != '\t' || s[103] != '\0')) {
+                    error("Registry totals line %zu is wrong (len = %zu).", line, len);
+                    continue;
+                }
+                registry.persons_count = strtoull(&s[2], NULL, 16);
+                registry.machines_count = strtoull(&s[19], NULL, 16);
+                registry.usages_count = strtoull(&s[36], NULL, 16);
+                registry.urls_count = strtoull(&s[53], NULL, 16);
+                registry.persons_urls_count = strtoull(&s[70], NULL, 16);
+                registry.machines_urls_count = strtoull(&s[87], NULL, 16);
+                break;
+
+            case 'P': // person
+                m = NULL;
+                // verify it is valid
+                if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
+                    error("Registry person line %zu is wrong (len = %zu).", line, len);
+                    continue;
+                }
+
+                s[1] = s[10] = s[19] = s[28] = '\0';
+                p = registry_person_allocate(&s[29], strtoul(&s[2], NULL, 16));
+                p->last_t = strtoul(&s[11], NULL, 16);
+                p->usages = strtoul(&s[20], NULL, 16);
+                debug(D_REGISTRY, "Registry loaded person '%s', first: %u, last: %u, usages: %u", p->guid, p->first_t, p->last_t, p->usages);
+                break;
+
+            case 'M': // machine
+                p = NULL;
+                // verify it is valid
+                if(unlikely(len != 65 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[65] != '\0')) {
+                    error("Registry person line %zu is wrong (len = %zu).", line, len);
+                    continue;
+                }
+
+                s[1] = s[10] = s[19] = s[28] = '\0';
+                m = registry_machine_allocate(&s[29], strtoul(&s[2], NULL, 16));
+                m->last_t = strtoul(&s[11], NULL, 16);
+                m->usages = strtoul(&s[20], NULL, 16);
+                debug(D_REGISTRY, "Registry loaded machine '%s', first: %u, last: %u, usages: %u", m->guid, m->first_t, m->last_t, m->usages);
+                break;
+
+            case 'U': // person URL
+                if(unlikely(!p)) {
+                    error("Registry: ignoring line %zu, no person loaded: %s", line, s);
+                    continue;
+                }
+
+                // verify it is valid
+                if(len < 69 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t' || s[68] != '\t') {
+                    error("Registry person URL line %zu is wrong (len = %zu).", line, len);
+                    continue;
+                }
+
+                s[1] = s[10] = s[19] = s[28] = s[31] = s[68] = '\0';
+
+                // skip the name to find the url
+                char *url = &s[69];
+                while(*url && *url != '\t') url++;
+                if(!*url) {
+                    error("Registry person URL line %zu does not have a url.", line);
+                    continue;
+                }
+                *url++ = '\0';
+
+                u = registry_url_allocate_nolock(url, strlen(url));
+
+                time_t first_t = strtoul(&s[2], NULL, 16);
+
+                m = registry_machine_find(&s[32]);
+                if(!m) m = registry_machine_allocate(&s[32], first_t);
+
+                PERSON_URL *pu = registry_person_url_allocate(p, m, u, &s[69], strlen(&s[69]), first_t);
+                pu->last_t = strtoul(&s[11], NULL, 16);
+                pu->usages = strtoul(&s[20], NULL, 16);
+                pu->flags = strtoul(&s[29], NULL, 16);
+                debug(D_REGISTRY, "Registry loaded person URL '%s' with name '%s' of machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, pu->name, m->guid, pu->first_t, pu->last_t, pu->usages, pu->flags);
+                break;
+
+            case 'V': // machine URL
+                if(unlikely(!m)) {
+                    error("Registry: ignoring line %zu, no machine loaded: %s", line, s);
+                    continue;
+                }
+
+                // verify it is valid
+                if(len < 32 || s[1] != '\t' || s[10] != '\t' || s[19] != '\t' || s[28] != '\t' || s[31] != '\t') {
+                    error("Registry person URL line %zu is wrong (len = %zu).", line, len);
+                    continue;
+                }
+
+                s[1] = s[10] = s[19] = s[28] = s[31] = '\0';
+                u = registry_url_allocate_nolock(&s[32], strlen(&s[32]));
+
+                MACHINE_URL *mu = registry_machine_url_allocate(m, u, strtoul(&s[2], NULL, 16));
+                mu->last_t = strtoul(&s[11], NULL, 16);
+                mu->usages = strtoul(&s[20], NULL, 16);
+                mu->flags = strtoul(&s[29], NULL, 16);
+                debug(D_REGISTRY, "Registry loaded machine URL '%s', machine '%s', first: %u, last: %u, usages: %u, flags: %02x", u->url, m->guid, mu->first_t, mu->last_t, mu->usages, mu->flags);
+                break;
+
+            default:
+                error("Registry: ignoring line %zu of filename '%s': %s.", line, registry.db_filename, s);
+                break;
+        }
+    }
+    fclose(fp);
+
+    return line;
 }
 
 // ----------------------------------------------------------------------------
 // REGISTRY
 
 int registry_init(void) {
-       char filename[FILENAME_MAX + 1];
-
-       // registry enabled?
-       registry.enabled = config_get_boolean("registry", "enabled", 0);
-
-       if(mkdir(VARLIB_DIR, 0755) == -1 && errno != EEXIST)
-               error("Cannot create directory '" VARLIB_DIR "'");
-
-       // pathnames
-       registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry");
-       if(mkdir(registry.pathname, 0755) == -1 && errno != EEXIST) {
-               error("Cannot create directory '%s'. Registry disabled.", registry.pathname);
-               registry.enabled = 0;
-               return -1;
-       }
-
-       // filenames
-       snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
-       registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
-       registry_get_this_machine_guid();
-
-       snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
-       registry.db_filename = config_get("registry", "registry db file", filename);
-
-       snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
-       registry.log_filename = config_get("registry", "registry log file", filename);
-
-       // configuration options
-       registry.save_registry_every_entries = config_get_number("registry", "registry save db every new entries", 1000000);
-       registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
-       registry.registry_domain = config_get("registry", "registry domain", "");
-       registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
-       registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", hostname));
-       registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
-
-       registry.max_url_length = config_get_number("registry", "max URL length", 1024);
-       if(registry.max_url_length < 10) {
-               registry.max_url_length = 10;
-               config_set_number("registry", "max URL length", registry.max_url_length);
-       }
-
-       registry.max_name_length = config_get_number("registry", "max URL name length", 50);
-       if(registry.max_name_length < 10) {
-               registry.max_name_length = 10;
-               config_set_number("registry", "max URL name length", registry.max_name_length);
-       }
-
-       // initialize entries counters
-       registry.persons_count = 0;
-       registry.machines_count = 0;
-       registry.usages_count = 0;
-       registry.urls_count = 0;
-       registry.persons_urls_count = 0;
-       registry.machines_urls_count = 0;
-
-       // initialize memory counters
-       registry.persons_memory = 0;
-       registry.machines_memory = 0;
-       registry.urls_memory = 0;
-       registry.persons_urls_memory = 0;
-       registry.machines_urls_memory = 0;
-
-       // initialize locks
-       pthread_mutex_init(&registry.persons_lock, NULL);
-       pthread_mutex_init(&registry.machines_lock, NULL);
-       pthread_mutex_init(&registry.urls_lock, NULL);
-       pthread_mutex_init(&registry.person_urls_lock, NULL);
-       pthread_mutex_init(&registry.machine_urls_lock, NULL);
-
-       // create dictionaries
-       registry.persons = dictionary_create(DICTIONARY_FLAGS);
-       registry.machines = dictionary_create(DICTIONARY_FLAGS);
-       registry.urls = dictionary_create(DICTIONARY_FLAGS);
-
-       // load the registry database
-       if(registry.enabled) {
-               registry_log_open_nolock();
-               registry_load();
-               registry_log_load();
-       }
-
-       return 0;
+    char filename[FILENAME_MAX + 1];
+
+    // registry enabled?
+    registry.enabled = config_get_boolean("registry", "enabled", 0);
+
+    if(mkdir(VARLIB_DIR, 0755) == -1 && errno != EEXIST)
+        error("Cannot create directory '" VARLIB_DIR "'");
+
+    // pathnames
+    registry.pathname = config_get("registry", "registry db directory", VARLIB_DIR "/registry");
+    if(mkdir(registry.pathname, 0755) == -1 && errno != EEXIST) {
+        error("Cannot create directory '%s'. Registry disabled.", registry.pathname);
+        registry.enabled = 0;
+        return -1;
+    }
+
+    // filenames
+    snprintfz(filename, FILENAME_MAX, "%s/netdata.public.unique.id", registry.pathname);
+    registry.machine_guid_filename = config_get("registry", "netdata unique id file", filename);
+    registry_get_this_machine_guid();
+
+    snprintfz(filename, FILENAME_MAX, "%s/registry.db", registry.pathname);
+    registry.db_filename = config_get("registry", "registry db file", filename);
+
+    snprintfz(filename, FILENAME_MAX, "%s/registry-log.db", registry.pathname);
+    registry.log_filename = config_get("registry", "registry log file", filename);
+
+    // configuration options
+    registry.save_registry_every_entries = config_get_number("registry", "registry save db every new entries", 1000000);
+    registry.persons_expiration = config_get_number("registry", "registry expire idle persons days", 365) * 86400;
+    registry.registry_domain = config_get("registry", "registry domain", "");
+    registry.registry_to_announce = config_get("registry", "registry to announce", "https://registry.my-netdata.io");
+    registry.hostname = config_get("registry", "registry hostname", config_get("global", "hostname", hostname));
+    registry.verify_cookies_redirects = config_get_boolean("registry", "verify browser cookies support", 1);
+
+    registry.max_url_length = config_get_number("registry", "max URL length", 1024);
+    if(registry.max_url_length < 10) {
+        registry.max_url_length = 10;
+        config_set_number("registry", "max URL length", registry.max_url_length);
+    }
+
+    registry.max_name_length = config_get_number("registry", "max URL name length", 50);
+    if(registry.max_name_length < 10) {
+        registry.max_name_length = 10;
+        config_set_number("registry", "max URL name length", registry.max_name_length);
+    }
+
+    // initialize entries counters
+    registry.persons_count = 0;
+    registry.machines_count = 0;
+    registry.usages_count = 0;
+    registry.urls_count = 0;
+    registry.persons_urls_count = 0;
+    registry.machines_urls_count = 0;
+
+    // initialize memory counters
+    registry.persons_memory = 0;
+    registry.machines_memory = 0;
+    registry.urls_memory = 0;
+    registry.persons_urls_memory = 0;
+    registry.machines_urls_memory = 0;
+
+    // initialize locks
+    pthread_mutex_init(&registry.persons_lock, NULL);
+    pthread_mutex_init(&registry.machines_lock, NULL);
+    pthread_mutex_init(&registry.urls_lock, NULL);
+    pthread_mutex_init(&registry.person_urls_lock, NULL);
+    pthread_mutex_init(&registry.machine_urls_lock, NULL);
+
+    // create dictionaries
+    registry.persons = dictionary_create(DICTIONARY_FLAGS);
+    registry.machines = dictionary_create(DICTIONARY_FLAGS);
+    registry.urls = dictionary_create(DICTIONARY_FLAGS);
+
+    // load the registry database
+    if(registry.enabled) {
+        registry_log_open_nolock();
+        registry_load();
+        registry_log_load();
+    }
+
+    return 0;
 }
 
 void registry_free(void) {
-       if(!registry.enabled) return;
+    if(!registry.enabled) return;
 
-       // we need to destroy the dictionaries ourselves
-       // since the dictionaries use memory we allocated
+    // we need to destroy the dictionaries ourselves
+    // since the dictionaries use memory we allocated
 
-       while(registry.persons->values_index.root) {
-               PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
+    while(registry.persons->values_index.root) {
+        PERSON *p = ((NAME_VALUE *)registry.persons->values_index.root)->value;
 
-               // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages);
+        // fprintf(stderr, "\nPERSON: '%s', first: %u, last: %u, usages: %u\n", p->guid, p->first_t, p->last_t, p->usages);
 
-               while(p->urls->values_index.root) {
-                       PERSON_URL *pu = ((NAME_VALUE *)p->urls->values_index.root)->value;
+        while(p->urls->values_index.root) {
+            PERSON_URL *pu = ((NAME_VALUE *)p->urls->values_index.root)->value;
 
-                       // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags);
+            // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", pu->url->url, pu->first_t, pu->last_t, pu->usages, pu->flags);
 
-                       debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid);
-                       dictionary_del(p->urls, pu->url->url);
+            debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", pu->url->url, p->guid);
+            dictionary_del(p->urls, pu->url->url);
 
-                       debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
-                       registry_url_unlink_nolock(pu->url);
+            debug(D_REGISTRY, "Registry: unlinking url '%s' from person", pu->url->url);
+            registry_url_unlink_nolock(pu->url);
 
-                       debug(D_REGISTRY, "Registry: freeing person url");
-                       free(pu);
-               }
+            debug(D_REGISTRY, "Registry: freeing person url");
+            freez(pu);
+        }
 
-               debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
-               dictionary_del(registry.persons, p->guid);
+        debug(D_REGISTRY, "Registry: deleting person '%s' from persons registry", p->guid);
+        dictionary_del(registry.persons, p->guid);
 
-               debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid);
-               dictionary_destroy(p->urls);
+        debug(D_REGISTRY, "Registry: destroying URL dictionary of person '%s'", p->guid);
+        dictionary_destroy(p->urls);
 
-               debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
-               free(p);
-       }
+        debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
+        freez(p);
+    }
 
-       while(registry.machines->values_index.root) {
-               MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value;
+    while(registry.machines->values_index.root) {
+        MACHINE *m = ((NAME_VALUE *)registry.machines->values_index.root)->value;
 
-               // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages);
+        // fprintf(stderr, "\nMACHINE: '%s', first: %u, last: %u, usages: %u\n", m->guid, m->first_t, m->last_t, m->usages);
 
-               while(m->urls->values_index.root) {
-                       MACHINE_URL *mu = ((NAME_VALUE *)m->urls->values_index.root)->value;
+        while(m->urls->values_index.root) {
+            MACHINE_URL *mu = ((NAME_VALUE *)m->urls->values_index.root)->value;
 
-                       // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags);
+            // fprintf(stderr, "\tURL: '%s', first: %u, last: %u, usages: %u, flags: 0x%02x\n", mu->url->url, mu->first_t, mu->last_t, mu->usages, mu->flags);
 
-                       //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url);
-                       //dictionary_destroy(mu->persons);
+            //debug(D_REGISTRY, "Registry: destroying persons dictionary from url '%s'", mu->url->url);
+            //dictionary_destroy(mu->persons);
 
-                       debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid);
-                       dictionary_del(m->urls, mu->url->url);
+            debug(D_REGISTRY, "Registry: deleting url '%s' from person '%s'", mu->url->url, m->guid);
+            dictionary_del(m->urls, mu->url->url);
 
-                       debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
-                       registry_url_unlink_nolock(mu->url);
+            debug(D_REGISTRY, "Registry: unlinking url '%s' from machine", mu->url->url);
+            registry_url_unlink_nolock(mu->url);
 
-                       debug(D_REGISTRY, "Registry: freeing machine url");
-                       free(mu);
-               }
+            debug(D_REGISTRY, "Registry: freeing machine url");
+            freez(mu);
+        }
 
-               debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
-               dictionary_del(registry.machines, m->guid);
+        debug(D_REGISTRY, "Registry: deleting machine '%s' from machines registry", m->guid);
+        dictionary_del(registry.machines, m->guid);
 
-               debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid);
-               dictionary_destroy(m->urls);
+        debug(D_REGISTRY, "Registry: destroying URL dictionary of machine '%s'", m->guid);
+        dictionary_destroy(m->urls);
 
-               debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
-               free(m);
-       }
+        debug(D_REGISTRY, "Registry: freeing machine '%s'", m->guid);
+        freez(m);
+    }
 
-       // and free the memory of remaining dictionary structures
+    // and free the memory of remaining dictionary structures
 
-       debug(D_REGISTRY, "Registry: destroying persons dictionary");
-       dictionary_destroy(registry.persons);
+    debug(D_REGISTRY, "Registry: destroying persons dictionary");
+    dictionary_destroy(registry.persons);
 
-       debug(D_REGISTRY, "Registry: destroying machines dictionary");
-       dictionary_destroy(registry.machines);
+    debug(D_REGISTRY, "Registry: destroying machines dictionary");
+    dictionary_destroy(registry.machines);
 
-       debug(D_REGISTRY, "Registry: destroying urls dictionary");
-       dictionary_destroy(registry.urls);
+    debug(D_REGISTRY, "Registry: destroying urls dictionary");
+    dictionary_destroy(registry.urls);
 }
 
 // ----------------------------------------------------------------------------
 // STATISTICS
 
 void registry_statistics(void) {
-       if(!registry.enabled) return;
-
-       static RRDSET *sts = NULL, *stc = NULL, *stm = NULL;
-
-       if(!sts) sts = rrdset_find("netdata.registry_sessions");
-       if(!sts) {
-               sts = rrdset_create("netdata", "registry_sessions", NULL, "registry", NULL, "NetData Registry Sessions", "session", 131000, rrd_update_every, RRDSET_TYPE_LINE);
-
-               rrddim_add(sts, "sessions",  NULL,  1, 1, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(sts);
-
-       rrddim_set(sts, "sessions", registry.usages_count);
-       rrdset_done(sts);
-
-       // ------------------------------------------------------------------------
-
-       if(!stc) stc = rrdset_find("netdata.registry_entries");
-       if(!stc) {
-               stc = rrdset_create("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries", "entries", 131100, rrd_update_every, RRDSET_TYPE_LINE);
-
-               rrddim_add(stc, "persons",        NULL,  1, 1, RRDDIM_ABSOLUTE);
-               rrddim_add(stc, "machines",       NULL,  1, 1, RRDDIM_ABSOLUTE);
-               rrddim_add(stc, "urls",           NULL,  1, 1, RRDDIM_ABSOLUTE);
-               rrddim_add(stc, "persons_urls",   NULL,  1, 1, RRDDIM_ABSOLUTE);
-               rrddim_add(stc, "machines_urls",  NULL,  1, 1, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(stc);
-
-       rrddim_set(stc, "persons",       registry.persons_count);
-       rrddim_set(stc, "machines",      registry.machines_count);
-       rrddim_set(stc, "urls",          registry.urls_count);
-       rrddim_set(stc, "persons_urls",  registry.persons_urls_count);
-       rrddim_set(stc, "machines_urls", registry.machines_urls_count);
-       rrdset_done(stc);
-
-       // ------------------------------------------------------------------------
-
-       if(!stm) stm = rrdset_find("netdata.registry_mem");
-       if(!stm) {
-               stm = rrdset_create("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB", 131300, rrd_update_every, RRDSET_TYPE_STACKED);
-
-               rrddim_add(stm, "persons",        NULL,  1, 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(stm, "machines",       NULL,  1, 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(stm, "urls",           NULL,  1, 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(stm, "persons_urls",   NULL,  1, 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(stm, "machines_urls",  NULL,  1, 1024, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(stm);
-
-       rrddim_set(stm, "persons",       registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
-       rrddim_set(stm, "machines",      registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
-       rrddim_set(stm, "urls",          registry.urls_memory + registry.urls_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
-       rrddim_set(stm, "persons_urls",  registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
-       rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
-       rrdset_done(stm);
+    if(!registry.enabled) return;
+
+    static RRDSET *sts = NULL, *stc = NULL, *stm = NULL;
+
+    if(!sts) sts = rrdset_find("netdata.registry_sessions");
+    if(!sts) {
+        sts = rrdset_create("netdata", "registry_sessions", NULL, "registry", NULL, "NetData Registry Sessions", "session", 131000, rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(sts, "sessions",  NULL,  1, 1, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(sts);
+
+    rrddim_set(sts, "sessions", registry.usages_count);
+    rrdset_done(sts);
+
+    // ------------------------------------------------------------------------
+
+    if(!stc) stc = rrdset_find("netdata.registry_entries");
+    if(!stc) {
+        stc = rrdset_create("netdata", "registry_entries", NULL, "registry", NULL, "NetData Registry Entries", "entries", 131100, rrd_update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(stc, "persons",        NULL,  1, 1, RRDDIM_ABSOLUTE);
+        rrddim_add(stc, "machines",       NULL,  1, 1, RRDDIM_ABSOLUTE);
+        rrddim_add(stc, "urls",           NULL,  1, 1, RRDDIM_ABSOLUTE);
+        rrddim_add(stc, "persons_urls",   NULL,  1, 1, RRDDIM_ABSOLUTE);
+        rrddim_add(stc, "machines_urls",  NULL,  1, 1, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(stc);
+
+    rrddim_set(stc, "persons",       registry.persons_count);
+    rrddim_set(stc, "machines",      registry.machines_count);
+    rrddim_set(stc, "urls",          registry.urls_count);
+    rrddim_set(stc, "persons_urls",  registry.persons_urls_count);
+    rrddim_set(stc, "machines_urls", registry.machines_urls_count);
+    rrdset_done(stc);
+
+    // ------------------------------------------------------------------------
+
+    if(!stm) stm = rrdset_find("netdata.registry_mem");
+    if(!stm) {
+        stm = rrdset_create("netdata", "registry_mem", NULL, "registry", NULL, "NetData Registry Memory", "KB", 131300, rrd_update_every, RRDSET_TYPE_STACKED);
+
+        rrddim_add(stm, "persons",        NULL,  1, 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(stm, "machines",       NULL,  1, 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(stm, "urls",           NULL,  1, 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(stm, "persons_urls",   NULL,  1, 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(stm, "machines_urls",  NULL,  1, 1024, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(stm);
+
+    rrddim_set(stm, "persons",       registry.persons_memory + registry.persons_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+    rrddim_set(stm, "machines",      registry.machines_memory + registry.machines_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+    rrddim_set(stm, "urls",          registry.urls_memory + registry.urls_count * sizeof(NAME_VALUE) + sizeof(DICTIONARY));
+    rrddim_set(stm, "persons_urls",  registry.persons_urls_memory + registry.persons_count * sizeof(DICTIONARY) + registry.persons_urls_count * sizeof(NAME_VALUE));
+    rrddim_set(stm, "machines_urls", registry.machines_urls_memory + registry.machines_count * sizeof(DICTIONARY) + registry.machines_urls_count * sizeof(NAME_VALUE));
+    rrdset_done(stm);
 }
index 9e7a13ecbf80168edf179dd957017170fc876fcb..c2b57a23df4c1c6f4c86968d3bc992ee16de111b 100644 (file)
@@ -1,5 +1,3 @@
-#include "web_client.h"
-
 #ifndef NETDATA_REGISTRY_H
 #define NETDATA_REGISTRY_H 1
 
index 47a23ea31e8167642b3d51935afb00fc81377dbc..09ecdcba0e7b0c6924bb1e8ee303948b553a16e6 100644 (file)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -1,25 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <pthread.h>
-#include <errno.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <stdlib.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-
-#include "main.h"
-#include "rrd.h"
 
 #define RRD_DEFAULT_GAP_INTERPOLATIONS 1
 
@@ -33,36 +12,136 @@ int rrd_delete_unupdated_dimensions = 0;
 
 int rrd_update_every = UPDATE_EVERY;
 int rrd_default_history_entries = RRD_DEFAULT_HISTORY_ENTRIES;
+int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
 
-RRDSET *rrdset_root = NULL;
-pthread_rwlock_t rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static int rrdset_compare(void* a, void* b);
+static int rrdset_compare_name(void* a, void* b);
+static int rrdcontext_compare(void* a, void* b);
 
-int rrd_memory_mode = RRD_MEMORY_MODE_SAVE;
+// ----------------------------------------------------------------------------
+// RRDHOST
+
+RRDHOST localhost = {
+        .hostname = "localhost",
+        .rrdset_root = NULL,
+        .rrdset_root_rwlock = PTHREAD_RWLOCK_INITIALIZER,
+        .rrdset_root_index = {
+            { NULL, rrdset_compare },
+            AVL_LOCK_INITIALIZER
+        },
+        .rrdset_root_index_name = {
+            { NULL, rrdset_compare_name },
+            AVL_LOCK_INITIALIZER
+        },
+        .rrdcontext_root_index = {
+            { NULL, rrdcontext_compare },
+            AVL_LOCK_INITIALIZER
+        },
+        .variables_root_index = {
+            { NULL, rrdvar_compare },
+            AVL_LOCK_INITIALIZER
+        }
+};
+
+void rrdhost_rwlock(RRDHOST *host) {
+    pthread_rwlock_wrlock(&host->rrdset_root_rwlock);
+}
+
+void rrdhost_rdlock(RRDHOST *host) {
+    pthread_rwlock_rdlock(&host->rrdset_root_rwlock);
+}
+
+void rrdhost_unlock(RRDHOST *host) {
+    pthread_rwlock_unlock(&host->rrdset_root_rwlock);
+}
+
+void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
+    int ret = pthread_rwlock_trywrlock(&host->rrdset_root_rwlock);
+
+    if(ret == 0)
+        fatal("RRDHOST '%s' should be read-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
+}
+
+void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line) {
+    int ret = pthread_rwlock_tryrdlock(&host->rrdset_root_rwlock);
+
+    if(ret == 0)
+        fatal("RRDHOST '%s' should be write-locked, but it is not, at function %s() at line %lu of file '%s'", host->hostname, function, line, file);
+}
+
+// ----------------------------------------------------------------------------
+// RRDCONTEXT index
+
+static int rrdcontext_compare(void* a, void* b) {
+    if(((RRDCONTEXT *)a)->hash < ((RRDCONTEXT *)b)->hash) return -1;
+    else if(((RRDCONTEXT *)a)->hash > ((RRDCONTEXT *)b)->hash) return 1;
+    else return strcmp(((RRDCONTEXT *)a)->id, ((RRDCONTEXT *)b)->id);
+}
+
+#define rrdcontext_index_add(host, rc) (RRDCONTEXT *)avl_insert_lock(&((host)->rrdcontext_root_index), (avl *)(rc))
+#define rrdcontext_index_del(host, rc) (RRDCONTEXT *)avl_remove_lock(&((host)->rrdcontext_root_index), (avl *)(rc))
 
+static RRDCONTEXT *rrdcontext_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+    RRDCONTEXT tmp;
+    tmp.id = id;
+    tmp.hash = (hash)?hash:simple_hash(tmp.id);
+
+    return (RRDCONTEXT *)avl_search_lock(&(host->rrdcontext_root_index), (avl *) &tmp);
+}
+
+RRDCONTEXT *rrdcontext_create(const char *id) {
+    RRDCONTEXT *rc = rrdcontext_index_find(&localhost, id, 0);
+    if(!rc) {
+        rc = callocz(1, sizeof(RRDCONTEXT));
+
+        rc->id = strdupz(id);
+        rc->hash = simple_hash(rc->id);
+
+        // initialize the variables index
+        avl_init_lock(&rc->variables_root_index, rrdvar_compare);
+
+        RRDCONTEXT *ret = rrdcontext_index_add(&localhost, rc);
+        if(ret != rc)
+            fatal("INTERNAL ERROR: Expected to INSERT RRDCONTEXT '%s' into index, but inserted '%s'.", rc->id, (ret)?ret->id:"NONE");
+    }
+
+    rc->use_count++;
+    return rc;
+}
+
+void rrdcontext_free(RRDCONTEXT *rc) {
+    rc->use_count--;
+    if(!rc->use_count) {
+        RRDCONTEXT *ret = rrdcontext_index_del(&localhost, rc);
+        if(ret != rc)
+            fatal("INTERNAL ERROR: Expected to DELETE RRDCONTEXT '%s' from index, but deleted '%s'.", rc->id, (ret)?ret->id:"NONE");
+
+        if(rc->variables_root_index.avl_tree.root != NULL)
+            fatal("INTERNAL ERROR: Variables index of RRDCONTEXT '%s' that is freed, is not empty.", rc->id);
+
+        freez((void *)rc->id);
+        freez(rc);
+    }
+}
 
 // ----------------------------------------------------------------------------
 // RRDSET index
 
 static int rrdset_compare(void* a, void* b) {
-       if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
-       else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
-       else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
+    if(((RRDSET *)a)->hash < ((RRDSET *)b)->hash) return -1;
+    else if(((RRDSET *)a)->hash > ((RRDSET *)b)->hash) return 1;
+    else return strcmp(((RRDSET *)a)->id, ((RRDSET *)b)->id);
 }
 
-avl_tree_lock rrdset_root_index = {
-               { NULL, rrdset_compare },
-               AVL_LOCK_INITIALIZER
-};
-
-#define rrdset_index_add(st) avl_insert_lock(&rrdset_root_index, (avl *)(st))
-#define rrdset_index_del(st) avl_remove_lock(&rrdset_root_index, (avl *)(st))
+#define rrdset_index_add(host, st) (RRDSET *)avl_insert_lock(&((host)->rrdset_root_index), (avl *)(st))
+#define rrdset_index_del(host, st) (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index), (avl *)(st))
 
-static RRDSET *rrdset_index_find(const char *id, uint32_t hash) {
-       RRDSET tmp;
-       strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
-       tmp.hash = (hash)?hash:simple_hash(tmp.id);
+static RRDSET *rrdset_index_find(RRDHOST *host, const char *id, uint32_t hash) {
+    RRDSET tmp;
+    strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
+    tmp.hash = (hash)?hash:simple_hash(tmp.id);
 
-       return (RRDSET *)avl_search_lock(&(rrdset_root_index), (avl *) &tmp);
+    return (RRDSET *)avl_search_lock(&(host->rrdset_root_index), (avl *) &tmp);
 }
 
 // ----------------------------------------------------------------------------
@@ -71,46 +150,50 @@ static RRDSET *rrdset_index_find(const char *id, uint32_t hash) {
 #define rrdset_from_avlname(avlname_ptr) ((RRDSET *)((avlname_ptr) - offsetof(RRDSET, avlname)))
 
 static int rrdset_compare_name(void* a, void* b) {
-       RRDSET *A = rrdset_from_avlname(a);
-       RRDSET *B = rrdset_from_avlname(b);
+    RRDSET *A = rrdset_from_avlname(a);
+    RRDSET *B = rrdset_from_avlname(b);
 
-       // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
+    // fprintf(stderr, "COMPARING: %s with %s\n", A->name, B->name);
 
-       if(A->hash_name < B->hash_name) return -1;
-       else if(A->hash_name > B->hash_name) return 1;
-       else return strcmp(A->name, B->name);
+    if(A->hash_name < B->hash_name) return -1;
+    else if(A->hash_name > B->hash_name) return 1;
+    else return strcmp(A->name, B->name);
 }
 
-avl_tree_lock rrdset_root_index_name = {
-               { NULL, rrdset_compare_name },
-               AVL_LOCK_INITIALIZER
-};
+RRDSET *rrdset_index_add_name(RRDHOST *host, RRDSET *st) {
+    void *result;
+    // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
+    result = avl_insert_lock(&host->rrdset_root_index_name, (avl *) (&st->avlname));
+    if(result) return rrdset_from_avlname(result);
+    return NULL;
+}
 
-RRDSET *rrdset_index_add_name(RRDSET *st) {
-       // fprintf(stderr, "ADDING: %s (name: %s)\n", st->id, st->name);
-       return (RRDSET *)avl_insert_lock(&rrdset_root_index_name, (avl *) (&st->avlname));
+RRDSET *rrdset_index_del_name(RRDHOST *host, RRDSET *st) {
+    void *result;
+    // fprintf(stderr, "DELETING: %s (name: %s)\n", st->id, st->name);
+    return (RRDSET *)avl_remove_lock(&((host)->rrdset_root_index_name), (avl *)(&st->avlname));
+    if(result) return rrdset_from_avlname(result);
+    return NULL;
 }
 
-#define rrdset_index_del_name(st) avl_remove_lock(&rrdset_root_index_name, (avl *)(&st->avlname))
-
-static RRDSET *rrdset_index_find_name(const char *name, uint32_t hash) {
-       void *result = NULL;
-       RRDSET tmp;
-       tmp.name = name;
-       tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
-
-       // fprintf(stderr, "SEARCHING: %s\n", name);
-       result = avl_search_lock(&(rrdset_root_index_name), (avl *) (&(tmp.avlname)));
-       if(result) {
-               RRDSET *st = rrdset_from_avlname(result);
-               if(strcmp(st->magic, RRDSET_MAGIC))
-                       error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
-
-               // fprintf(stderr, "FOUND: %s\n", name);
-               return rrdset_from_avlname(result);
-       }
-       // fprintf(stderr, "NOT FOUND: %s\n", name);
-       return NULL;
+static RRDSET *rrdset_index_find_name(RRDHOST *host, const char *name, uint32_t hash) {
+    void *result = NULL;
+    RRDSET tmp;
+    tmp.name = name;
+    tmp.hash_name = (hash)?hash:simple_hash(tmp.name);
+
+    // fprintf(stderr, "SEARCHING: %s\n", name);
+    result = avl_search_lock(&host->rrdset_root_index_name, (avl *) (&(tmp.avlname)));
+    if(result) {
+        RRDSET *st = rrdset_from_avlname(result);
+        if(strcmp(st->magic, RRDSET_MAGIC))
+            error("Search for RRDSET %s returned an invalid RRDSET %s (name %s)", name, st->id, st->name);
+
+        // fprintf(stderr, "FOUND: %s\n", name);
+        return rrdset_from_avlname(result);
+    }
+    // fprintf(stderr, "NOT FOUND: %s\n", name);
+    return NULL;
 }
 
 
@@ -118,20 +201,20 @@ static RRDSET *rrdset_index_find_name(const char *name, uint32_t hash) {
 // RRDDIM index
 
 static int rrddim_compare(void* a, void* b) {
-       if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
-       else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
-       else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
+    if(((RRDDIM *)a)->hash < ((RRDDIM *)b)->hash) return -1;
+    else if(((RRDDIM *)a)->hash > ((RRDDIM *)b)->hash) return 1;
+    else return strcmp(((RRDDIM *)a)->id, ((RRDDIM *)b)->id);
 }
 
 #define rrddim_index_add(st, rd) avl_insert_lock(&((st)->dimensions_index), (avl *)(rd))
 #define rrddim_index_del(st,rd ) avl_remove_lock(&((st)->dimensions_index), (avl *)(rd))
 
 static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
-       RRDDIM tmp;
-       strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
-       tmp.hash = (hash)?hash:simple_hash(tmp.id);
+    RRDDIM tmp;
+    strncpyz(tmp.id, id, RRD_ID_LENGTH_MAX);
+    tmp.hash = (hash)?hash:simple_hash(tmp.id);
 
-       return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp);
+    return (RRDDIM *)avl_search_lock(&(st->dimensions_index), (avl *) &tmp);
 }
 
 // ----------------------------------------------------------------------------
@@ -139,29 +222,29 @@ static RRDDIM *rrddim_index_find(RRDSET *st, const char *id, uint32_t hash) {
 
 int rrdset_type_id(const char *name)
 {
-       if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) return RRDSET_TYPE_AREA;
-       else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) return RRDSET_TYPE_STACKED;
-       else if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) return RRDSET_TYPE_LINE;
-       return RRDSET_TYPE_LINE;
+    if(unlikely(strcmp(name, RRDSET_TYPE_AREA_NAME) == 0)) return RRDSET_TYPE_AREA;
+    else if(unlikely(strcmp(name, RRDSET_TYPE_STACKED_NAME) == 0)) return RRDSET_TYPE_STACKED;
+    else if(unlikely(strcmp(name, RRDSET_TYPE_LINE_NAME) == 0)) return RRDSET_TYPE_LINE;
+    return RRDSET_TYPE_LINE;
 }
 
 const char *rrdset_type_name(int chart_type)
 {
-       static char line[] = RRDSET_TYPE_LINE_NAME;
-       static char area[] = RRDSET_TYPE_AREA_NAME;
-       static char stacked[] = RRDSET_TYPE_STACKED_NAME;
+    static char line[] = RRDSET_TYPE_LINE_NAME;
+    static char area[] = RRDSET_TYPE_AREA_NAME;
+    static char stacked[] = RRDSET_TYPE_STACKED_NAME;
 
-       switch(chart_type) {
-               case RRDSET_TYPE_LINE:
-                       return line;
+    switch(chart_type) {
+        case RRDSET_TYPE_LINE:
+            return line;
 
-               case RRDSET_TYPE_AREA:
-                       return area;
+        case RRDSET_TYPE_AREA:
+            return area;
 
-               case RRDSET_TYPE_STACKED:
-                       return stacked;
-       }
-       return line;
+        case RRDSET_TYPE_STACKED:
+            return stacked;
+    }
+    return line;
 }
 
 // ----------------------------------------------------------------------------
@@ -169,33 +252,33 @@ const char *rrdset_type_name(int chart_type)
 
 const char *rrd_memory_mode_name(int id)
 {
-       static const char ram[] = RRD_MEMORY_MODE_RAM_NAME;
-       static const char map[] = RRD_MEMORY_MODE_MAP_NAME;
-       static const char save[] = RRD_MEMORY_MODE_SAVE_NAME;
+    static const char ram[] = RRD_MEMORY_MODE_RAM_NAME;
+    static const char map[] = RRD_MEMORY_MODE_MAP_NAME;
+    static const char save[] = RRD_MEMORY_MODE_SAVE_NAME;
 
-       switch(id) {
-               case RRD_MEMORY_MODE_RAM:
-                       return ram;
+    switch(id) {
+        case RRD_MEMORY_MODE_RAM:
+            return ram;
 
-               case RRD_MEMORY_MODE_MAP:
-                       return map;
+        case RRD_MEMORY_MODE_MAP:
+            return map;
 
-               case RRD_MEMORY_MODE_SAVE:
-               default:
-                       return save;
-       }
+        case RRD_MEMORY_MODE_SAVE:
+        default:
+            return save;
+    }
 
-       return save;
+    return save;
 }
 
 int rrd_memory_mode_id(const char *name)
 {
-       if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
-               return RRD_MEMORY_MODE_RAM;
-       else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
-               return RRD_MEMORY_MODE_MAP;
+    if(unlikely(!strcmp(name, RRD_MEMORY_MODE_RAM_NAME)))
+        return RRD_MEMORY_MODE_RAM;
+    else if(unlikely(!strcmp(name, RRD_MEMORY_MODE_MAP_NAME)))
+        return RRD_MEMORY_MODE_MAP;
 
-       return RRD_MEMORY_MODE_SAVE;
+    return RRD_MEMORY_MODE_SAVE;
 }
 
 // ----------------------------------------------------------------------------
@@ -203,34 +286,34 @@ int rrd_memory_mode_id(const char *name)
 
 int rrddim_algorithm_id(const char *name)
 {
-       if(strcmp(name, RRDDIM_INCREMENTAL_NAME) == 0)                  return RRDDIM_INCREMENTAL;
-       if(strcmp(name, RRDDIM_ABSOLUTE_NAME) == 0)                     return RRDDIM_ABSOLUTE;
-       if(strcmp(name, RRDDIM_PCENT_OVER_ROW_TOTAL_NAME) == 0)                 return RRDDIM_PCENT_OVER_ROW_TOTAL;
-       if(strcmp(name, RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME) == 0)        return RRDDIM_PCENT_OVER_DIFF_TOTAL;
-       return RRDDIM_ABSOLUTE;
+    if(strcmp(name, RRDDIM_INCREMENTAL_NAME) == 0)          return RRDDIM_INCREMENTAL;
+    if(strcmp(name, RRDDIM_ABSOLUTE_NAME) == 0)             return RRDDIM_ABSOLUTE;
+    if(strcmp(name, RRDDIM_PCENT_OVER_ROW_TOTAL_NAME) == 0)         return RRDDIM_PCENT_OVER_ROW_TOTAL;
+    if(strcmp(name, RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME) == 0)    return RRDDIM_PCENT_OVER_DIFF_TOTAL;
+    return RRDDIM_ABSOLUTE;
 }
 
 const char *rrddim_algorithm_name(int chart_type)
 {
-       static char absolute[] = RRDDIM_ABSOLUTE_NAME;
-       static char incremental[] = RRDDIM_INCREMENTAL_NAME;
-       static char percentage_of_absolute_row[] = RRDDIM_PCENT_OVER_ROW_TOTAL_NAME;
-       static char percentage_of_incremental_row[] = RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME;
+    static char absolute[] = RRDDIM_ABSOLUTE_NAME;
+    static char incremental[] = RRDDIM_INCREMENTAL_NAME;
+    static char percentage_of_absolute_row[] = RRDDIM_PCENT_OVER_ROW_TOTAL_NAME;
+    static char percentage_of_incremental_row[] = RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME;
 
-       switch(chart_type) {
-               case RRDDIM_ABSOLUTE:
-                       return absolute;
+    switch(chart_type) {
+        case RRDDIM_ABSOLUTE:
+            return absolute;
 
-               case RRDDIM_INCREMENTAL:
-                       return incremental;
+        case RRDDIM_INCREMENTAL:
+            return incremental;
 
-               case RRDDIM_PCENT_OVER_ROW_TOTAL:
-                       return percentage_of_absolute_row;
+        case RRDDIM_PCENT_OVER_ROW_TOTAL:
+            return percentage_of_absolute_row;
 
-               case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-                       return percentage_of_incremental_row;
-       }
-       return absolute;
+        case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+            return percentage_of_incremental_row;
+    }
+    return absolute;
 }
 
 // ----------------------------------------------------------------------------
@@ -238,35 +321,38 @@ const char *rrddim_algorithm_name(int chart_type)
 
 char *rrdset_strncpyz_name(char *to, const char *from, size_t length)
 {
-       char c, *p = to;
+    char c, *p = to;
 
-       while (length-- && (c = *from++)) {
-               if(c != '.' && !isalnum(c))
-                       c = '_';
+    while (length-- && (c = *from++)) {
+        if(c != '.' && !isalnum(c))
+            c = '_';
 
-               *p++ = c;
-       }
+        *p++ = c;
+    }
 
-       *p = '\0';
+    *p = '\0';
 
-       return to;
+    return to;
 }
 
 void rrdset_set_name(RRDSET *st, const char *name)
 {
-       debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
+    debug(D_RRD_CALLS, "rrdset_set_name() old: %s, new: %s", st->name, name);
 
-       if(st->name) rrdset_index_del_name(st);
+    if(st->name) {
+        rrdset_index_del_name(&localhost, st);
+        rrdsetvar_rename_all(st);
+    }
 
-       char b[CONFIG_MAX_VALUE + 1];
-       char n[RRD_ID_LENGTH_MAX + 1];
+    char b[CONFIG_MAX_VALUE + 1];
+    char n[RRD_ID_LENGTH_MAX + 1];
 
-       snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
-       rrdset_strncpyz_name(b, n, CONFIG_MAX_VALUE);
-       st->name = config_get(st->id, "name", b);
-       st->hash_name = simple_hash(st->name);
+    snprintfz(n, RRD_ID_LENGTH_MAX, "%s.%s", st->type, name);
+    rrdset_strncpyz_name(b, n, CONFIG_MAX_VALUE);
+    st->name = config_get(st->id, "name", b);
+    st->hash_name = simple_hash(st->name);
 
-       rrdset_index_add_name(st);
+    rrdset_index_add_name(&localhost, st);
 }
 
 // ----------------------------------------------------------------------------
@@ -274,30 +360,30 @@ void rrdset_set_name(RRDSET *st, const char *name)
 
 char *rrdset_cache_dir(const char *id)
 {
-       char *ret = NULL;
-
-       static char *cache_dir = NULL;
-       if(!cache_dir) {
-               cache_dir = config_get("global", "cache directory", CACHE_DIR);
-               int r = mkdir(cache_dir, 0755);
-               if(r != 0 && errno != EEXIST)
-                       error("Cannot create directory '%s'", cache_dir);
-       }
-
-       char b[FILENAME_MAX + 1];
-       char n[FILENAME_MAX + 1];
-       rrdset_strncpyz_name(b, id, FILENAME_MAX);
-
-       snprintfz(n, FILENAME_MAX, "%s/%s", cache_dir, b);
-       ret = config_get(id, "cache directory", n);
-
-       if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
-               int r = mkdir(ret, 0775);
-               if(r != 0 && errno != EEXIST)
-                       error("Cannot create directory '%s'", ret);
-       }
-
-       return ret;
+    char *ret = NULL;
+
+    static char *cache_dir = NULL;
+    if(!cache_dir) {
+        cache_dir = config_get("global", "cache directory", CACHE_DIR);
+        int r = mkdir(cache_dir, 0755);
+        if(r != 0 && errno != EEXIST)
+            error("Cannot create directory '%s'", cache_dir);
+    }
+
+    char b[FILENAME_MAX + 1];
+    char n[FILENAME_MAX + 1];
+    rrdset_strncpyz_name(b, id, FILENAME_MAX);
+
+    snprintfz(n, FILENAME_MAX, "%s/%s", cache_dir, b);
+    ret = config_get(id, "cache directory", n);
+
+    if(rrd_memory_mode == RRD_MEMORY_MODE_MAP || rrd_memory_mode == RRD_MEMORY_MODE_SAVE) {
+        int r = mkdir(ret, 0775);
+        if(r != 0 && errno != EEXIST)
+            error("Cannot create directory '%s'", ret);
+    }
+
+    return ret;
 }
 
 // ----------------------------------------------------------------------------
@@ -305,1055 +391,1097 @@ char *rrdset_cache_dir(const char *id)
 
 void rrdset_reset(RRDSET *st)
 {
-       debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
-
-       st->last_collected_time.tv_sec = 0;
-       st->last_collected_time.tv_usec = 0;
-       st->last_updated.tv_sec = 0;
-       st->last_updated.tv_usec = 0;
-       st->current_entry = 0;
-       st->counter = 0;
-       st->counter_done = 0;
-
-       RRDDIM *rd;
-       for(rd = st->dimensions; rd ; rd = rd->next) {
-               rd->last_collected_time.tv_sec = 0;
-               rd->last_collected_time.tv_usec = 0;
-               rd->counter = 0;
-               bzero(rd->values, rd->entries * sizeof(storage_number));
-       }
+    debug(D_RRD_CALLS, "rrdset_reset() %s", st->name);
+
+    st->last_collected_time.tv_sec = 0;
+    st->last_collected_time.tv_usec = 0;
+    st->last_updated.tv_sec = 0;
+    st->last_updated.tv_usec = 0;
+    st->current_entry = 0;
+    st->counter = 0;
+    st->counter_done = 0;
+
+    RRDDIM *rd;
+    for(rd = st->dimensions; rd ; rd = rd->next) {
+        rd->last_collected_time.tv_sec = 0;
+        rd->last_collected_time.tv_usec = 0;
+        rd->counter = 0;
+        bzero(rd->values, rd->entries * sizeof(storage_number));
+    }
 }
 
 RRDSET *rrdset_create(const char *type, const char *id, const char *name, const char *family, const char *context, const char *title, const char *units, long priority, int update_every, int chart_type)
 {
-       if(!type || !type[0]) {
-               fatal("Cannot create rrd stats without a type.");
-               return NULL;
-       }
-
-       if(!id || !id[0]) {
-               fatal("Cannot create rrd stats without an id.");
-               return NULL;
-       }
-
-       char fullid[RRD_ID_LENGTH_MAX + 1];
-       char fullfilename[FILENAME_MAX + 1];
-       RRDSET *st = NULL;
-
-       snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
-
-       st = rrdset_find(fullid);
-       if(st) {
-               error("Cannot create rrd stats for '%s', it already exists.", fullid);
-               return st;
-       }
-
-       long entries = config_get_number(fullid, "history", rrd_default_history_entries);
-       if(entries < 5) entries = config_set_number(fullid, "history", 5);
-       if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);
-
-       int enabled = config_get_boolean(fullid, "enabled", 1);
-       if(!enabled) entries = 5;
-
-       unsigned long size = sizeof(RRDSET);
-       char *cache_dir = rrdset_cache_dir(fullid);
-
-       debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
-
-       snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
-       if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
-       if(st) {
-               if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
-                       errno = 0;
-                       info("Initializing file %s.", fullfilename);
-                       bzero(st, size);
-               }
-               else if(strcmp(st->id, fullid) != 0) {
-                       errno = 0;
-                       error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
-                       // munmap(st, size);
-                       // st = NULL;
-                       bzero(st, size);
-               }
-               else if(st->memsize != size || st->entries != entries) {
-                       errno = 0;
-                       error("File %s does not have the desired size. Clearing it.", fullfilename);
-                       bzero(st, size);
-               }
-               else if(st->update_every != update_every) {
-                       errno = 0;
-                       error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
-                       bzero(st, size);
-               }
-               else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
-                       errno = 0;
-                       error("File %s is too old. Clearing it.", fullfilename);
-                       bzero(st, size);
-               }
-       }
-
-       if(st) {
-               st->name = NULL;
-               st->type = NULL;
-               st->family = NULL;
-               st->context = NULL;
-               st->title = NULL;
-               st->units = NULL;
-               st->dimensions = NULL;
-               st->next = NULL;
-               st->mapped = rrd_memory_mode;
-       }
-       else {
-               st = calloc(1, size);
-               if(!st) {
-                       fatal("Cannot allocate memory for RRD_STATS %s.%s", type, id);
-                       return NULL;
-               }
-               st->mapped = RRD_MEMORY_MODE_RAM;
-       }
-       st->memsize = size;
-       st->entries = entries;
-       st->update_every = update_every;
-
-       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->context    = config_get(st->id, "context", context?context:st->id);
-       st->units      = config_get(st->id, "units", units?units:"");
-
-       st->priority = config_get_number(st->id, "priority", priority);
-       st->enabled = enabled;
-
-       st->isdetail = 0;
-       st->debug = 0;
-
-       st->last_collected_time.tv_sec = 0;
-       st->last_collected_time.tv_usec = 0;
-       st->counter_done = 0;
-
-       st->gap_when_lost_iterations_above = (int) (
-                       config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
-
-       avl_init_lock(&st->dimensions_index, rrddim_compare);
-
-       pthread_rwlock_init(&st->rwlock, NULL);
-       pthread_rwlock_wrlock(&rrdset_root_rwlock);
-
-       if(name && *name) rrdset_set_name(st, name);
-       else rrdset_set_name(st, id);
-
-       {
-               char varvalue[CONFIG_MAX_VALUE + 1];
-               snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
-               st->title = config_get(st->id, "title", varvalue);
-       }
-
-       st->next = rrdset_root;
-       rrdset_root = st;
-
-       rrdset_index_add(st);
-
-       pthread_rwlock_unlock(&rrdset_root_rwlock);
-
-       return(st);
+    if(!type || !type[0]) {
+        fatal("Cannot create rrd stats without a type.");
+        return NULL;
+    }
+
+    if(!id || !id[0]) {
+        fatal("Cannot create rrd stats without an id.");
+        return NULL;
+    }
+
+    char fullid[RRD_ID_LENGTH_MAX + 1];
+    char fullfilename[FILENAME_MAX + 1];
+    RRDSET *st = NULL;
+
+    snprintfz(fullid, RRD_ID_LENGTH_MAX, "%s.%s", type, id);
+
+    st = rrdset_find(fullid);
+    if(st) {
+        error("Cannot create rrd stats for '%s', it already exists.", fullid);
+        return st;
+    }
+
+    long entries = config_get_number(fullid, "history", rrd_default_history_entries);
+    if(entries < 5) entries = config_set_number(fullid, "history", 5);
+    if(entries > RRD_HISTORY_ENTRIES_MAX) entries = config_set_number(fullid, "history", RRD_HISTORY_ENTRIES_MAX);
+
+    int enabled = config_get_boolean(fullid, "enabled", 1);
+    if(!enabled) entries = 5;
+
+    unsigned long size = sizeof(RRDSET);
+    char *cache_dir = rrdset_cache_dir(fullid);
+
+    debug(D_RRD_CALLS, "Creating RRD_STATS for '%s.%s'.", type, id);
+
+    snprintfz(fullfilename, FILENAME_MAX, "%s/main.db", cache_dir);
+    if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) st = (RRDSET *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 0);
+    if(st) {
+        if(strcmp(st->magic, RRDSET_MAGIC) != 0) {
+            errno = 0;
+            info("Initializing file %s.", fullfilename);
+            bzero(st, size);
+        }
+        else if(strcmp(st->id, fullid) != 0) {
+            errno = 0;
+            error("File %s contents are not for chart %s. Clearing it.", fullfilename, fullid);
+            // munmap(st, size);
+            // st = NULL;
+            bzero(st, size);
+        }
+        else if(st->memsize != size || st->entries != entries) {
+            errno = 0;
+            error("File %s does not have the desired size. Clearing it.", fullfilename);
+            bzero(st, size);
+        }
+        else if(st->update_every != update_every) {
+            errno = 0;
+            error("File %s does not have the desired update frequency. Clearing it.", fullfilename);
+            bzero(st, size);
+        }
+        else if((time(NULL) - st->last_updated.tv_sec) > update_every * entries) {
+            errno = 0;
+            error("File %s is too old. Clearing it.", fullfilename);
+            bzero(st, size);
+        }
+    }
+
+    if(st) {
+        st->name = NULL;
+        st->type = NULL;
+        st->family = NULL;
+        st->context = NULL;
+        st->title = NULL;
+        st->units = NULL;
+        st->dimensions = NULL;
+        st->next = NULL;
+        st->mapped = rrd_memory_mode;
+        st->variables = NULL;
+    }
+    else {
+        st = callocz(1, size);
+        st->mapped = RRD_MEMORY_MODE_RAM;
+    }
+    st->memsize = size;
+    st->entries = entries;
+    st->update_every = update_every;
+
+    if(st->current_entry >= st->entries) st->current_entry = 0;
+
+    strcpy(st->cache_filename, fullfilename);
+    strcpy(st->magic, RRDSET_MAGIC);
+
+    strcpy(st->id, fullid);
+    st->hash = simple_hash(st->id);
+
+    st->cache_dir = cache_dir;
+
+    st->chart_type = rrdset_type_id(config_get(st->id, "chart type", rrdset_type_name(chart_type)));
+    st->type       = config_get(st->id, "type", type);
+    st->family     = config_get(st->id, "family", family?family:st->type);
+    st->units      = config_get(st->id, "units", units?units:"");
+
+    st->context    = config_get(st->id, "context", context?context:st->id);
+    st->hash_context = simple_hash(st->context);
+
+    st->priority = config_get_number(st->id, "priority", priority);
+    st->enabled = enabled;
+
+    st->isdetail = 0;
+    st->debug = 0;
+
+    st->last_collected_time.tv_sec = 0;
+    st->last_collected_time.tv_usec = 0;
+    st->counter_done = 0;
+
+    st->gap_when_lost_iterations_above = (int) (
+            config_get_number(st->id, "gap when lost iterations above", RRD_DEFAULT_GAP_INTERPOLATIONS) + 2);
+
+    avl_init_lock(&st->dimensions_index, rrddim_compare);
+    avl_init_lock(&st->variables_root_index, rrdvar_compare);
+
+    pthread_rwlock_init(&st->rwlock, NULL);
+    rrdhost_rwlock(&localhost);
+
+    if(name && *name) rrdset_set_name(st, name);
+    else rrdset_set_name(st, id);
+
+    {
+        char varvalue[CONFIG_MAX_VALUE + 1];
+        snprintfz(varvalue, CONFIG_MAX_VALUE, "%s (%s)", title?title:"", st->name);
+        st->title = config_get(st->id, "title", varvalue);
+    }
+
+    st->rrdcontext = rrdcontext_create(st->context);
+    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);
+    }
+
+    rrdset_index_add(&localhost, st);
+
+    rrdsetcalc_link_matching(st);
+    rrdcalctemplate_link_matching(st);
+
+    rrdhost_unlock(&localhost);
+
+    return(st);
 }
 
 RRDDIM *rrddim_add(RRDSET *st, const char *id, const char *name, long multiplier, long divisor, int algorithm)
 {
-       char filename[FILENAME_MAX + 1];
-       char fullfilename[FILENAME_MAX + 1];
-
-       char varname[CONFIG_MAX_NAME + 1];
-       RRDDIM *rd = NULL;
-       unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
-
-       debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
-
-       rrdset_strncpyz_name(filename, id, FILENAME_MAX);
-       snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
-       if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
-       if(rd) {
-               struct timeval now;
-               gettimeofday(&now, NULL);
-
-               if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
-                       errno = 0;
-                       info("Initializing file %s.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(rd->memsize != size) {
-                       errno = 0;
-                       error("File %s does not have the desired size. Clearing it.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(rd->multiplier != multiplier) {
-                       errno = 0;
-                       error("File %s does not have the same multiplier. Clearing it.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(rd->divisor != divisor) {
-                       errno = 0;
-                       error("File %s does not have the same divisor. Clearing it.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(rd->algorithm != algorithm) {
-                       errno = 0;
-                       error("File %s does not have the same algorithm. Clearing it.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(rd->update_every != st->update_every) {
-                       errno = 0;
-                       error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(usecdiff(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
-                       errno = 0;
-                       error("File %s is too old. Clearing it.", fullfilename);
-                       bzero(rd, size);
-               }
-               else if(strcmp(rd->id, id) != 0) {
-                       errno = 0;
-                       error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
-                       // munmap(rd, size);
-                       // rd = NULL;
-                       bzero(rd, size);
-               }
-       }
-
-       if(rd) {
-               // we have a file mapped for rd
-               rd->mapped = rrd_memory_mode;
-               rd->flags = 0x00000000;
-               rd->next = NULL;
-               rd->name = NULL;
-       }
-       else {
-               // if we didn't manage to get a mmap'd dimension, just create one
-
-               rd = calloc(1, size);
-               if(!rd) {
-                       fatal("Cannot allocate RRD_DIMENSION %s/%s.", st->id, id);
-                       return NULL;
-               }
-
-               rd->mapped = RRD_MEMORY_MODE_RAM;
-       }
-       rd->memsize = size;
-
-       strcpy(rd->magic, RRDDIMENSION_MAGIC);
-       strcpy(rd->cache_filename, fullfilename);
-       strncpyz(rd->id, id, RRD_ID_LENGTH_MAX);
-       rd->hash = simple_hash(rd->id);
-
-       snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
-       rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
-
-       snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
-       rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));
-
-       snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
-       rd->multiplier = config_get_number(st->id, varname, multiplier);
-
-       snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
-       rd->divisor = config_get_number(st->id, varname, divisor);
-       if(!rd->divisor) rd->divisor = 1;
-
-       rd->entries = st->entries;
-       rd->update_every = st->update_every;
-
-       // prevent incremental calculation spikes
-       rd->counter = 0;
-       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->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;
-
-       // append this dimension
-       pthread_rwlock_wrlock(&st->rwlock);
-       if(!st->dimensions)
-               st->dimensions = rd;
-       else {
-               RRDDIM *td = st->dimensions;
-               for(; td->next; td = td->next) ;
-               td->next = rd;
-       }
-       pthread_rwlock_unlock(&st->rwlock);
-
-       rrddim_index_add(st, rd);
-
-       return(rd);
+    char filename[FILENAME_MAX + 1];
+    char fullfilename[FILENAME_MAX + 1];
+
+    char varname[CONFIG_MAX_NAME + 1];
+    RRDDIM *rd = NULL;
+    unsigned long size = sizeof(RRDDIM) + (st->entries * sizeof(storage_number));
+
+    debug(D_RRD_CALLS, "Adding dimension '%s/%s'.", st->id, id);
+
+    rrdset_strncpyz_name(filename, id, FILENAME_MAX);
+    snprintfz(fullfilename, FILENAME_MAX, "%s/%s.db", st->cache_dir, filename);
+    if(rrd_memory_mode != RRD_MEMORY_MODE_RAM) rd = (RRDDIM *)mymmap(fullfilename, size, ((rrd_memory_mode == RRD_MEMORY_MODE_MAP)?MAP_SHARED:MAP_PRIVATE), 1);
+    if(rd) {
+        struct timeval now;
+        gettimeofday(&now, NULL);
+
+        if(strcmp(rd->magic, RRDDIMENSION_MAGIC) != 0) {
+            errno = 0;
+            info("Initializing file %s.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(rd->memsize != size) {
+            errno = 0;
+            error("File %s does not have the desired size. Clearing it.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(rd->multiplier != multiplier) {
+            errno = 0;
+            error("File %s does not have the same multiplier. Clearing it.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(rd->divisor != divisor) {
+            errno = 0;
+            error("File %s does not have the same divisor. Clearing it.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(rd->algorithm != algorithm) {
+            errno = 0;
+            error("File %s does not have the same algorithm. Clearing it.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(rd->update_every != st->update_every) {
+            errno = 0;
+            error("File %s does not have the same refresh frequency. Clearing it.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(usec_dt(&now, &rd->last_collected_time) > (rd->entries * rd->update_every * 1000000ULL)) {
+            errno = 0;
+            error("File %s is too old. Clearing it.", fullfilename);
+            bzero(rd, size);
+        }
+        else if(strcmp(rd->id, id) != 0) {
+            errno = 0;
+            error("File %s contents are not for dimension %s. Clearing it.", fullfilename, id);
+            // munmap(rd, size);
+            // rd = NULL;
+            bzero(rd, size);
+        }
+    }
+
+    if(rd) {
+        // we have a file mapped for rd
+        rd->mapped = rrd_memory_mode;
+        rd->flags = 0x00000000;
+        rd->variables = NULL;
+        rd->next = NULL;
+        rd->name = NULL;
+    }
+    else {
+        // if we didn't manage to get a mmap'd dimension, just create one
+
+        rd = callocz(1, size);
+        rd->mapped = RRD_MEMORY_MODE_RAM;
+    }
+    rd->memsize = size;
+
+    strcpy(rd->magic, RRDDIMENSION_MAGIC);
+    strcpy(rd->cache_filename, fullfilename);
+    strncpyz(rd->id, id, RRD_ID_LENGTH_MAX);
+    rd->hash = simple_hash(rd->id);
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
+    rd->name = config_get(st->id, varname, (name && *name)?name:rd->id);
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s algorithm", rd->id);
+    rd->algorithm = rrddim_algorithm_id(config_get(st->id, varname, rrddim_algorithm_name(algorithm)));
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s multiplier", rd->id);
+    rd->multiplier = config_get_number(st->id, varname, multiplier);
+
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s divisor", rd->id);
+    rd->divisor = config_get_number(st->id, varname, divisor);
+    if(!rd->divisor) rd->divisor = 1;
+
+    rd->entries = st->entries;
+    rd->update_every = st->update_every;
+
+    // prevent incremental calculation spikes
+    rd->counter = 0;
+    rd->updated = 0;
+    rd->calculated_value = 0;
+    rd->last_calculated_value = 0;
+    rd->collected_value = 0;
+    rd->last_collected_value = 0;
+    rd->collected_volume = 0;
+    rd->stored_volume = 0;
+    rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+    rd->last_collected_time.tv_sec = 0;
+    rd->last_collected_time.tv_usec = 0;
+    rd->rrdset = st;
+
+    // append this dimension
+    pthread_rwlock_wrlock(&st->rwlock);
+    if(!st->dimensions)
+        st->dimensions = rd;
+    else {
+        RRDDIM *td = st->dimensions;
+        for(; td->next; td = td->next) ;
+        td->next = rd;
+    }
+
+    if(health_enabled) {
+        rrddimvar_create(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->last_stored_value, 0);
+        rrddimvar_create(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->last_collected_value, 0);
+        rrddimvar_create(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->last_collected_time.tv_sec, 0);
+    }
+
+    pthread_rwlock_unlock(&st->rwlock);
+
+    rrddim_index_add(st, rd);
+
+    return(rd);
 }
 
 void rrddim_set_name(RRDSET *st, RRDDIM *rd, const char *name)
 {
-       debug(D_RRD_CALLS, "rrddim_set_name() %s.%s", st->name, rd->name);
+    debug(D_RRD_CALLS, "rrddim_set_name() %s.%s", st->name, rd->name);
+
+    char varname[CONFIG_MAX_NAME + 1];
+    snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
+    config_set_default(st->id, varname, name);
 
-       char varname[CONFIG_MAX_NAME + 1];
-       snprintfz(varname, CONFIG_MAX_NAME, "dim %s name", rd->id);
-       config_set_default(st->id, varname, name);
+    rrddimvar_rename_all(rd);
 }
 
 void rrddim_free(RRDSET *st, RRDDIM *rd)
 {
-       debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
-
-       RRDDIM *i, *last = NULL;
-       for(i = st->dimensions; i && i != rd ; i = i->next) last = i;
-
-       if(!i) {
-               error("Request to free dimension %s.%s but it is not linked.", st->id, rd->name);
-               return;
-       }
-
-       if(last) last->next = rd->next;
-       else st->dimensions = rd->next;
-       rd->next = NULL;
-
-       rrddim_index_del(st, rd);
-
-       // free(rd->annotations);
-       if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
-               debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
-               savememory(rd->cache_filename, rd, rd->memsize);
-
-               debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
-               munmap(rd, rd->memsize);
-       }
-       else if(rd->mapped == RRD_MEMORY_MODE_MAP) {
-               debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
-               munmap(rd, rd->memsize);
-       }
-       else {
-               debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
-               free(rd);
-       }
+    debug(D_RRD_CALLS, "rrddim_free() %s.%s", st->name, rd->name);
+
+    RRDDIM *i, *last = NULL;
+    for(i = st->dimensions; i && i != rd ; i = i->next) last = i;
+
+    if(!i) {
+        error("Request to free dimension %s.%s but it is not linked.", st->id, rd->name);
+        return;
+    }
+
+    if(last) last->next = rd->next;
+    else st->dimensions = rd->next;
+    rd->next = NULL;
+
+    while(rd->variables)
+        rrddimvar_free(rd->variables);
+
+    rrddim_index_del(st, rd);
+
+    // free(rd->annotations);
+    if(rd->mapped == RRD_MEMORY_MODE_SAVE) {
+        debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
+        savememory(rd->cache_filename, rd, rd->memsize);
+
+        debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
+        munmap(rd, rd->memsize);
+    }
+    else if(rd->mapped == RRD_MEMORY_MODE_MAP) {
+        debug(D_RRD_CALLS, "Unmapping dimension '%s'.", rd->name);
+        munmap(rd, rd->memsize);
+    }
+    else {
+        debug(D_RRD_CALLS, "Removing dimension '%s'.", rd->name);
+        freez(rd);
+    }
 }
 
 void rrdset_free_all(void)
 {
-       info("Freeing all memory...");
+    info("Freeing all memory...");
+
+    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->calculations)
+            rrdsetcalc_unlink(st->calculations);
+
+        while(st->dimensions)
+            rrddim_free(st, st->dimensions);
+
+        rrdset_index_del(&localhost, st);
 
-       RRDSET *st;
-       for(st = rrdset_root; st ;) {
-               RRDSET *next = st->next;
+        st->rrdcontext->use_count--;
+        if(!st->rrdcontext->use_count)
+            rrdcontext_free(st->rrdcontext);
 
-               while(st->dimensions)
-                       rrddim_free(st, st->dimensions);
+        pthread_rwlock_unlock(&st->rwlock);
 
-               rrdset_index_del(st);
+        if(st->mapped == RRD_MEMORY_MODE_SAVE) {
+            debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
+            savememory(st->cache_filename, st, st->memsize);
 
-               if(st->mapped == RRD_MEMORY_MODE_SAVE) {
-                       debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
-                       savememory(st->cache_filename, st, st->memsize);
+            debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
+            munmap(st, st->memsize);
+        }
+        else if(st->mapped == RRD_MEMORY_MODE_MAP) {
+            debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
+            munmap(st, st->memsize);
+        }
+        else
+            freez(st);
 
-                       debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
-                       munmap(st, st->memsize);
-               }
-               else if(st->mapped == RRD_MEMORY_MODE_MAP) {
-                       debug(D_RRD_CALLS, "Unmapping stats '%s'.", st->name);
-                       munmap(st, st->memsize);
-               }
-               else
-                       free(st);
+        st = next;
+    }
+    localhost.rrdset_root = NULL;
 
-               st = next;
-       }
-       rrdset_root = NULL;
+    rrdhost_unlock(&localhost);
 
-       info("Memory cleanup completed...");
+    info("Memory cleanup completed...");
 }
 
 void rrdset_save_all(void) {
-       info("Saving database...");
-
-       RRDSET *st;
-       RRDDIM *rd;
-
-       pthread_rwlock_wrlock(&rrdset_root_rwlock);
-       for(st = rrdset_root; st ; st = st->next) {
-               pthread_rwlock_wrlock(&st->rwlock);
-
-               if(st->mapped == RRD_MEMORY_MODE_SAVE) {
-                       debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
-                       savememory(st->cache_filename, st, st->memsize);
-               }
-
-               for(rd = st->dimensions; rd ; rd = rd->next) {
-                       if(likely(rd->mapped == RRD_MEMORY_MODE_SAVE)) {
-                               debug(D_RRD_CALLS, "Saving dimension '%s' to '%s'.", rd->name, rd->cache_filename);
-                               savememory(rd->cache_filename, rd, rd->memsize);
-                       }
-               }
-
-               pthread_rwlock_unlock(&st->rwlock);
-       }
-       pthread_rwlock_unlock(&rrdset_root_rwlock);
+    info("Saving database...");
+
+    RRDSET *st;
+    RRDDIM *rd;
+
+    rrdhost_rwlock(&localhost);
+    for(st = localhost.rrdset_root; st ; st = st->next) {
+        pthread_rwlock_wrlock(&st->rwlock);
+
+        if(st->mapped == RRD_MEMORY_MODE_SAVE) {
+            debug(D_RRD_CALLS, "Saving stats '%s' to '%s'.", st->name, st->cache_filename);
+            savememory(st->cache_filename, st, st->memsize);
+        }
+
+        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);
+    debug(D_RRD_CALLS, "rrdset_find() for chart %s", id);
 
-       RRDSET *st = rrdset_index_find(id, 0);
-       return(st);
+    RRDSET *st = rrdset_index_find(&localhost, id, 0);
+    return(st);
 }
 
 RRDSET *rrdset_find_bytype(const char *type, const char *id)
 {
-       debug(D_RRD_CALLS, "rrdset_find_bytype() for chart %s.%s", type, id);
+    debug(D_RRD_CALLS, "rrdset_find_bytype() for chart %s.%s", type, id);
 
-       char buf[RRD_ID_LENGTH_MAX + 1];
+    char buf[RRD_ID_LENGTH_MAX + 1];
 
-       strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1);
-       strcat(buf, ".");
-       int len = (int) strlen(buf);
-       strncpyz(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
+    strncpyz(buf, type, RRD_ID_LENGTH_MAX - 1);
+    strcat(buf, ".");
+    int len = (int) strlen(buf);
+    strncpyz(&buf[len], id, (size_t) (RRD_ID_LENGTH_MAX - len));
 
-       return(rrdset_find(buf));
+    return(rrdset_find(buf));
 }
 
 RRDSET *rrdset_find_byname(const char *name)
 {
-       debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
+    debug(D_RRD_CALLS, "rrdset_find_byname() for chart %s", name);
 
-       RRDSET *st = rrdset_index_find_name(name, 0);
-       return(st);
+    RRDSET *st = rrdset_index_find_name(&localhost, name, 0);
+    return(st);
 }
 
 RRDDIM *rrddim_find(RRDSET *st, const char *id)
 {
-       debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
+    debug(D_RRD_CALLS, "rrddim_find() for chart %s, dimension %s", st->name, id);
 
-       return rrddim_index_find(st, id, 0);
+    return rrddim_index_find(st, id, 0);
 }
 
 int rrddim_hide(RRDSET *st, const char *id)
 {
-       debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
+    debug(D_RRD_CALLS, "rrddim_hide() for chart %s, dimension %s", st->name, id);
 
-       RRDDIM *rd = rrddim_find(st, id);
-       if(unlikely(!rd)) {
-               error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-               return 1;
-       }
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(!rd)) {
+        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+        return 1;
+    }
 
-       rd->flags |= RRDDIM_FLAG_HIDDEN;
-       return 0;
+    rd->flags |= RRDDIM_FLAG_HIDDEN;
+    return 0;
 }
 
 int rrddim_unhide(RRDSET *st, const char *id)
 {
-       debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
+    debug(D_RRD_CALLS, "rrddim_unhide() for chart %s, dimension %s", st->name, id);
 
-       RRDDIM *rd = rrddim_find(st, id);
-       if(unlikely(!rd)) {
-               error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-               return 1;
-       }
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(!rd)) {
+        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+        return 1;
+    }
 
-       if(rd->flags & RRDDIM_FLAG_HIDDEN) rd->flags ^= RRDDIM_FLAG_HIDDEN;
-       return 0;
+    if(rd->flags & RRDDIM_FLAG_HIDDEN) rd->flags ^= RRDDIM_FLAG_HIDDEN;
+    return 0;
 }
 
 collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number value)
 {
-       debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
+    debug(D_RRD_CALLS, "rrddim_set_by_pointer() for chart %s, dimension %s, value " COLLECTED_NUMBER_FORMAT, st->name, rd->name, value);
 
-       gettimeofday(&rd->last_collected_time, NULL);
-       rd->collected_value = value;
-       rd->updated = 1;
-       rd->counter++;
+    gettimeofday(&rd->last_collected_time, NULL);
+    rd->collected_value = value;
+    rd->updated = 1;
+    rd->counter++;
 
-       return rd->last_collected_value;
+    return rd->last_collected_value;
 }
 
 collected_number rrddim_set(RRDSET *st, const char *id, collected_number value)
 {
-       RRDDIM *rd = rrddim_find(st, id);
-       if(unlikely(!rd)) {
-               error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
-               return 0;
-       }
+    RRDDIM *rd = rrddim_find(st, id);
+    if(unlikely(!rd)) {
+        error("Cannot find dimension with id '%s' on stats '%s' (%s).", id, st->name, st->id);
+        return 0;
+    }
 
-       return rrddim_set_by_pointer(st, rd, value);
+    return rrddim_set_by_pointer(st, rd, value);
 }
 
 void rrdset_next_usec(RRDSET *st, unsigned long long microseconds)
 {
-       if(!microseconds) rrdset_next(st);
-       else {
-               debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
+    if(!microseconds) rrdset_next(st);
+    else {
+        debug(D_RRD_CALLS, "rrdset_next_usec() for chart %s with microseconds %llu", st->name, microseconds);
 
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
-               st->usec_since_last_update = microseconds;
-       }
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: NEXT: %llu microseconds", st->name, microseconds);
+        st->usec_since_last_update = microseconds;
+    }
 }
 
 void rrdset_next(RRDSET *st)
 {
-       unsigned long long microseconds = 0;
+    unsigned long long microseconds = 0;
 
-       if(likely(st->last_collected_time.tv_sec)) {
-               struct timeval now;
-               gettimeofday(&now, NULL);
-               microseconds = usecdiff(&now, &st->last_collected_time);
-       }
-       // prevent infinite loop
-       else microseconds = st->update_every * 1000000ULL;
+    if(likely(st->last_collected_time.tv_sec)) {
+        struct timeval now;
+        gettimeofday(&now, NULL);
+        microseconds = usec_dt(&now, &st->last_collected_time);
+    }
+    // prevent infinite loop
+    else microseconds = st->update_every * 1000000ULL;
 
-       rrdset_next_usec(st, microseconds);
+    rrdset_next_usec(st, microseconds);
 }
 
 void rrdset_next_plugins(RRDSET *st)
 {
-       rrdset_next(st);
+    rrdset_next(st);
 }
 
 unsigned long long rrdset_done(RRDSET *st)
 {
-       if(unlikely(netdata_exit)) return 0;
-
-       debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
-
-       RRDDIM *rd, *last;
-       int oldstate, store_this_entry = 1, first_entry = 0;
-       unsigned long long last_ut, now_ut, next_ut, stored_entries = 0;
-
-       if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
-               error("Cannot set pthread cancel state to DISABLE.");
-
-       // a read lock is OK here
-       pthread_rwlock_rdlock(&st->rwlock);
-
-       // enable the chart, if it was disabled
-       if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
-               st->enabled = 1;
-
-       // check if the chart has a long time to be updated
-       if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
-               info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
-               rrdset_reset(st);
-               st->usec_since_last_update = st->update_every * 1000000ULL;
-               first_entry = 1;
-       }
-       if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
-
-       // set last_collected_time
-       if(unlikely(!st->last_collected_time.tv_sec)) {
-               // it is the first entry
-               // set the last_collected_time to now
-               gettimeofday(&st->last_collected_time, NULL);
-
-               // the first entry should not be stored
-               store_this_entry = 0;
-               first_entry = 1;
-
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
-       }
-       else {
-               // it is not the first entry
-               // calculate the proper last_collected_time, using usec_since_last_update
-               unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
-               st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL);
-               st->last_collected_time.tv_usec = (suseconds_t) (ut % 1000000ULL);
-       }
-
-       // if this set has not been updated in the past
-       // we fake the last_update time to be = now - usec_since_last_update
-       if(unlikely(!st->last_updated.tv_sec)) {
-               // it has never been updated before
-               // set a fake last_updated, in the past using usec_since_last_update
-               unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
-               st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
-               st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL);
-
-               // the first entry should not be stored
-               store_this_entry = 0;
-               first_entry = 1;
-
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf). Will not store the next entry.", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
-       }
-
-       // check if we will re-write the entire data set
-       if(unlikely(usecdiff(&st->last_collected_time, &st->last_updated) > st->update_every * st->entries * 1000000ULL)) {
-               info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
-               rrdset_reset(st);
-
-               st->usec_since_last_update = st->update_every * 1000000ULL;
-
-               gettimeofday(&st->last_collected_time, NULL);
-
-               unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
-               st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
-               st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL);
-
-               // the first entry should not be stored
-               store_this_entry = 0;
-               first_entry = 1;
-       }
-
-       // these are the 3 variables that will help us in interpolation
-       // last_ut = the last time we added a value to the storage
-       //  now_ut = the time the current value is taken at
-       // next_ut = the time of the next interpolation point
-       last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
-       now_ut  = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
-       next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
-
-       if(unlikely(!first_entry && now_ut < next_ut)) {
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
-       }
-
-       if(unlikely(st->debug)) {
-               debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
-               debug(D_RRD_STATS, "%s: now  ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
-               debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
-       }
-
-       if(unlikely(!st->counter_done)) {
-               store_this_entry = 0;
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
-       }
-       st->counter_done++;
-
-       // calculate totals and count the dimensions
-       int dimensions;
-       st->collected_total = 0;
-       for( rd = st->dimensions, dimensions = 0 ; likely(rd) ; rd = rd->next, dimensions++ )
-               if(likely(rd->updated)) st->collected_total += rd->collected_value;
-
-       uint32_t storage_flags = SN_EXISTS;
-
-       // process all dimensions to calculate their values
-       // based on the collected figures only
-       // at this stage we do not interpolate anything
-       for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
-
-               if(unlikely(!rd->updated)) {
-                       rd->calculated_value = 0;
-                       continue;
-               }
-
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: START "
-                       " last_collected_value = " COLLECTED_NUMBER_FORMAT
-                       " collected_value = " COLLECTED_NUMBER_FORMAT
-                       " last_calculated_value = " CALCULATED_NUMBER_FORMAT
-                       " calculated_value = " CALCULATED_NUMBER_FORMAT
-                       , st->id, rd->name
-                       , rd->last_collected_value
-                       , rd->collected_value
-                       , rd->last_calculated_value
-                       , rd->calculated_value
-                       );
-
-               switch(rd->algorithm) {
-                       case RRDDIM_ABSOLUTE:
-                               rd->calculated_value = (calculated_number)rd->collected_value
-                                       * (calculated_number)rd->multiplier
-                                       / (calculated_number)rd->divisor;
-
-                               if(unlikely(st->debug))
-                                       debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
-                                               CALCULATED_NUMBER_FORMAT " = "
-                                               COLLECTED_NUMBER_FORMAT
-                                               " * " CALCULATED_NUMBER_FORMAT
-                                               " / " CALCULATED_NUMBER_FORMAT
-                                               , st->id, rd->name
-                                               , rd->calculated_value
-                                               , rd->collected_value
-                                               , (calculated_number)rd->multiplier
-                                               , (calculated_number)rd->divisor
-                                               );
-                               break;
-
-                       case RRDDIM_PCENT_OVER_ROW_TOTAL:
-                               if(unlikely(!st->collected_total))
-                                       rd->calculated_value = 0;
-                               else
-                                       // the percentage of the current value
-                                       // over the total of all dimensions
-                                       rd->calculated_value =
-                                             (calculated_number)100
-                                           * (calculated_number)rd->collected_value
-                                           / (calculated_number)st->collected_total;
-
-                               if(unlikely(st->debug))
-                                       debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
-                                               CALCULATED_NUMBER_FORMAT " = 100"
-                                               " * " COLLECTED_NUMBER_FORMAT
-                                               " / " COLLECTED_NUMBER_FORMAT
-                                               , st->id, rd->name
-                                               , rd->calculated_value
-                                               , rd->collected_value
-                                               , st->collected_total
-                                               );
-                               break;
-
-                       case RRDDIM_INCREMENTAL:
-                               if(unlikely(rd->counter <= 1)) {
-                                       rd->calculated_value = 0;
-                                       continue;
-                               }
-
-                               // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
-                               // to reset the calculation (it will give zero as the calculation for this second)
-                               if(unlikely(rd->last_collected_value > rd->collected_value)) {
-                                       debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
-                                                       , st->name, rd->name
-                                                       , rd->last_collected_value
-                                                       , rd->collected_value);
-                                       if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
-                                       rd->last_collected_value = rd->collected_value;
-                               }
-
-                               rd->calculated_value =
-                                     (calculated_number)(rd->collected_value - rd->last_collected_value)
-                                   * (calculated_number)rd->multiplier
-                                   / (calculated_number)rd->divisor;
-
-                               if(unlikely(st->debug))
-                                       debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
-                                               CALCULATED_NUMBER_FORMAT " = ("
-                                               COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
-                                               ")"
-                                               " * " CALCULATED_NUMBER_FORMAT
-                                               " / " CALCULATED_NUMBER_FORMAT
-                                               , st->id, rd->name
-                                               , rd->calculated_value
-                                               , rd->collected_value, rd->last_collected_value
-                                               , (calculated_number)rd->multiplier
-                                               , (calculated_number)rd->divisor
-                                               );
-                               break;
-
-                       case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-                               if(unlikely(rd->counter <= 1)) {
-                                       rd->calculated_value = 0;
-                                       continue;
-                               }
-
-                               // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
-                               // to reset the calculation (it will give zero as the calculation for this second)
-                               if(unlikely(rd->last_collected_value > rd->collected_value)) {
-                                       debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
-                                       , st->name, rd->name
-                                       , rd->last_collected_value
-                                       , rd->collected_value);
-                                       if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
-                                       rd->last_collected_value = rd->collected_value;
-                               }
-
-                               // the percentage of the current increment
-                               // over the increment of all dimensions together
-                               if(unlikely(st->collected_total == st->last_collected_total))
-                                       rd->calculated_value = 0;
-                               else
-                                       rd->calculated_value =
-                                                 (calculated_number)100
-                                               * (calculated_number)(rd->collected_value - rd->last_collected_value)
-                                               / (calculated_number)(st->collected_total - st->last_collected_total);
-
-                               if(unlikely(st->debug))
-                                       debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
-                                               CALCULATED_NUMBER_FORMAT " = 100"
-                                               " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
-                                               " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
-                                               , st->id, rd->name
-                                               , rd->calculated_value
-                                               , rd->collected_value, rd->last_collected_value
-                                               , st->collected_total, st->last_collected_total
-                                               );
-                               break;
-
-                       default:
-                               // make the default zero, to make sure
-                               // it gets noticed when we add new types
-                               rd->calculated_value = 0;
-
-                               if(unlikely(st->debug))
-                                       debug(D_RRD_STATS, "%s/%s: CALC "
-                                               CALCULATED_NUMBER_FORMAT " = 0"
-                                               , st->id, rd->name
-                                               , rd->calculated_value
-                                               );
-                               break;
-               }
-
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: PHASE2 "
-                       " last_collected_value = " COLLECTED_NUMBER_FORMAT
-                       " collected_value = " COLLECTED_NUMBER_FORMAT
-                       " last_calculated_value = " CALCULATED_NUMBER_FORMAT
-                       " calculated_value = " CALCULATED_NUMBER_FORMAT
-                       , st->id, rd->name
-                       , rd->last_collected_value
-                       , rd->collected_value
-                       , rd->last_calculated_value
-                       , rd->calculated_value
-                       );
-
-       }
-
-       // at this point we have all the calculated values ready
-       // it is now time to interpolate values on a second boundary
-
-       unsigned long long first_ut = last_ut;
-       long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
-       if((now_ut % (st->update_every * 1000000ULL)) == 0) iterations++;
-
-       for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
+    if(unlikely(netdata_exit)) return 0;
+
+    debug(D_RRD_CALLS, "rrdset_done() for chart %s", st->name);
+
+    RRDDIM *rd, *last;
+    int oldstate, store_this_entry = 1, first_entry = 0;
+    unsigned long long last_ut, now_ut, next_ut, stored_entries = 0;
+
+    if(unlikely(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0))
+        error("Cannot set pthread cancel state to DISABLE.");
+
+    // a read lock is OK here
+    pthread_rwlock_rdlock(&st->rwlock);
+
+    // enable the chart, if it was disabled
+    if(unlikely(rrd_delete_unupdated_dimensions) && !st->enabled)
+        st->enabled = 1;
+
+    // check if the chart has a long time to be updated
+    if(unlikely(st->usec_since_last_update > st->entries * st->update_every * 1000000ULL)) {
+        info("%s: took too long to be updated (%0.3Lf secs). Reseting it.", st->name, (long double)(st->usec_since_last_update / 1000000.0));
+        rrdset_reset(st);
+        st->usec_since_last_update = st->update_every * 1000000ULL;
+        first_entry = 1;
+    }
+    if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: microseconds since last update: %llu", st->name, st->usec_since_last_update);
+
+    // set last_collected_time
+    if(unlikely(!st->last_collected_time.tv_sec)) {
+        // it is the first entry
+        // set the last_collected_time to now
+        gettimeofday(&st->last_collected_time, NULL);
+
+        // the first entry should not be stored
+        store_this_entry = 0;
+        first_entry = 1;
+
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: has not set last_collected_time. Setting it now. Will not store the next entry.", st->name);
+    }
+    else {
+        // it is not the first entry
+        // calculate the proper last_collected_time, using usec_since_last_update
+        unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec + st->usec_since_last_update;
+        st->last_collected_time.tv_sec = (time_t) (ut / 1000000ULL);
+        st->last_collected_time.tv_usec = (suseconds_t) (ut % 1000000ULL);
+    }
+
+    // if this set has not been updated in the past
+    // we fake the last_update time to be = now - usec_since_last_update
+    if(unlikely(!st->last_updated.tv_sec)) {
+        // it has never been updated before
+        // set a fake last_updated, in the past using usec_since_last_update
+        unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
+        st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
+        st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL);
+
+        // the first entry should not be stored
+        store_this_entry = 0;
+        first_entry = 1;
+
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: initializing last_updated to now - %llu microseconds (%0.3Lf). Will not store the next entry.", st->name, st->usec_since_last_update, (long double)ut/1000000.0);
+    }
+
+    // check if we will re-write the entire data set
+    if(unlikely(usec_dt(&st->last_collected_time, &st->last_updated) > st->update_every * st->entries * 1000000ULL)) {
+        info("%s: too old data (last updated at %ld.%ld, last collected at %ld.%ld). Reseting it. Will not store the next entry.", st->name, st->last_updated.tv_sec, st->last_updated.tv_usec, st->last_collected_time.tv_sec, st->last_collected_time.tv_usec);
+        rrdset_reset(st);
+
+        st->usec_since_last_update = st->update_every * 1000000ULL;
+
+        gettimeofday(&st->last_collected_time, NULL);
+
+        unsigned long long ut = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec - st->usec_since_last_update;
+        st->last_updated.tv_sec = (time_t) (ut / 1000000ULL);
+        st->last_updated.tv_usec = (suseconds_t) (ut % 1000000ULL);
+
+        // the first entry should not be stored
+        store_this_entry = 0;
+        first_entry = 1;
+    }
+
+    // these are the 3 variables that will help us in interpolation
+    // last_ut = the last time we added a value to the storage
+    //  now_ut = the time the current value is taken at
+    // next_ut = the time of the next interpolation point
+    last_ut = st->last_updated.tv_sec * 1000000ULL + st->last_updated.tv_usec;
+    now_ut  = st->last_collected_time.tv_sec * 1000000ULL + st->last_collected_time.tv_usec;
+    next_ut = (st->last_updated.tv_sec + st->update_every) * 1000000ULL;
+
+    if(unlikely(!first_entry && now_ut < next_ut)) {
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: THIS IS IN THE SAME INTERPOLATION POINT", st->name);
+    }
+
+    if(unlikely(st->debug)) {
+        debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
+        debug(D_RRD_STATS, "%s: now  ut = %0.3Lf (current update time)", st->name, (long double)now_ut/1000000.0);
+        debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
+    }
+
+    if(unlikely(!st->counter_done)) {
+        store_this_entry = 0;
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s: Will not store the next entry.", st->name);
+    }
+    st->counter_done++;
+
+    // calculate totals and count the dimensions
+    int dimensions;
+    st->collected_total = 0;
+    for( rd = st->dimensions, dimensions = 0 ; likely(rd) ; rd = rd->next, dimensions++ )
+        if(likely(rd->updated)) st->collected_total += rd->collected_value;
+
+    uint32_t storage_flags = SN_EXISTS;
+
+    // process all dimensions to calculate their values
+    // based on the collected figures only
+    // at this stage we do not interpolate anything
+    for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
+
+        if(unlikely(!rd->updated)) {
+            rd->calculated_value = 0;
+            continue;
+        }
+
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: START "
+            " last_collected_value = " COLLECTED_NUMBER_FORMAT
+            " collected_value = " COLLECTED_NUMBER_FORMAT
+            " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+            " calculated_value = " CALCULATED_NUMBER_FORMAT
+            , st->id, rd->name
+            , rd->last_collected_value
+            , rd->collected_value
+            , rd->last_calculated_value
+            , rd->calculated_value
+            );
+
+        switch(rd->algorithm) {
+            case RRDDIM_ABSOLUTE:
+                rd->calculated_value = (calculated_number)rd->collected_value
+                    * (calculated_number)rd->multiplier
+                    / (calculated_number)rd->divisor;
+
+                if(unlikely(st->debug))
+                    debug(D_RRD_STATS, "%s/%s: CALC ABS/ABS-NO-IN "
+                        CALCULATED_NUMBER_FORMAT " = "
+                        COLLECTED_NUMBER_FORMAT
+                        " * " CALCULATED_NUMBER_FORMAT
+                        " / " CALCULATED_NUMBER_FORMAT
+                        , st->id, rd->name
+                        , rd->calculated_value
+                        , rd->collected_value
+                        , (calculated_number)rd->multiplier
+                        , (calculated_number)rd->divisor
+                        );
+                break;
+
+            case RRDDIM_PCENT_OVER_ROW_TOTAL:
+                if(unlikely(!st->collected_total))
+                    rd->calculated_value = 0;
+                else
+                    // the percentage of the current value
+                    // over the total of all dimensions
+                    rd->calculated_value =
+                          (calculated_number)100
+                        * (calculated_number)rd->collected_value
+                        / (calculated_number)st->collected_total;
+
+                if(unlikely(st->debug))
+                    debug(D_RRD_STATS, "%s/%s: CALC PCENT-ROW "
+                        CALCULATED_NUMBER_FORMAT " = 100"
+                        " * " COLLECTED_NUMBER_FORMAT
+                        " / " COLLECTED_NUMBER_FORMAT
+                        , st->id, rd->name
+                        , rd->calculated_value
+                        , rd->collected_value
+                        , st->collected_total
+                        );
+                break;
+
+            case RRDDIM_INCREMENTAL:
+                if(unlikely(rd->counter <= 1)) {
+                    rd->calculated_value = 0;
+                    continue;
+                }
+
+                // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
+                // to reset the calculation (it will give zero as the calculation for this second)
+                if(unlikely(rd->last_collected_value > rd->collected_value)) {
+                    debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
+                            , st->name, rd->name
+                            , rd->last_collected_value
+                            , rd->collected_value);
+                    if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
+                    rd->last_collected_value = rd->collected_value;
+                }
+
+                rd->calculated_value =
+                      (calculated_number)(rd->collected_value - rd->last_collected_value)
+                    * (calculated_number)rd->multiplier
+                    / (calculated_number)rd->divisor;
+
+                if(unlikely(st->debug))
+                    debug(D_RRD_STATS, "%s/%s: CALC INC PRE "
+                        CALCULATED_NUMBER_FORMAT " = ("
+                        COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT
+                        ")"
+                        " * " CALCULATED_NUMBER_FORMAT
+                        " / " CALCULATED_NUMBER_FORMAT
+                        , st->id, rd->name
+                        , rd->calculated_value
+                        , rd->collected_value, rd->last_collected_value
+                        , (calculated_number)rd->multiplier
+                        , (calculated_number)rd->divisor
+                        );
+                break;
+
+            case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+                if(unlikely(rd->counter <= 1)) {
+                    rd->calculated_value = 0;
+                    continue;
+                }
+
+                // if the new is smaller than the old (an overflow, or reset), set the old equal to the new
+                // to reset the calculation (it will give zero as the calculation for this second)
+                if(unlikely(rd->last_collected_value > rd->collected_value)) {
+                    debug(D_RRD_STATS, "%s.%s: RESET or OVERFLOW. Last collected value = " COLLECTED_NUMBER_FORMAT ", current = " COLLECTED_NUMBER_FORMAT
+                    , st->name, rd->name
+                    , rd->last_collected_value
+                    , rd->collected_value);
+                    if(!(rd->flags & RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS)) storage_flags = SN_EXISTS_RESET;
+                    rd->last_collected_value = rd->collected_value;
+                }
+
+                // the percentage of the current increment
+                // over the increment of all dimensions together
+                if(unlikely(st->collected_total == st->last_collected_total))
+                    rd->calculated_value = 0;
+                else
+                    rd->calculated_value =
+                          (calculated_number)100
+                        * (calculated_number)(rd->collected_value - rd->last_collected_value)
+                        / (calculated_number)(st->collected_total - st->last_collected_total);
+
+                if(unlikely(st->debug))
+                    debug(D_RRD_STATS, "%s/%s: CALC PCENT-DIFF "
+                        CALCULATED_NUMBER_FORMAT " = 100"
+                        " * (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
+                        " / (" COLLECTED_NUMBER_FORMAT " - " COLLECTED_NUMBER_FORMAT ")"
+                        , st->id, rd->name
+                        , rd->calculated_value
+                        , rd->collected_value, rd->last_collected_value
+                        , st->collected_total, st->last_collected_total
+                        );
+                break;
+
+            default:
+                // make the default zero, to make sure
+                // it gets noticed when we add new types
+                rd->calculated_value = 0;
+
+                if(unlikely(st->debug))
+                    debug(D_RRD_STATS, "%s/%s: CALC "
+                        CALCULATED_NUMBER_FORMAT " = 0"
+                        , st->id, rd->name
+                        , rd->calculated_value
+                        );
+                break;
+        }
+
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: PHASE2 "
+            " last_collected_value = " COLLECTED_NUMBER_FORMAT
+            " collected_value = " COLLECTED_NUMBER_FORMAT
+            " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+            " calculated_value = " CALCULATED_NUMBER_FORMAT
+            , st->id, rd->name
+            , rd->last_collected_value
+            , rd->collected_value
+            , rd->last_calculated_value
+            , rd->calculated_value
+            );
+
+    }
+
+    // at this point we have all the calculated values ready
+    // it is now time to interpolate values on a second boundary
+
+    unsigned long long first_ut = last_ut;
+    long long iterations = (now_ut - last_ut) / (st->update_every * 1000000ULL);
+    if((now_ut % (st->update_every * 1000000ULL)) == 0) iterations++;
+
+    for( ; likely(next_ut <= now_ut) ; next_ut += st->update_every * 1000000ULL, iterations-- ) {
 #ifdef NETDATA_INTERNAL_CHECKS
-               if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_ut = %llu, next_ut = %llu, now_ut = %llu", st->name, first_ut, last_ut, next_ut, now_ut); }
+        if(iterations < 0) { error("%s: iterations calculation wrapped! first_ut = %llu, last_ut = %llu, next_ut = %llu, now_ut = %llu", st->name, first_ut, last_ut, next_ut, now_ut); }
 #endif
 
-               if(unlikely(st->debug)) {
-                       debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
-                       debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
-               }
-
-               st->last_updated.tv_sec = (time_t) (next_ut / 1000000ULL);
-               st->last_updated.tv_usec = 0;
-
-               for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
-                       calculated_number new_value;
-
-                       switch(rd->algorithm) {
-                               case RRDDIM_INCREMENTAL:
-                                       new_value = (calculated_number)
-                                               (          rd->calculated_value
-                                                       * (calculated_number)(next_ut - last_ut)
-                                                       / (calculated_number)(now_ut - last_ut)
-                                               );
-
-                                       if(unlikely(st->debug))
-                                               debug(D_RRD_STATS, "%s/%s: CALC2 INC "
-                                                       CALCULATED_NUMBER_FORMAT " = "
-                                                       CALCULATED_NUMBER_FORMAT
-                                                       " * %llu"
-                                                       " / %llu"
-                                                       , st->id, rd->name
-                                                       , new_value
-                                                       , rd->calculated_value
-                                                       , (next_ut - last_ut)
-                                                       , (now_ut - last_ut)
-                                                       );
-
-                                       rd->calculated_value -= new_value;
-                                       new_value += rd->last_calculated_value;
-                                       rd->last_calculated_value = 0;
-                                       new_value /= (calculated_number)st->update_every;
-                                       break;
-
-                               case RRDDIM_ABSOLUTE:
-                               case RRDDIM_PCENT_OVER_ROW_TOTAL:
-                               case RRDDIM_PCENT_OVER_DIFF_TOTAL:
-                               default:
-                                       if(iterations == 1) {
-                                               // this is the last iteration
-                                               // do not interpolate
-                                               // just show the calculated value
-
-                                               new_value = rd->calculated_value;
-                                       }
-                                       else {
-                                               // we have missed an update
-                                               // interpolate in the middle values
-
-                                               new_value = (calculated_number)
-                                                       (       (         (rd->calculated_value - rd->last_calculated_value)
-                                                                       * (calculated_number)(next_ut - first_ut)
-                                                                       / (calculated_number)(now_ut - first_ut)
-                                                               )
-                                                               +  rd->last_calculated_value
-                                                       );
-
-                                               if(unlikely(st->debug))
-                                                       debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
-                                                               CALCULATED_NUMBER_FORMAT " = ((("
-                                                               "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
-                                                               " * %llu"
-                                                               " / %llu) + " CALCULATED_NUMBER_FORMAT
-                                                               , st->id, rd->name
-                                                               , new_value
-                                                               , rd->calculated_value, rd->last_calculated_value
-                                                               , (next_ut - first_ut)
-                                                               , (now_ut - first_ut), rd->last_calculated_value
-                                                               );
-
-                                               // this is wrong
-                                               // it fades the value towards the target
-                                               // while we know the calculated value is different
-                                               // if(likely(next_ut + st->update_every * 1000000ULL > now_ut)) rd->calculated_value = new_value;
-                                       }
-                                       break;
-                       }
-
-                       if(unlikely(!store_this_entry)) {
-                               store_this_entry = 1;
-                               continue;
-                       }
-
-                       if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
-                               rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
-
-                               if(unlikely(st->debug))
-                                       debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
-                                               CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
-                                               , st->id, rd->name
-                                               , st->current_entry
-                                               , unpack_storage_number(rd->values[st->current_entry]), new_value
-                                               );
-                       }
-                       else {
-                               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
-                                               , st->id, rd->name
-                                               , st->current_entry
-                                               );
-                               rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
-                       }
-
-                       stored_entries++;
-
-                       if(unlikely(st->debug)) {
-                               calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
-                               calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
-                               calculated_number accuracy = accuracy_loss(t1, t2);
-                               debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
-                                               , st->id, rd->name
-                                               , st->current_entry
-                                               , t2
-                                               , get_storage_number_flags(rd->values[st->current_entry])
-                                               , t1
-                                               , accuracy
-                                               , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
-                                               );
-
-                               rd->collected_volume += t1;
-                               rd->stored_volume += t2;
-                               accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
-                               debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated  = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
-                                               , st->id, rd->name
-                                               , st->current_entry
-                                               , rd->stored_volume
-                                               , rd->collected_volume
-                                               , accuracy
-                                               , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
-                                               );
-
-                       }
-               }
-               // reset the storage flags for the next point, if any;
-               storage_flags = SN_EXISTS;
-
-               st->counter++;
-               st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
-               last_ut = next_ut;
-       }
-
-       // align next interpolation to last collection point
-       if(likely(stored_entries || !store_this_entry)) {
-               st->last_updated.tv_sec = st->last_collected_time.tv_sec;
-               st->last_updated.tv_usec = st->last_collected_time.tv_usec;
-               st->last_collected_total  = st->collected_total;
-       }
-
-       for( rd = st->dimensions; likely(rd) ; rd = rd->next ) {
-               if(unlikely(!rd->updated)) continue;
-
-               if(likely(stored_entries || !store_this_entry)) {
-                       if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
-                       rd->last_collected_value = rd->collected_value;
-
-                       if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
-                       rd->last_calculated_value = rd->calculated_value;
-               }
-
-               rd->calculated_value = 0;
-               rd->collected_value = 0;
-               rd->updated = 0;
-
-               if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: END "
-                       " last_collected_value = " COLLECTED_NUMBER_FORMAT
-                       " collected_value = " COLLECTED_NUMBER_FORMAT
-                       " last_calculated_value = " CALCULATED_NUMBER_FORMAT
-                       " calculated_value = " CALCULATED_NUMBER_FORMAT
-                       , st->id, rd->name
-                       , rd->last_collected_value
-                       , rd->collected_value
-                       , rd->last_calculated_value
-                       , rd->calculated_value
-                       );
-       }
-
-       // ALL DONE ABOUT THE DATA UPDATE
-       // --------------------------------------------------------------------
-
-       // find if there are any obsolete dimensions (not updated recently)
-       if(unlikely(rrd_delete_unupdated_dimensions)) {
-
-               for( rd = st->dimensions; likely(rd) ; rd = rd->next )
-                       if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
-                               break;
-
-               if(unlikely(rd)) {
-                       // there is dimension to free
-                       // upgrade our read lock to a write lock
-                       pthread_rwlock_unlock(&st->rwlock);
-                       pthread_rwlock_wrlock(&st->rwlock);
-
-                       for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
-                               // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
-
-                               if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
-                                       info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
-
-                                       if(unlikely(!last)) {
-                                               st->dimensions = rd->next;
-                                               rd->next = NULL;
-                                               rrddim_free(st, rd);
-                                               rd = st->dimensions;
-                                               continue;
-                                       }
-                                       else {
-                                               last->next = rd->next;
-                                               rd->next = NULL;
-                                               rrddim_free(st, rd);
-                                               rd = last->next;
-                                               continue;
-                                       }
-                               }
-
-                               last = rd;
-                               rd = rd->next;
-                       }
-
-                       if(unlikely(!st->dimensions)) {
-                               info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
-                               st->enabled = 0;
-                       }
-               }
-       }
-
-       pthread_rwlock_unlock(&st->rwlock);
-
-       if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
-               error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
-
-       return(st->usec_since_last_update);
+        if(unlikely(st->debug)) {
+            debug(D_RRD_STATS, "%s: last ut = %0.3Lf (last updated time)", st->name, (long double)last_ut/1000000.0);
+            debug(D_RRD_STATS, "%s: next ut = %0.3Lf (next interpolation point)", st->name, (long double)next_ut/1000000.0);
+        }
+
+        st->last_updated.tv_sec = (time_t) (next_ut / 1000000ULL);
+        st->last_updated.tv_usec = 0;
+
+        for( rd = st->dimensions ; likely(rd) ; rd = rd->next ) {
+            calculated_number new_value;
+
+            switch(rd->algorithm) {
+                case RRDDIM_INCREMENTAL:
+                    new_value = (calculated_number)
+                        (      rd->calculated_value
+                            * (calculated_number)(next_ut - last_ut)
+                            / (calculated_number)(now_ut - last_ut)
+                        );
+
+                    if(unlikely(st->debug))
+                        debug(D_RRD_STATS, "%s/%s: CALC2 INC "
+                            CALCULATED_NUMBER_FORMAT " = "
+                            CALCULATED_NUMBER_FORMAT
+                            " * %llu"
+                            " / %llu"
+                            , st->id, rd->name
+                            , new_value
+                            , rd->calculated_value
+                            , (next_ut - last_ut)
+                            , (now_ut - last_ut)
+                            );
+
+                    rd->calculated_value -= new_value;
+                    new_value += rd->last_calculated_value;
+                    rd->last_calculated_value = 0;
+                    new_value /= (calculated_number)st->update_every;
+                    break;
+
+                case RRDDIM_ABSOLUTE:
+                case RRDDIM_PCENT_OVER_ROW_TOTAL:
+                case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+                default:
+                    if(iterations == 1) {
+                        // this is the last iteration
+                        // do not interpolate
+                        // just show the calculated value
+
+                        new_value = rd->calculated_value;
+                    }
+                    else {
+                        // we have missed an update
+                        // interpolate in the middle values
+
+                        new_value = (calculated_number)
+                            (   (     (rd->calculated_value - rd->last_calculated_value)
+                                    * (calculated_number)(next_ut - first_ut)
+                                    / (calculated_number)(now_ut - first_ut)
+                                )
+                                +  rd->last_calculated_value
+                            );
+
+                        if(unlikely(st->debug))
+                            debug(D_RRD_STATS, "%s/%s: CALC2 DEF "
+                                CALCULATED_NUMBER_FORMAT " = ((("
+                                "(" CALCULATED_NUMBER_FORMAT " - " CALCULATED_NUMBER_FORMAT ")"
+                                " * %llu"
+                                " / %llu) + " CALCULATED_NUMBER_FORMAT
+                                , st->id, rd->name
+                                , new_value
+                                , rd->calculated_value, rd->last_calculated_value
+                                , (next_ut - first_ut)
+                                , (now_ut - first_ut), rd->last_calculated_value
+                                );
+
+                        // this is wrong
+                        // it fades the value towards the target
+                        // while we know the calculated value is different
+                        // if(likely(next_ut + st->update_every * 1000000ULL > now_ut)) rd->calculated_value = new_value;
+                    }
+                    break;
+            }
+
+            if(unlikely(!store_this_entry)) {
+                store_this_entry = 1;
+                continue;
+            }
+
+            if(likely(rd->updated && rd->counter > 1 && iterations < st->gap_when_lost_iterations_above)) {
+                rd->values[st->current_entry] = pack_storage_number(new_value, storage_flags );
+                rd->last_stored_value = new_value;
+
+                if(unlikely(st->debug))
+                    debug(D_RRD_STATS, "%s/%s: STORE[%ld] "
+                        CALCULATED_NUMBER_FORMAT " = " CALCULATED_NUMBER_FORMAT
+                        , st->id, rd->name
+                        , st->current_entry
+                        , unpack_storage_number(rd->values[st->current_entry]), new_value
+                        );
+            }
+            else {
+                if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: STORE[%ld] = NON EXISTING "
+                        , st->id, rd->name
+                        , st->current_entry
+                        );
+                rd->values[st->current_entry] = pack_storage_number(0, SN_NOT_EXISTS);
+                rd->last_stored_value = 0;
+            }
+
+            stored_entries++;
+
+            if(unlikely(st->debug)) {
+                calculated_number t1 = new_value * (calculated_number)rd->multiplier / (calculated_number)rd->divisor;
+                calculated_number t2 = unpack_storage_number(rd->values[st->current_entry]);
+                calculated_number accuracy = accuracy_loss(t1, t2);
+                debug(D_RRD_STATS, "%s/%s: UNPACK[%ld] = " CALCULATED_NUMBER_FORMAT " FLAGS=0x%08x (original = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s)"
+                        , st->id, rd->name
+                        , st->current_entry
+                        , t2
+                        , get_storage_number_flags(rd->values[st->current_entry])
+                        , t1
+                        , accuracy
+                        , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
+                        );
+
+                rd->collected_volume += t1;
+                rd->stored_volume += t2;
+                accuracy = accuracy_loss(rd->collected_volume, rd->stored_volume);
+                debug(D_RRD_STATS, "%s/%s: VOLUME[%ld] = " CALCULATED_NUMBER_FORMAT ", calculated  = " CALCULATED_NUMBER_FORMAT ", accuracy loss = " CALCULATED_NUMBER_FORMAT "%%%s"
+                        , st->id, rd->name
+                        , st->current_entry
+                        , rd->stored_volume
+                        , rd->collected_volume
+                        , accuracy
+                        , (accuracy > ACCURACY_LOSS) ? " **TOO BIG** " : ""
+                        );
+
+            }
+        }
+        // reset the storage flags for the next point, if any;
+        storage_flags = SN_EXISTS;
+
+        st->counter++;
+        st->current_entry = ((st->current_entry + 1) >= st->entries) ? 0 : st->current_entry + 1;
+        last_ut = next_ut;
+    }
+
+    // align next interpolation to last collection point
+    if(likely(stored_entries || !store_this_entry)) {
+        st->last_updated.tv_sec = st->last_collected_time.tv_sec;
+        st->last_updated.tv_usec = st->last_collected_time.tv_usec;
+        st->last_collected_total  = st->collected_total;
+    }
+
+    for( rd = st->dimensions; likely(rd) ; rd = rd->next ) {
+        if(unlikely(!rd->updated)) continue;
+
+        if(likely(stored_entries || !store_this_entry)) {
+            if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_collected_value (old: " COLLECTED_NUMBER_FORMAT ") to last_collected_value (new: " COLLECTED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_collected_value, rd->collected_value);
+            rd->last_collected_value = rd->collected_value;
+
+            if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: setting last_calculated_value (old: " CALCULATED_NUMBER_FORMAT ") to last_calculated_value (new: " CALCULATED_NUMBER_FORMAT ")", st->id, rd->name, rd->last_calculated_value, rd->calculated_value);
+            rd->last_calculated_value = rd->calculated_value;
+        }
+
+        rd->calculated_value = 0;
+        rd->collected_value = 0;
+        rd->updated = 0;
+
+        if(unlikely(st->debug)) debug(D_RRD_STATS, "%s/%s: END "
+            " last_collected_value = " COLLECTED_NUMBER_FORMAT
+            " collected_value = " COLLECTED_NUMBER_FORMAT
+            " last_calculated_value = " CALCULATED_NUMBER_FORMAT
+            " calculated_value = " CALCULATED_NUMBER_FORMAT
+            , st->id, rd->name
+            , rd->last_collected_value
+            , rd->collected_value
+            , rd->last_calculated_value
+            , rd->calculated_value
+            );
+    }
+
+    // ALL DONE ABOUT THE DATA UPDATE
+    // --------------------------------------------------------------------
+
+    // find if there are any obsolete dimensions (not updated recently)
+    if(unlikely(rrd_delete_unupdated_dimensions)) {
+
+        for( rd = st->dimensions; likely(rd) ; rd = rd->next )
+            if((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)
+                break;
+
+        if(unlikely(rd)) {
+            // there is dimension to free
+            // upgrade our read lock to a write lock
+            pthread_rwlock_unlock(&st->rwlock);
+            pthread_rwlock_wrlock(&st->rwlock);
+
+            for( rd = st->dimensions, last = NULL ; likely(rd) ; ) {
+                // remove it only it is not updated in rrd_delete_unupdated_dimensions seconds
+
+                if(unlikely((rd->last_collected_time.tv_sec + (rrd_delete_unupdated_dimensions * st->update_every)) < st->last_collected_time.tv_sec)) {
+                    info("Removing obsolete dimension '%s' (%s) of '%s' (%s).", rd->name, rd->id, st->name, st->id);
+
+                    if(unlikely(!last)) {
+                        st->dimensions = rd->next;
+                        rd->next = NULL;
+                        rrddim_free(st, rd);
+                        rd = st->dimensions;
+                        continue;
+                    }
+                    else {
+                        last->next = rd->next;
+                        rd->next = NULL;
+                        rrddim_free(st, rd);
+                        rd = last->next;
+                        continue;
+                    }
+                }
+
+                last = rd;
+                rd = rd->next;
+            }
+
+            if(unlikely(!st->dimensions)) {
+                info("Disabling chart %s (%s) since it does not have any dimensions", st->name, st->id);
+                st->enabled = 0;
+            }
+        }
+    }
+
+    pthread_rwlock_unlock(&st->rwlock);
+
+    if(unlikely(pthread_setcancelstate(oldstate, NULL) != 0))
+        error("Cannot set pthread cancel state to RESTORE (%d).", oldstate);
+
+    return(st->usec_since_last_update);
 }
index 83ab35b69938cebbfebb0316811a58ff2a65e50f..70fe5d3cae6d593755556d2bc0c06b9083f76a29 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -1,10 +1,3 @@
-#include <sys/time.h>
-#include <pthread.h>
-#include <stdint.h>
-
-#include "avl.h"
-#include "storage_number.h"
-
 #ifndef NETDATA_RRD_H
 #define NETDATA_RRD_H 1
 
@@ -22,8 +15,8 @@ extern int rrd_delete_unupdated_dimensions;
 
 #define RRD_ID_LENGTH_MAX 1024
 
-#define RRDSET_MAGIC           "NETDATA RRD SET FILE V017"
-#define RRDDIMENSION_MAGIC     "NETDATA RRD DIMENSION FILE V017"
+#define RRDSET_MAGIC        "NETDATA RRD SET FILE V018"
+#define RRDDIMENSION_MAGIC  "NETDATA RRD DIMENSION FILE V018"
 
 typedef long long total_number;
 #define TOTAL_NUMBER_FORMAT "%lld"
@@ -35,8 +28,8 @@ typedef long long total_number;
 #define RRDSET_TYPE_AREA_NAME "area"
 #define RRDSET_TYPE_STACKED_NAME "stacked"
 
-#define RRDSET_TYPE_LINE       0
-#define RRDSET_TYPE_AREA       1
+#define RRDSET_TYPE_LINE    0
+#define RRDSET_TYPE_AREA    1
 #define RRDSET_TYPE_STACKED 2
 
 int rrdset_type_id(const char *name);
@@ -63,15 +56,15 @@ extern int rrd_memory_mode_id(const char *name);
 // ----------------------------------------------------------------------------
 // algorithms types
 
-#define RRDDIM_ABSOLUTE_NAME                           "absolute"
-#define RRDDIM_INCREMENTAL_NAME                        "incremental"
-#define RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME      "percentage-of-incremental-row"
-#define RRDDIM_PCENT_OVER_ROW_TOTAL_NAME       "percentage-of-absolute-row"
+#define RRDDIM_ABSOLUTE_NAME                "absolute"
+#define RRDDIM_INCREMENTAL_NAME             "incremental"
+#define RRDDIM_PCENT_OVER_DIFF_TOTAL_NAME   "percentage-of-incremental-row"
+#define RRDDIM_PCENT_OVER_ROW_TOTAL_NAME    "percentage-of-absolute-row"
 
-#define RRDDIM_ABSOLUTE                                        0
-#define RRDDIM_INCREMENTAL                             1
-#define RRDDIM_PCENT_OVER_DIFF_TOTAL   2
-#define RRDDIM_PCENT_OVER_ROW_TOTAL            3
+#define RRDDIM_ABSOLUTE                 0
+#define RRDDIM_INCREMENTAL              1
+#define RRDDIM_PCENT_OVER_DIFF_TOTAL    2
+#define RRDDIM_PCENT_OVER_ROW_TOTAL     3
 
 extern int rrddim_algorithm_id(const char *name);
 extern const char *rrddim_algorithm_name(int chart_type);
@@ -82,84 +75,104 @@ extern const char *rrddim_algorithm_name(int chart_type);
 #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
 
+// ----------------------------------------------------------------------------
+// RRD CONTEXT
+
+struct rrdcontext {
+    avl avl;
+
+    const char *id;
+    uint32_t hash;
+
+    size_t use_count;
+
+    avl_tree_lock variables_root_index;
+};
+typedef struct rrdcontext RRDCONTEXT;
+
 // ----------------------------------------------------------------------------
 // RRD DIMENSION
 
 struct rrddim {
-       // ------------------------------------------------------------------------
-       // binary indexing structures
+    // ------------------------------------------------------------------------
+    // binary indexing structures
+
+    avl avl;                                        // the binary index - this has to be first member!
 
-       avl avl;                                                                                // the binary index - this has to be first member!
+    // ------------------------------------------------------------------------
+    // the dimension definition
 
-       // ------------------------------------------------------------------------
-       // the dimension definition
+    char id[RRD_ID_LENGTH_MAX + 1];                 // the id of this dimension (for internal identification)
 
-       char id[RRD_ID_LENGTH_MAX + 1];                                 // 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)
 
-       const char *name;                                                               // the name of this dimension (as presented to user)
-                                                                                                       // this is a pointer to the config structure
-                                                                                                       // since the config always has a higher priority
-                                                                                                       // (the user overwrites the name of the charts)
+    int 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
 
-       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
+    int mapped;                                     // if set to non zero, this dimension is mapped to a file
 
-       int mapped;                                                                             // if set to non zero, this dimension is mapped to a file
+    // ------------------------------------------------------------------------
+    // members for temporary data we need for calculations
 
-       // ------------------------------------------------------------------------
-       // members for temporary data we need for calculations
+    uint32_t hash;                                  // a simple hash of the id, to speed up searching / indexing
+                                                    // instead of strcmp() every item in the binary index
+                                                    // we first compare the hashes
 
-       uint32_t hash;                                                                  // a simple hash of the id, to speed up searching / indexing
-                                                                                                       // instead of strcmp() every item in the binary index
-                                                                                                       // we first compare the hashes
+    // FIXME
+    // we need the hash_name too!
 
-       // FIXME
-       // we need the hash_name too!
+    uint32_t flags;
 
-       uint32_t flags;
+    char cache_filename[FILENAME_MAX+1];            // the filename we load/save from/to this set
 
-       char cache_filename[FILENAME_MAX+1];                    // the filename we load/save from/to this set
+    unsigned long counter;                          // the number of times we added values to this rrdim
 
-       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;                                                                    // set to 0 after each calculation, to 1 after each collected value
-                                                                                                       // we use this to detect that a dimension is not updated
+    struct timeval last_collected_time;             // when was this dimension last updated
+                                                    // this is actual date time we updated the last_collected_value
+                                                    // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET
 
-       struct timeval last_collected_time;                             // when was this dimension last updated
-                                                                                                       // this is actual date time we updated the last_collected_value
-                                                                                                       // THIS IS DIFFERENT FROM THE SAME MEMBER OF RRDSET
+    calculated_number calculated_value;             // the current calculated value, after applying the algorithm - resets to zero after being used
+    calculated_number last_calculated_value;        // the last calculated value processed
 
-       calculated_number calculated_value;                             // the current calculated value, after applying the algorithm
-       calculated_number last_calculated_value;                // the last calculated value
+    calculated_number last_stored_value;            // the last value as stored in the database (after interpolation)
 
-       collected_number collected_value;                               // the current value, as collected
-       collected_number last_collected_value;                  // the last value that was collected
+    collected_number collected_value;               // the current value, as collected - resets to 0 after being used
+    collected_number last_collected_value;          // the last value that was collected, after being processed
 
-       // the *_volume members are used to calculate the accuracy of the rounding done by the
-       // storage number - they are printed to debug.log when debug is enabled for a set.
-       calculated_number collected_volume;                             // the sum of all collected values so far
-       calculated_number stored_volume;                                // the sum of all stored values so far
+    // the *_volume members are used to calculate the accuracy of the rounding done by the
+    // storage number - they are printed to debug.log when debug is enabled for a set.
+    calculated_number collected_volume;             // the sum of all collected values so far
+    calculated_number stored_volume;                // the sum of all stored values so far
 
-       struct rrddim *next;                                                    // linking of dimensions within the same data set
+    struct rrddim *next;                            // linking of dimensions within the same data set
+    struct rrdset *rrdset;
 
-       // ------------------------------------------------------------------------
-       // members for checking the data when loading from disk
+    // ------------------------------------------------------------------------
+    // members for checking the data when loading from disk
 
-       long entries;                                                                   // how many entries this dimension has in ram
-                                                                                                       // this is the same to the entries of the data set
-                                                                                                       // we set it here, to check the data when we load it from disk.
+    long entries;                                   // how many entries this dimension has in ram
+                                                    // this is the same to the entries of the data set
+                                                    // we set it here, to check the data when we load it from disk.
 
-       int update_every;                                                               // every how many seconds is this updated
+    int update_every;                               // every how many seconds is this updated
 
-       unsigned long memsize;                                                  // the memory allocated for this dimension
+    unsigned long memsize;                          // the memory allocated for this dimension
 
-       char magic[sizeof(RRDDIMENSION_MAGIC) + 1];             // a string to be saved, used to identify our data file
+    char magic[sizeof(RRDDIMENSION_MAGIC) + 1];     // a string to be saved, used to identify our data file
 
-       // ------------------------------------------------------------------------
-       // the values stored in this dimension, using our floating point numbers
+    struct rrddimvar *variables;
 
-       storage_number values[];                                                // the array of values - THIS HAS TO BE THE LAST MEMBER
+    // ------------------------------------------------------------------------
+    // the values stored in this dimension, using our floating point numbers
+
+    storage_number values[];                        // the array of values - THIS HAS TO BE THE LAST MEMBER
 };
 typedef struct rrddim RRDDIM;
 
@@ -168,94 +181,149 @@ typedef struct rrddim RRDDIM;
 // RRDSET
 
 struct rrdset {
-       // ------------------------------------------------------------------------
-       // binary indexing structures
+    // ------------------------------------------------------------------------
+    // binary indexing structures
+
+    avl avl;                                        // the index, with key the id - this has to be first!
+    avl avlname;                                    // the index, with key the name
+
+    // ------------------------------------------------------------------------
+    // the set configuration
+
+    char id[RRD_ID_LENGTH_MAX + 1];                 // id of the data set
+
+    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)
 
-       avl avl;                                                                                // the index, with key the id - this has to be first!
-       avl avlname;                                                                    // the index, with key the name
+    char *type;                                     // the type of graph RRD_TYPE_* (a category, for determining graphing options)
+    char *family;                                   // grouping sets under the same family
+    char *title;                                    // title shown to user
+    char *units;                                    // units of measurement
 
-       // ------------------------------------------------------------------------
-       // the set configuration
+    char *context;                                  // the template of this data set
+    uint32_t hash_context;
 
-       char id[RRD_ID_LENGTH_MAX + 1];                                 // id of the data set
+    int chart_type;
 
-       const char *name;                                                               // the name of this dimension (as presented to user)
-                                                                                                       // this is a pointer to the config structure
-                                                                                                       // since the config always has a higher priority
-                                                                                                       // (the user overwrites the name of the charts)
+    int update_every;                               // every how many seconds is this updated?
 
-       char *type;                                                                             // the type of graph RRD_TYPE_* (a category, for determining graphing options)
-       char *family;                                                                   // grouping sets under the same family
-       char *context;                                                                  // the template of this data set
-       char *title;                                                                    // title shown to user
-       char *units;                                                                    // units of measurement
+    long entries;                                   // total number of entries in the data set
 
-       int chart_type;
+    long current_entry;                             // the entry that is currently being updated
+                                                    // it goes around in a round-robin fashion
 
-       int update_every;                                                               // every how many seconds is this updated?
+    int enabled;
 
-       long entries;                                                                   // total number of entries in the data set
+    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 current_entry;                                                             // the entry that is currently being updated
-                                                                                                       // it goes around in a round-robin fashion
+    long priority;
 
-       int enabled;
+    int isdetail;                                   // if set, the data set should be considered as a detail of another
+                                                    // (the master data set should be the one that has the same family and is not detail)
 
-       int gap_when_lost_iterations_above;                             // after how many lost iterations a gap should be stored
-                                                                                                       // netdata will interpolate values for gaps lower than this
+    // ------------------------------------------------------------------------
+    // members for temporary data we need for calculations
 
-       long priority;
+    int mapped;                                     // if set to 1, this is memory mapped
 
-       int isdetail;                                                                   // if set, the data set should be considered as a detail of another
-                                                                                                       // (the master data set should be the one that has the same family and is not detail)
+    int debug;
 
-       // ------------------------------------------------------------------------
-       // members for temporary data we need for calculations
+    char *cache_dir;                                // the directory to store dimensions
+    char cache_filename[FILENAME_MAX+1];            // the filename to store this set
 
-       int mapped;                                                                             // if set to 1, this is memory mapped
+    pthread_rwlock_t rwlock;
 
-       int debug;
+    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
 
-       char *cache_dir;                                                                // the directory to store dimensions
-       char cache_filename[FILENAME_MAX+1];                    // the filename to store this set
+    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
 
-       pthread_rwlock_t rwlock;
+    uint32_t hash_name;                             // a simple hash on the name
 
-       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
+    unsigned long long usec_since_last_update;      // the time in microseconds since the last collection of data
 
-       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
+    struct timeval last_updated;                    // when this data set was last updated (updated every time the rrd_stats_done() function)
+    struct timeval last_collected_time;             // when did this data set last collected values
 
-       uint32_t hash_name;                                                             // a simple hash on the name
+    total_number collected_total;                   // used internally to calculate percentages
+    total_number last_collected_total;              // used internally to calculate percentages
 
-       unsigned long long usec_since_last_update;              // the time in microseconds since the last collection of data
+    RRDCONTEXT *rrdcontext;
+    struct rrdhost *rrdhost;
 
-       struct timeval last_updated;                                    // when this data set was last updated (updated every time the rrd_stats_done() function)
-       struct timeval last_collected_time;                             // when did this data set last collected values
+    struct rrdset *next;                            // linking of rrdsets
 
-       total_number collected_total;                                   // used internally to calculate percentages
-       total_number last_collected_total;                              // used internally to calculate percentages
+    // ------------------------------------------------------------------------
+    // local variables
 
-       struct rrdset *next;                                                    // linking of rrdsets
+    calculated_number green;
+    calculated_number red;
 
-       // ------------------------------------------------------------------------
-       // members for checking the data when loading from disk
+    avl_tree_lock variables_root_index;
+    RRDSETVAR *variables;
+    RRDCALC *calculations;
 
-       unsigned long memsize;                                                  // how much mem we have allocated for this (without dimensions)
+    // ------------------------------------------------------------------------
+    // members for checking the data when loading from disk
 
-       char magic[sizeof(RRDSET_MAGIC) + 1];                   // our magic
+    unsigned long memsize;                          // how much mem we have allocated for this (without dimensions)
 
-       // ------------------------------------------------------------------------
-       // the dimensions
+    char magic[sizeof(RRDSET_MAGIC) + 1];           // our magic
+
+    // ------------------------------------------------------------------------
+    // the dimensions
+
+    avl_tree_lock dimensions_index;                     // the root of the dimensions index
+    RRDDIM *dimensions;                             // the actual data for every dimension
 
-       avl_tree_lock dimensions_index;                                         // the root of the dimensions index
-       RRDDIM *dimensions;                                                             // the actual data for every dimension
 };
 typedef struct rrdset RRDSET;
 
-extern RRDSET *rrdset_root;
-extern pthread_rwlock_t rrdset_root_rwlock;
+// ----------------------------------------------------------------------------
+// RRD HOST
+
+struct rrdhost {
+    avl avl;
+
+    char *hostname;
+
+    RRDSET *rrdset_root;
+    pthread_rwlock_t rrdset_root_rwlock;
+
+    avl_tree_lock rrdset_root_index;
+    avl_tree_lock rrdset_root_index_name;
+
+    avl_tree_lock rrdcontext_root_index;
+    avl_tree_lock variables_root_index;
+
+    // all RRDCALCs are primarily allocated and linked here
+    // RRDCALCs may be linked to charts at any point
+    // (charts may or may not exist when these are loaded)
+    RRDCALC *calculations;
+
+    RRDCALCTEMPLATE *templates;
+};
+typedef struct rrdhost RRDHOST;
+extern RRDHOST localhost;
+
+#ifdef NETDATA_INTERNAL_CHECKS
+#define rrdhost_check_wrlock(host) rrdhost_check_wrlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+#define rrdhost_check_rdlock(host) rrdhost_check_rdlock_int(host, __FILE__, __FUNCTION__, __LINE__)
+#else
+#define rrdhost_check_rdlock(host) (void)0
+#define rrdhost_check_wrlock(host) (void)0
+#endif
+
+extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+extern void rrdhost_check_rdlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
+
+extern void rrdhost_rwlock(RRDHOST *host);
+extern void rrdhost_rdlock(RRDHOST *host);
+extern void rrdhost_unlock(RRDHOST *host);
 
 // ----------------------------------------------------------------------------
 // RRD SET functions
@@ -268,15 +336,15 @@ extern char *rrdset_cache_dir(const char *id);
 extern void rrdset_reset(RRDSET *st);
 
 extern RRDSET *rrdset_create(const char *type
-               , const char *id
-               , const char *name
-               , const char *family
-               , const char *context
-               , const char *title
-               , const char *units
-               , long priority
-               , int update_every
-               , int chart_type);
+        , const char *id
+        , const char *name
+        , const char *family
+        , const char *context
+        , const char *title
+        , const char *units
+        , long priority
+        , int update_every
+        , int chart_type);
 
 extern void rrdset_free_all(void);
 extern void rrdset_save_all(void);
@@ -309,20 +377,20 @@ extern unsigned long long rrdset_done(RRDSET *st);
 // get the slot of the round robin database, for the given timestamp (t)
 // it always returns a valid slot, although may not be for the time requested if the time is outside the round robin database
 #define rrdset_time2slot(st, t) ( \
-               (  (time_t)(t) >= rrdset_last_entry_t(st))  ? ( rrdset_last_slot(st) ) : \
-               ( ((time_t)(t) <= rrdset_first_entry_t(st)) ?   rrdset_first_slot(st) : \
-               ( (rrdset_last_slot(st) >= (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) ? \
-                 (rrdset_last_slot(st) -  (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) : \
-                 (rrdset_last_slot(st) -  (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) + (unsigned long)(st)->entries ) \
-               )))
+        (  (time_t)(t) >= rrdset_last_entry_t(st))  ? ( rrdset_last_slot(st) ) : \
+        ( ((time_t)(t) <= rrdset_first_entry_t(st)) ?   rrdset_first_slot(st) : \
+        ( (rrdset_last_slot(st) >= (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) ? \
+          (rrdset_last_slot(st) -  (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) : \
+          (rrdset_last_slot(st) -  (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) + (unsigned long)(st)->entries ) \
+        )))
 
 // get the timestamp of a specific slot in the round robin database
 #define rrdset_slot2time(st, slot) ( rrdset_last_entry_t(st) - \
-               ((unsigned long)(st)->update_every * ( \
-                               ( (unsigned long)(slot) > rrdset_last_slot(st)) ? \
-                               ( (rrdset_last_slot(st) - (unsigned long)(slot) + (unsigned long)(st)->entries) ) : \
-                               ( (rrdset_last_slot(st) - (unsigned long)(slot)) )) \
-               ))
+        ((unsigned long)(st)->update_every * ( \
+                ( (unsigned long)(slot) > rrdset_last_slot(st)) ? \
+                ( (rrdset_last_slot(st) - (unsigned long)(slot) + (unsigned long)(st)->entries) ) : \
+                ( (rrdset_last_slot(st) - (unsigned long)(slot)) )) \
+        ))
 
 // ----------------------------------------------------------------------------
 // RRD DIMENSION functions
index 0afb4531bc2e2e383603edab0f878c9e29450891..cb7d333babbfb43e7daed76feb5a4477289857e0 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <math.h>
-
-#include "log.h"
 #include "common.h"
-#include "rrd2json.h"
 
 #define HOSTNAME_MAX 1024
 char *hostname = "unknown";
 
 void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
 {
-       pthread_rwlock_rdlock(&st->rwlock);
-
-       buffer_sprintf(wb,
-               "\t\t{\n"
-               "\t\t\t\"id\": \"%s\",\n"
-               "\t\t\t\"name\": \"%s\",\n"
-               "\t\t\t\"type\": \"%s\",\n"
-               "\t\t\t\"family\": \"%s\",\n"
-               "\t\t\t\"context\": \"%s\",\n"
-               "\t\t\t\"title\": \"%s\",\n"
-               "\t\t\t\"priority\": %ld,\n"
-               "\t\t\t\"enabled\": %s,\n"
-               "\t\t\t\"units\": \"%s\",\n"
-               "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
-               "\t\t\t\"chart_type\": \"%s\",\n"
-               "\t\t\t\"duration\": %ld,\n"
-               "\t\t\t\"first_entry\": %ld,\n"
-               "\t\t\t\"last_entry\": %ld,\n"
-               "\t\t\t\"update_every\": %d,\n"
-               "\t\t\t\"dimensions\": {\n"
-               , st->id
-               , st->name
-               , st->type
-               , st->family
-               , st->context
-               , st->title
-               , st->priority
-               , st->enabled?"true":"false"
-               , st->units
-               , st->name
-               , rrdset_type_name(st->chart_type)
-               , st->entries * st->update_every
-               , rrdset_first_entry_t(st)
-               , rrdset_last_entry_t(st)
-               , st->update_every
-               );
-
-       unsigned long memory = st->memsize;
-
-       int c = 0;
-       RRDDIM *rd;
-       for(rd = st->dimensions; rd ; rd = rd->next) {
-               if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
-
-               memory += rd->memsize;
-
-               buffer_sprintf(wb,
-                       "%s"
-                       "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
-                       , c?",\n":""
-                       , rd->id
-                       , rd->name
-                       );
-
-               c++;
-       }
-
-       buffer_sprintf(wb,
-               "\n\t\t\t}\n"
-               "\t\t}"
-               );
-
-       pthread_rwlock_unlock(&st->rwlock);
+    pthread_rwlock_rdlock(&st->rwlock);
+
+    buffer_sprintf(wb,
+        "\t\t{\n"
+        "\t\t\t\"id\": \"%s\",\n"
+        "\t\t\t\"name\": \"%s\",\n"
+        "\t\t\t\"type\": \"%s\",\n"
+        "\t\t\t\"family\": \"%s\",\n"
+        "\t\t\t\"context\": \"%s\",\n"
+        "\t\t\t\"title\": \"%s\",\n"
+        "\t\t\t\"priority\": %ld,\n"
+        "\t\t\t\"enabled\": %s,\n"
+        "\t\t\t\"units\": \"%s\",\n"
+        "\t\t\t\"data_url\": \"/api/v1/data?chart=%s\",\n"
+        "\t\t\t\"chart_type\": \"%s\",\n"
+        "\t\t\t\"duration\": %ld,\n"
+        "\t\t\t\"first_entry\": %ld,\n"
+        "\t\t\t\"last_entry\": %ld,\n"
+        "\t\t\t\"update_every\": %d,\n"
+        "\t\t\t\"dimensions\": {\n"
+        , st->id
+        , st->name
+        , st->type
+        , st->family
+        , st->context
+        , st->title
+        , st->priority
+        , st->enabled?"true":"false"
+        , st->units
+        , st->name
+        , rrdset_type_name(st->chart_type)
+        , st->entries * st->update_every
+        , rrdset_first_entry_t(st)
+        , rrdset_last_entry_t(st)
+        , st->update_every
+        );
+
+    unsigned long memory = st->memsize;
+
+    int c = 0;
+    RRDDIM *rd;
+    for(rd = st->dimensions; rd ; rd = rd->next) {
+        if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
+
+        memory += rd->memsize;
+
+        buffer_sprintf(wb,
+            "%s"
+            "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
+            , c?",\n":""
+            , rd->id
+            , rd->name
+            );
+
+        c++;
+    }
+
+    buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
+    buffer_rrd_value(wb, st->green);
+    buffer_strcat(wb, ",\n\t\t\t\"red\": ");
+    buffer_rrd_value(wb, st->red);
+
+    buffer_sprintf(wb,
+        "\n\t\t}"
+        );
+
+    pthread_rwlock_unlock(&st->rwlock);
 }
 
 void rrd_stats_api_v1_charts(BUFFER *wb)
 {
-       long c;
-       RRDSET *st;
-
-       buffer_sprintf(wb, "{\n"
-                  "\t\"hostname\": \"%s\""
-               ",\n\t\"update_every\": %d"
-               ",\n\t\"history\": %d"
-               ",\n\t\"charts\": {"
-               , hostname
-               , rrd_update_every
-               , rrd_default_history_entries
-               );
-
-       pthread_rwlock_rdlock(&rrdset_root_rwlock);
-       for(st = rrdset_root, c = 0; st ; st = st->next) {
-               if(st->enabled && st->dimensions) {
-                       if(c) buffer_strcat(wb, ",");
-                       buffer_strcat(wb, "\n\t\t\"");
-                       buffer_strcat(wb, st->id);
-                       buffer_strcat(wb, "\": ");
-                       rrd_stats_api_v1_chart(st, wb);
-                       c++;
-               }
-       }
-       pthread_rwlock_unlock(&rrdset_root_rwlock);
-
-       buffer_strcat(wb, "\n\t}\n}\n");
+    long c;
+    RRDSET *st;
+
+    buffer_sprintf(wb, "{\n"
+           "\t\"hostname\": \"%s\""
+        ",\n\t\"update_every\": %d"
+        ",\n\t\"history\": %d"
+        ",\n\t\"charts\": {"
+        , hostname
+        , rrd_update_every
+        , rrd_default_history_entries
+        );
+
+    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+    for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
+        if(st->enabled && st->dimensions) {
+            if(c) buffer_strcat(wb, ",");
+            buffer_strcat(wb, "\n\t\t\"");
+            buffer_strcat(wb, st->id);
+            buffer_strcat(wb, "\": ");
+            rrd_stats_api_v1_chart(st, wb);
+            c++;
+        }
+    }
+    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+
+    buffer_strcat(wb, "\n\t}\n}\n");
 }
 
 
 unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
 {
-       time_t now = time(NULL);
-
-       pthread_rwlock_rdlock(&st->rwlock);
-
-       buffer_sprintf(wb,
-               "\t\t{\n"
-               "\t\t\t\"id\": \"%s\",\n"
-               "\t\t\t\"name\": \"%s\",\n"
-               "\t\t\t\"type\": \"%s\",\n"
-               "\t\t\t\"family\": \"%s\",\n"
-               "\t\t\t\"context\": \"%s\",\n"
-               "\t\t\t\"title\": \"%s\",\n"
-               "\t\t\t\"priority\": %ld,\n"
-               "\t\t\t\"enabled\": %d,\n"
-               "\t\t\t\"units\": \"%s\",\n"
-               "\t\t\t\"url\": \"/data/%s/%s\",\n"
-               "\t\t\t\"chart_type\": \"%s\",\n"
-               "\t\t\t\"counter\": %lu,\n"
-               "\t\t\t\"entries\": %ld,\n"
-               "\t\t\t\"first_entry_t\": %ld,\n"
-               "\t\t\t\"last_entry\": %lu,\n"
-               "\t\t\t\"last_entry_t\": %ld,\n"
-               "\t\t\t\"last_entry_secs_ago\": %ld,\n"
-               "\t\t\t\"update_every\": %d,\n"
-               "\t\t\t\"isdetail\": %d,\n"
-               "\t\t\t\"usec_since_last_update\": %llu,\n"
-               "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
-               "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
-               "\t\t\t\"dimensions\": [\n"
-               , st->id
-               , st->name
-               , st->type
-               , st->family
-               , st->context
-               , st->title
-               , st->priority
-               , st->enabled
-               , st->units
-               , st->name, options?options:""
-               , rrdset_type_name(st->chart_type)
-               , st->counter
-               , st->entries
-               , rrdset_first_entry_t(st)
-               , rrdset_last_slot(st)
-               , rrdset_last_entry_t(st)
-               , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
-               , st->update_every
-               , st->isdetail
-               , st->usec_since_last_update
-               , st->collected_total
-               , st->last_collected_total
-               );
-
-       unsigned long memory = st->memsize;
-
-       RRDDIM *rd;
-       for(rd = st->dimensions; rd ; rd = rd->next) {
-
-               memory += rd->memsize;
-
-               buffer_sprintf(wb,
-                       "\t\t\t\t{\n"
-                       "\t\t\t\t\t\"id\": \"%s\",\n"
-                       "\t\t\t\t\t\"name\": \"%s\",\n"
-                       "\t\t\t\t\t\"entries\": %ld,\n"
-                       "\t\t\t\t\t\"isHidden\": %d,\n"
-                       "\t\t\t\t\t\"algorithm\": \"%s\",\n"
-                       "\t\t\t\t\t\"multiplier\": %ld,\n"
-                       "\t\t\t\t\t\"divisor\": %ld,\n"
-                       "\t\t\t\t\t\"last_entry_t\": %ld,\n"
-                       "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
-                       "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
-                       "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
-                       "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
-                       "\t\t\t\t\t\"memory\": %lu\n"
-                       "\t\t\t\t}%s\n"
-                       , rd->id
-                       , rd->name
-                       , rd->entries
-                       , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
-                       , rrddim_algorithm_name(rd->algorithm)
-                       , rd->multiplier
-                       , rd->divisor
-                       , rd->last_collected_time.tv_sec
-                       , rd->collected_value
-                       , rd->calculated_value
-                       , rd->last_collected_value
-                       , rd->last_calculated_value
-                       , rd->memsize
-                       , rd->next?",":""
-                       );
-       }
-
-       buffer_sprintf(wb,
-               "\t\t\t],\n"
-               "\t\t\t\"memory\" : %lu\n"
-               "\t\t}"
-               , memory
-               );
-
-       pthread_rwlock_unlock(&st->rwlock);
-       return memory;
+    time_t now = time(NULL);
+
+    pthread_rwlock_rdlock(&st->rwlock);
+
+    buffer_sprintf(wb,
+        "\t\t{\n"
+        "\t\t\t\"id\": \"%s\",\n"
+        "\t\t\t\"name\": \"%s\",\n"
+        "\t\t\t\"type\": \"%s\",\n"
+        "\t\t\t\"family\": \"%s\",\n"
+        "\t\t\t\"context\": \"%s\",\n"
+        "\t\t\t\"title\": \"%s\",\n"
+        "\t\t\t\"priority\": %ld,\n"
+        "\t\t\t\"enabled\": %d,\n"
+        "\t\t\t\"units\": \"%s\",\n"
+        "\t\t\t\"url\": \"/data/%s/%s\",\n"
+        "\t\t\t\"chart_type\": \"%s\",\n"
+        "\t\t\t\"counter\": %lu,\n"
+        "\t\t\t\"entries\": %ld,\n"
+        "\t\t\t\"first_entry_t\": %ld,\n"
+        "\t\t\t\"last_entry\": %lu,\n"
+        "\t\t\t\"last_entry_t\": %ld,\n"
+        "\t\t\t\"last_entry_secs_ago\": %ld,\n"
+        "\t\t\t\"update_every\": %d,\n"
+        "\t\t\t\"isdetail\": %d,\n"
+        "\t\t\t\"usec_since_last_update\": %llu,\n"
+        "\t\t\t\"collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
+        "\t\t\t\"last_collected_total\": " TOTAL_NUMBER_FORMAT ",\n"
+        "\t\t\t\"dimensions\": [\n"
+        , st->id
+        , st->name
+        , st->type
+        , st->family
+        , st->context
+        , st->title
+        , st->priority
+        , st->enabled
+        , st->units
+        , st->name, options?options:""
+        , rrdset_type_name(st->chart_type)
+        , st->counter
+        , st->entries
+        , rrdset_first_entry_t(st)
+        , rrdset_last_slot(st)
+        , rrdset_last_entry_t(st)
+        , (now < rrdset_last_entry_t(st)) ? (time_t)0 : now - rrdset_last_entry_t(st)
+        , st->update_every
+        , st->isdetail
+        , st->usec_since_last_update
+        , st->collected_total
+        , st->last_collected_total
+        );
+
+    unsigned long memory = st->memsize;
+
+    RRDDIM *rd;
+    for(rd = st->dimensions; rd ; rd = rd->next) {
+
+        memory += rd->memsize;
+
+        buffer_sprintf(wb,
+            "\t\t\t\t{\n"
+            "\t\t\t\t\t\"id\": \"%s\",\n"
+            "\t\t\t\t\t\"name\": \"%s\",\n"
+            "\t\t\t\t\t\"entries\": %ld,\n"
+            "\t\t\t\t\t\"isHidden\": %d,\n"
+            "\t\t\t\t\t\"algorithm\": \"%s\",\n"
+            "\t\t\t\t\t\"multiplier\": %ld,\n"
+            "\t\t\t\t\t\"divisor\": %ld,\n"
+            "\t\t\t\t\t\"last_entry_t\": %ld,\n"
+            "\t\t\t\t\t\"collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
+            "\t\t\t\t\t\"calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
+            "\t\t\t\t\t\"last_collected_value\": " COLLECTED_NUMBER_FORMAT ",\n"
+            "\t\t\t\t\t\"last_calculated_value\": " CALCULATED_NUMBER_FORMAT ",\n"
+            "\t\t\t\t\t\"memory\": %lu\n"
+            "\t\t\t\t}%s\n"
+            , rd->id
+            , rd->name
+            , rd->entries
+            , (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0
+            , rrddim_algorithm_name(rd->algorithm)
+            , rd->multiplier
+            , rd->divisor
+            , rd->last_collected_time.tv_sec
+            , rd->collected_value
+            , rd->calculated_value
+            , rd->last_collected_value
+            , rd->last_calculated_value
+            , rd->memsize
+            , rd->next?",":""
+            );
+    }
+
+    buffer_sprintf(wb,
+        "\t\t\t],\n"
+        "\t\t\t\"memory\" : %lu\n"
+        "\t\t}"
+        , memory
+        );
+
+    pthread_rwlock_unlock(&st->rwlock);
+    return memory;
 }
 
 #define RRD_GRAPH_JSON_HEADER "{\n\t\"charts\": [\n"
@@ -224,40 +217,40 @@ unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
 
 void rrd_stats_graph_json(RRDSET *st, char *options, BUFFER *wb)
 {
-       buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
-       rrd_stats_one_json(st, options, wb);
-       buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
+    buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
+    rrd_stats_one_json(st, options, wb);
+    buffer_strcat(wb, RRD_GRAPH_JSON_FOOTER);
 }
 
 void rrd_stats_all_json(BUFFER *wb)
 {
-       unsigned long memory = 0;
-       long c;
-       RRDSET *st;
-
-       buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
-
-       pthread_rwlock_rdlock(&rrdset_root_rwlock);
-       for(st = rrdset_root, c = 0; st ; st = st->next) {
-               if(st->enabled && st->dimensions) {
-                       if(c) buffer_strcat(wb, ",\n");
-                       memory += rrd_stats_one_json(st, NULL, wb);
-                       c++;
-               }
-       }
-       pthread_rwlock_unlock(&rrdset_root_rwlock);
-
-       buffer_sprintf(wb, "\n\t],\n"
-               "\t\"hostname\": \"%s\",\n"
-               "\t\"update_every\": %d,\n"
-               "\t\"history\": %d,\n"
-               "\t\"memory\": %lu\n"
-               "}\n"
-               , hostname
-               , rrd_update_every
-               , rrd_default_history_entries
-               , memory
-               );
+    unsigned long memory = 0;
+    long c;
+    RRDSET *st;
+
+    buffer_strcat(wb, RRD_GRAPH_JSON_HEADER);
+
+    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+    for(st = localhost.rrdset_root, c = 0; st ; st = st->next) {
+        if(st->enabled && st->dimensions) {
+            if(c) buffer_strcat(wb, ",\n");
+            memory += rrd_stats_one_json(st, NULL, wb);
+            c++;
+        }
+    }
+    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+
+    buffer_sprintf(wb, "\n\t],\n"
+        "\t\"hostname\": \"%s\",\n"
+        "\t\"update_every\": %d,\n"
+        "\t\"history\": %d,\n"
+        "\t\"memory\": %lu\n"
+        "}\n"
+        , hostname
+        , rrd_update_every
+        , rrd_default_history_entries
+        , memory
+        );
 }
 
 
@@ -265,42 +258,42 @@ void rrd_stats_all_json(BUFFER *wb)
 // ----------------------------------------------------------------------------
 
 // RRDR dimension options
-#define RRDR_EMPTY     0x01 // the dimension contains / the value is empty (null)
-#define RRDR_RESET     0x02 // the dimension contains / the value is reset
-#define RRDR_HIDDEN    0x04 // the dimension contains / the value is hidden
-#define RRDR_NONZERO   0x08 // the dimension contains / the value is non-zero
+#define RRDR_EMPTY      0x01 // the dimension contains / the value is empty (null)
+#define RRDR_RESET      0x02 // the dimension contains / the value is reset
+#define RRDR_HIDDEN     0x04 // the dimension contains / the value is hidden
+#define RRDR_NONZERO    0x08 // the dimension contains / the value is non-zero
 
 // RRDR result options
 #define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001
 #define RRDR_RESULT_OPTION_RELATIVE 0x00000002
 
 typedef struct rrdresult {
-       RRDSET *st;                     // the chart this result refers to
+    RRDSET *st;         // the chart this result refers to
 
-       uint32_t result_options;        // RRDR_RESULT_OPTION_*
+    uint32_t result_options;    // RRDR_RESULT_OPTION_*
 
-       int d;                                  // the number of dimensions
-       long n;                                 // the number of values in the arrays
-       long rows;                          // the number of rows used
+    int d;                  // the number of dimensions
+    long n;                 // the number of values in the arrays
+    long rows;              // the number of rows used
 
-       uint8_t *od;                    // the options for the dimensions
+    uint8_t *od;            // the options for the dimensions
 
-       time_t *t;                              // array of n timestamps
-       calculated_number *v;   // array n x d values
-       uint8_t *o;                             // array n x d options
+    time_t *t;              // array of n timestamps
+    calculated_number *v;   // array n x d values
+    uint8_t *o;             // array n x d options
 
-       long c;                                 // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
+    long c;                 // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
 
-       long group;                             // how many collected values were grouped for each row
-       int update_every;               // what is the suggested update frequency in seconds
+    long group;             // how many collected values were grouped for each row
+    int update_every;       // what is the suggested update frequency in seconds
 
-       calculated_number min;
-       calculated_number max;
+    calculated_number min;
+    calculated_number max;
 
-       time_t before;
-       time_t after;
+    time_t before;
+    time_t after;
 
-       int has_st_lock;                // if st is read locked by us
+    int has_st_lock;        // if st is read locked by us
 } RRDR;
 
 #define rrdr_rows(r) ((r)->rows)
@@ -308,347 +301,347 @@ typedef struct rrdresult {
 /*
 static void rrdr_dump(RRDR *r)
 {
-       long c, i;
-       RRDDIM *d;
-
-       fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
-
-       for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
-               fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
-                               , d->id
-                               , d->name
-                               , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
-                               , (r->od[c] & RRDR_RESET)?"RESET ":""
-                               , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
-                               , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
-                               );
-       }
-
-       if(r->rows <= 0) {
-               fprintf(stderr, "RRDR does not have any values in it.\n");
-               return;
-       }
-
-       fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
-
-       // for each line in the array
-       for(i = 0; i < r->rows ;i++) {
-               calculated_number *cn = &r->v[ i * r->d ];
-               uint8_t *co = &r->o[ i * r->d ];
-
-               // print the id and the timestamp of the line
-               fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
-
-               // for each dimension
-               for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
-                       if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-                       if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
-
-                       if(co[c] & RRDR_EMPTY)
-                               fprintf(stderr, "null ");
-                       else
-                               fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
-                                       , cn[c]
-                                       , (co[c] & RRDR_EMPTY)?"E":" "
-                                       , (co[c] & RRDR_RESET)?"R":" "
-                                       , (co[c] & RRDR_HIDDEN)?"H":" "
-                                       , (co[c] & RRDR_NONZERO)?"N":" "
-                                       );
-               }
-
-               fprintf(stderr, "\n");
-       }
+    long c, i;
+    RRDDIM *d;
+
+    fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
+
+    for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
+        fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
+                , d->id
+                , d->name
+                , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
+                , (r->od[c] & RRDR_RESET)?"RESET ":""
+                , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
+                , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
+                );
+    }
+
+    if(r->rows <= 0) {
+        fprintf(stderr, "RRDR does not have any values in it.\n");
+        return;
+    }
+
+    fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
+
+    // for each line in the array
+    for(i = 0; i < r->rows ;i++) {
+        calculated_number *cn = &r->v[ i * r->d ];
+        uint8_t *co = &r->o[ i * r->d ];
+
+        // print the id and the timestamp of the line
+        fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
+
+        // for each dimension
+        for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
+            if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+            if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
+
+            if(co[c] & RRDR_EMPTY)
+                fprintf(stderr, "null ");
+            else
+                fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
+                    , cn[c]
+                    , (co[c] & RRDR_EMPTY)?"E":" "
+                    , (co[c] & RRDR_RESET)?"R":" "
+                    , (co[c] & RRDR_HIDDEN)?"H":" "
+                    , (co[c] & RRDR_NONZERO)?"N":" "
+                    );
+        }
+
+        fprintf(stderr, "\n");
+    }
 }
 */
 
 void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
 {
-       char b[strlen(dims) + 1];
-       char *o = b, *tok;
-       strcpy(o, dims);
-
-       long c;
-       RRDDIM *d;
-
-       // disable all of them
-       for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
-               r->od[c] |= RRDR_HIDDEN;
-
-       while(o && *o && (tok = mystrsep(&o, ",|"))) {
-               if(!*tok) continue;
-               
-               uint32_t hash = simple_hash(tok);
-
-               // find it and enable it
-               for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
-                       if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
-                               r->od[c] &= ~RRDR_HIDDEN;
-
-                               // since the user needs this dimension
-                               // make it appear as NONZERO, to return it
-                               // even if the dimension has only zeros
-                               r->od[c] |= RRDR_NONZERO;
-                       }
-               }
-       }
+    char b[strlen(dims) + 1];
+    char *o = b, *tok;
+    strcpy(o, dims);
+
+    long c;
+    RRDDIM *d;
+
+    // disable all of them
+    for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
+        r->od[c] |= RRDR_HIDDEN;
+
+    while(o && *o && (tok = mystrsep(&o, ",|"))) {
+        if(!*tok) continue;
+        
+        uint32_t hash = simple_hash(tok);
+
+        // find it and enable it
+        for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
+            if(unlikely((hash == d->hash && !strcmp(d->id, tok)) || !strcmp(d->name, tok))) {
+                r->od[c] &= ~RRDR_HIDDEN;
+
+                // since the user needs this dimension
+                // make it appear as NONZERO, to return it
+                // even if the dimension has only zeros
+                r->od[c] |= RRDR_NONZERO;
+            }
+        }
+    }
 }
 
 void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
 {
-       switch(format) {
-       case DATASOURCE_JSON:
-               buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
-               break;
-
-       case DATASOURCE_DATATABLE_JSON:
-               buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
-               break;
-
-       case DATASOURCE_DATATABLE_JSONP:
-               buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
-               break;
-
-       case DATASOURCE_JSONP:
-               buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
-               break;
-
-       case DATASOURCE_SSV:
-               buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
-               break;
-
-       case DATASOURCE_CSV:
-               buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
-               break;
-
-       case DATASOURCE_TSV:
-               buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
-               break;
-
-       case DATASOURCE_HTML:
-               buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
-               break;
-
-       case DATASOURCE_JS_ARRAY:
-               buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
-               break;
-
-       case DATASOURCE_SSV_COMMA:
-               buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
-               break;
-
-       default:
-               buffer_strcat(wb, "unknown");
-               break;
-       }
+    switch(format) {
+    case DATASOURCE_JSON:
+        buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
+        break;
+
+    case DATASOURCE_DATATABLE_JSON:
+        buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
+        break;
+
+    case DATASOURCE_DATATABLE_JSONP:
+        buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
+        break;
+
+    case DATASOURCE_JSONP:
+        buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
+        break;
+
+    case DATASOURCE_SSV:
+        buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
+        break;
+
+    case DATASOURCE_CSV:
+        buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
+        break;
+
+    case DATASOURCE_TSV:
+        buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
+        break;
+
+    case DATASOURCE_HTML:
+        buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
+        break;
+
+    case DATASOURCE_JS_ARRAY:
+        buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
+        break;
+
+    case DATASOURCE_SSV_COMMA:
+        buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
+        break;
+
+    default:
+        buffer_strcat(wb, "unknown");
+        break;
+    }
 }
 
 uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
 {
-       if(options & RRDR_OPTION_NONZERO) {
-               long i;
-
-               if(dims && *dims) {
-                       // the caller wants specific dimensions
-                       // disable NONZERO option
-                       // to make sure we don't accidentally prevent
-                       // the specific dimensions from being returned
-                       i = 0;
-               }
-               else {
-                       // find how many dimensions are not zero
-                       long c;
-                       RRDDIM *rd;
-                       for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
-                               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-                               if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
-                               i++;
-                       }
-               }
-
-               // if with nonzero we get i = 0 (no dimensions will be returned)
-               // disable nonzero to show all dimensions
-               if(!i) options &= ~RRDR_OPTION_NONZERO;
-       }
-
-       return options;
+    if(options & RRDR_OPTION_NONZERO) {
+        long i;
+
+        if(dims && *dims) {
+            // the caller wants specific dimensions
+            // disable NONZERO option
+            // to make sure we don't accidentally prevent
+            // the specific dimensions from being returned
+            i = 0;
+        }
+        else {
+            // find how many dimensions are not zero
+            long c;
+            RRDDIM *rd;
+            for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
+                if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+                if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
+                i++;
+            }
+        }
+
+        // if with nonzero we get i = 0 (no dimensions will be returned)
+        // disable nonzero to show all dimensions
+        if(!i) options &= ~RRDR_OPTION_NONZERO;
+    }
+
+    return options;
 }
 
 void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
 {
-       long rows = rrdr_rows(r);
-       long c, i;
-       RRDDIM *rd;
-
-       //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
-       char kq[2] = "",                                        // key quote
-               sq[2] = "";                                             // string quote
-
-       if( options & RRDR_OPTION_GOOGLE_JSON ) {
-               kq[0] = '\0';
-               sq[0] = '\'';
-       }
-       else {
-               kq[0] = '"';
-               sq[0] = '"';
-       }
-
-       buffer_sprintf(wb, "{\n"
-                       "       %sapi%s: 1,\n"
-                       "       %sid%s: %s%s%s,\n"
-                       "       %sname%s: %s%s%s,\n"
-                       "       %sview_update_every%s: %d,\n"
-                       "       %supdate_every%s: %d,\n"
-                       "       %sfirst_entry%s: %u,\n"
-                       "       %slast_entry%s: %u,\n"
-                       "       %sbefore%s: %u,\n"
-                       "       %safter%s: %u,\n"
-                       "       %sdimension_names%s: ["
-                       , kq, kq
-                       , kq, kq, sq, r->st->id, sq
-                       , kq, kq, sq, r->st->name, sq
-                       , kq, kq, r->update_every
-                       , kq, kq, r->st->update_every
-                       , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
-                       , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
-                       , kq, kq, (uint32_t)r->before
-                       , kq, kq, (uint32_t)r->after
-                       , kq, kq);
-
-       for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-               if(i) buffer_strcat(wb, ", ");
-               buffer_strcat(wb, sq);
-               buffer_strcat(wb, rd->name);
-               buffer_strcat(wb, sq);
-               i++;
-       }
-       if(!i) {
+    long rows = rrdr_rows(r);
+    long c, i;
+    RRDDIM *rd;
+
+    //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
+    char kq[2] = "",                    // key quote
+        sq[2] = "";                     // string quote
+
+    if( options & RRDR_OPTION_GOOGLE_JSON ) {
+        kq[0] = '\0';
+        sq[0] = '\'';
+    }
+    else {
+        kq[0] = '"';
+        sq[0] = '"';
+    }
+
+    buffer_sprintf(wb, "{\n"
+            "   %sapi%s: 1,\n"
+            "   %sid%s: %s%s%s,\n"
+            "   %sname%s: %s%s%s,\n"
+            "   %sview_update_every%s: %d,\n"
+            "   %supdate_every%s: %d,\n"
+            "   %sfirst_entry%s: %u,\n"
+            "   %slast_entry%s: %u,\n"
+            "   %sbefore%s: %u,\n"
+            "   %safter%s: %u,\n"
+            "   %sdimension_names%s: ["
+            , kq, kq
+            , kq, kq, sq, r->st->id, sq
+            , kq, kq, sq, r->st->name, sq
+            , kq, kq, r->update_every
+            , kq, kq, r->st->update_every
+            , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
+            , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
+            , kq, kq, (uint32_t)r->before
+            , kq, kq, (uint32_t)r->after
+            , kq, kq);
+
+    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+        if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+        if(i) buffer_strcat(wb, ", ");
+        buffer_strcat(wb, sq);
+        buffer_strcat(wb, rd->name);
+        buffer_strcat(wb, sq);
+        i++;
+    }
+    if(!i) {
 #ifdef NETDATA_INTERNAL_CHECKS
-               error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
+        error("RRDR is empty for %s (RRDR has %d dimensions, options is 0x%08x)", r->st->id, r->d, options);
 #endif
-               rows = 0;
-               buffer_strcat(wb, sq);
-               buffer_strcat(wb, "no data");
-               buffer_strcat(wb, sq);
-       }
-
-       buffer_sprintf(wb, "],\n"
-                       "       %sdimension_ids%s: ["
-                       , kq, kq);
-
-       for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-               if(i) buffer_strcat(wb, ", ");
-               buffer_strcat(wb, sq);
-               buffer_strcat(wb, rd->id);
-               buffer_strcat(wb, sq);
-               i++;
-       }
-       if(!i) {
-               rows = 0;
-               buffer_strcat(wb, sq);
-               buffer_strcat(wb, "no data");
-               buffer_strcat(wb, sq);
-       }
-
-       buffer_sprintf(wb, "],\n"
-                       "       %slatest_values%s: ["
-                       , kq, kq);
-
-       for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-               if(i) buffer_strcat(wb, ", ");
-               i++;
-
-               storage_number n = rd->values[rrdset_last_slot(r->st)];
-
-               if(!does_storage_number_exist(n))
-                       buffer_strcat(wb, "null");
-               else
-                       buffer_rrd_value(wb, unpack_storage_number(n));
-       }
-       if(!i) {
-               rows = 0;
-               buffer_strcat(wb, "null");
-       }
-
-       buffer_sprintf(wb, "],\n"
-                       "       %sview_latest_values%s: ["
-                       , kq, kq);
-
-       i = 0;
-       if(rows) {
-               for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-                       if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-                       if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-                       if(i) buffer_strcat(wb, ", ");
-                       i++;
-
-                       calculated_number *cn = &r->v[ (0) * r->d ];
-                       uint8_t *co = &r->o[ (0) * r->d ];
-
-                       if(co[c] & RRDR_EMPTY)
-                               buffer_strcat(wb, "null");
-                       else
-                               buffer_rrd_value(wb, cn[c]);
-               }
-       }
-       if(!i) {
-               rows = 0;
-               buffer_strcat(wb, "null");
-       }
-
-       buffer_sprintf(wb, "],\n"
-                       "       %sdimensions%s: %ld,\n"
-                       "       %spoints%s: %ld,\n"
-                       "       %sformat%s: %s"
-                       , kq, kq, i
-                       , kq, kq, rows
-                       , kq, kq, sq
-                       );
-
-       rrdr_buffer_print_format(wb, format);
-
-       buffer_sprintf(wb, "%s,\n"
-                       "       %sresult%s: "
-                       , sq
-                       , kq, kq
-                       );
-
-       if(string_value) buffer_strcat(wb, sq);
-       //info("JSONWRAPPER(): %s: END", r->st->id);
+        rows = 0;
+        buffer_strcat(wb, sq);
+        buffer_strcat(wb, "no data");
+        buffer_strcat(wb, sq);
+    }
+
+    buffer_sprintf(wb, "],\n"
+            "   %sdimension_ids%s: ["
+            , kq, kq);
+
+    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+        if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+        if(i) buffer_strcat(wb, ", ");
+        buffer_strcat(wb, sq);
+        buffer_strcat(wb, rd->id);
+        buffer_strcat(wb, sq);
+        i++;
+    }
+    if(!i) {
+        rows = 0;
+        buffer_strcat(wb, sq);
+        buffer_strcat(wb, "no data");
+        buffer_strcat(wb, sq);
+    }
+
+    buffer_sprintf(wb, "],\n"
+            "   %slatest_values%s: ["
+            , kq, kq);
+
+    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+        if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+        if(i) buffer_strcat(wb, ", ");
+        i++;
+
+        storage_number n = rd->values[rrdset_last_slot(r->st)];
+
+        if(!does_storage_number_exist(n))
+            buffer_strcat(wb, "null");
+        else
+            buffer_rrd_value(wb, unpack_storage_number(n));
+    }
+    if(!i) {
+        rows = 0;
+        buffer_strcat(wb, "null");
+    }
+
+    buffer_sprintf(wb, "],\n"
+            "   %sview_latest_values%s: ["
+            , kq, kq);
+
+    i = 0;
+    if(rows) {
+        for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+            if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+            if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+            if(i) buffer_strcat(wb, ", ");
+            i++;
+
+            calculated_number *cn = &r->v[ (0) * r->d ];
+            uint8_t *co = &r->o[ (0) * r->d ];
+
+            if(co[c] & RRDR_EMPTY)
+                buffer_strcat(wb, "null");
+            else
+                buffer_rrd_value(wb, cn[c]);
+        }
+    }
+    if(!i) {
+        rows = 0;
+        buffer_strcat(wb, "null");
+    }
+
+    buffer_sprintf(wb, "],\n"
+            "   %sdimensions%s: %ld,\n"
+            "   %spoints%s: %ld,\n"
+            "   %sformat%s: %s"
+            , kq, kq, i
+            , kq, kq, rows
+            , kq, kq, sq
+            );
+
+    rrdr_buffer_print_format(wb, format);
+
+    buffer_sprintf(wb, "%s,\n"
+            "   %sresult%s: "
+            , sq
+            , kq, kq
+            );
+
+    if(string_value) buffer_strcat(wb, sq);
+    //info("JSONWRAPPER(): %s: END", r->st->id);
 }
 
 void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
 {
-       (void)format;
-
-       char kq[2] = "",                                        // key quote
-               sq[2] = "";                                             // string quote
-
-       if( options & RRDR_OPTION_GOOGLE_JSON ) {
-               kq[0] = '\0';
-               sq[0] = '\'';
-       }
-       else {
-               kq[0] = '"';
-               sq[0] = '"';
-       }
-
-       if(string_value) buffer_strcat(wb, sq);
-
-       buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
-       buffer_rrd_value(wb, r->min);
-       buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
-       buffer_rrd_value(wb, r->max);
-       buffer_strcat(wb, "\n}\n");
+    (void)format;
+
+    char kq[2] = "",                    // key quote
+        sq[2] = "";                     // string quote
+
+    if( options & RRDR_OPTION_GOOGLE_JSON ) {
+        kq[0] = '\0';
+        sq[0] = '\'';
+    }
+    else {
+        kq[0] = '"';
+        sq[0] = '"';
+    }
+
+    if(string_value) buffer_strcat(wb, sq);
+
+    buffer_sprintf(wb, ",\n %smin%s: ", kq, kq);
+    buffer_rrd_value(wb, r->min);
+    buffer_sprintf(wb, ",\n %smax%s: ", kq, kq);
+    buffer_rrd_value(wb, r->max);
+    buffer_strcat(wb, "\n}\n");
 }
 
 #define JSON_DATES_JS 1
@@ -656,1422 +649,1407 @@ void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t option
 
 static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
 {
-       //info("RRD2JSON(): %s: BEGIN", r->st->id);
-       int row_annotations = 0, dates, dates_with_new = 0;
-       char kq[2] = "",                                        // key quote
-               sq[2] = "",                                             // string quote
-               pre_label[101] = "",                    // before each label
-               post_label[101] = "",                   // after each label
-               pre_date[101] = "",                             // the beginning of line, to the date
-               post_date[101] = "",                    // closing the date
-               pre_value[101] = "",                    // before each value
-               post_value[101] = "",                   // after each value
-               post_line[101] = "",                    // at the end of each row
-               normal_annotation[201] = "",    // default row annotation
-               overflow_annotation[201] = "",  // overflow row annotation
-               data_begin[101] = "",                   // between labels and values
-               finish[101] = "";                               // at the end of everything
-
-       if(datatable) {
-               dates = JSON_DATES_JS;
-               if( options & RRDR_OPTION_GOOGLE_JSON ) {
-                       kq[0] = '\0';
-                       sq[0] = '\'';
-               }
-               else {
-                       kq[0] = '"';
-                       sq[0] = '"';
-               }
-               row_annotations = 1;
-               snprintfz(pre_date,   100, "            {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
-               snprintfz(post_date,  100, "%s}", sq);
-               snprintfz(pre_label,  100, ",\n         {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
-               snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
-               snprintfz(pre_value,  100, ",{%sv%s:", kq, kq);
-               strcpy(post_value,         "}");
-               strcpy(post_line,          "]}");
-               snprintfz(data_begin, 100, "\n  ],\n    %srows%s:\n     [\n", kq, kq);
-               strcpy(finish,             "\n  ]\n}");
-
-               snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
-               snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
-
-               buffer_sprintf(wb, "{\n %scols%s:\n     [\n", kq, kq);
-               buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
-               buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
-               buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
-
-               // remove the valueobjects flag
-               // google wants its own keys
-               if(options & RRDR_OPTION_OBJECTSROWS)
-                       options &= ~RRDR_OPTION_OBJECTSROWS;
-       }
-       else {
-               kq[0] = '"';
-               sq[0] = '"';
-               if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
-                       dates = JSON_DATES_TIMESTAMP;
-                       dates_with_new = 0;
-               }
-               else {
-                       dates = JSON_DATES_JS;
-                       dates_with_new = 1;
-               }
-               if( options & RRDR_OPTION_OBJECTSROWS )
-                       strcpy(pre_date, "              { ");
-               else
-                       strcpy(pre_date, "              [ ");
-               strcpy(pre_label,  ", \"");
-               strcpy(post_label, "\"");
-               strcpy(pre_value,  ", ");
-               if( options & RRDR_OPTION_OBJECTSROWS )
-                       strcpy(post_line, "}");
-               else
-                       strcpy(post_line, "]");
-               snprintfz(data_begin, 100, "],\n        %sdata%s:\n     [\n", kq, kq);
-               strcpy(finish,             "\n  ]\n}");
-
-               buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
-               buffer_sprintf(wb, "%stime%s", sq, sq);
-       }
-
-       // -------------------------------------------------------------------------
-       // print the JSON header
-
-       long c, i;
-       RRDDIM *rd;
-
-       // print the header lines
-       for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-               buffer_strcat(wb, pre_label);
-               buffer_strcat(wb, rd->name);
-               buffer_strcat(wb, post_label);
-               i++;
-       }
-       if(!i) {
-               buffer_strcat(wb, pre_label);
-               buffer_strcat(wb, "no data");
-               buffer_strcat(wb, post_label);
-       }
-
-       // print the begin of row data
-       buffer_strcat(wb, data_begin);
-
-       // if all dimensions are hidden, print a null
-       if(!i) {
-               buffer_strcat(wb, finish);
-               return;
-       }
-
-       long start = 0, end = rrdr_rows(r), step = 1;
-       if((options & RRDR_OPTION_REVERSED)) {
-               start = rrdr_rows(r) - 1;
-               end = -1;
-               step = -1;
-       }
-
-       // for each line in the array
-       calculated_number total = 1;
-       for(i = start; i != end ;i += step) {
-               calculated_number *cn = &r->v[ i * r->d ];
-               uint8_t *co = &r->o[ i * r->d ];
-
-               time_t now = r->t[i];
-
-               if(dates == JSON_DATES_JS) {
-                       // generate the local date time
-                       struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
-                       if(!tm) { error("localtime_r() failed."); continue; }
-
-                       if(likely(i != start)) buffer_strcat(wb, ",\n");
-                       buffer_strcat(wb, pre_date);
-
-                       if( options & RRDR_OPTION_OBJECTSROWS )
-                               buffer_sprintf(wb, "%stime%s: ", kq, kq);
-
-                       if(dates_with_new)
-                               buffer_strcat(wb, "new ");
-
-                       buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
-
-                       buffer_strcat(wb, post_date);
-
-                       if(row_annotations) {
-                               // google supports one annotation per row
-                               int annotation_found = 0;
-                               for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
-                                       if(co[c] & RRDR_RESET) {
-                                               buffer_strcat(wb, overflow_annotation);
-                                               annotation_found = 1;
-                                               break;
-                                       }
-                               }
-                               if(!annotation_found)
-                                       buffer_strcat(wb, normal_annotation);
-                       }
-               }
-               else {
-                       // print the timestamp of the line
-                       if(likely(i != start)) buffer_strcat(wb, ",\n");
-                       buffer_strcat(wb, pre_date);
-
-                       if( options & RRDR_OPTION_OBJECTSROWS )
-                               buffer_sprintf(wb, "%stime%s: ", kq, kq);
-
-                       buffer_rrd_value(wb, (calculated_number)r->t[i]);
-                       // in ms
-                       if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
-
-                       buffer_strcat(wb, post_date);
-               }
-
-               if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
-                       total = 0;
-                       for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-                               calculated_number n = cn[c];
-
-                               if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
-                                       n = -n;
-
-                               total += n;
-                       }
-                       // prevent a division by zero
-                       if(total == 0) total = 1;
-               }
-
-               // for each dimension
-               for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
-                       if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-                       if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-                       calculated_number n = cn[c];
-
-                       buffer_strcat(wb, pre_value);
-
-                       if( options & RRDR_OPTION_OBJECTSROWS )
-                               buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
-
-                       if(co[c] & RRDR_EMPTY) {
-                               if(options & RRDR_OPTION_NULL2ZERO)
-                                       buffer_strcat(wb, "0");
-                               else
-                                       buffer_strcat(wb, "null");
-                       }
-                       else {
-                               if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
-                                       n = -n;
-
-                               if(unlikely(options & RRDR_OPTION_PERCENTAGE))
-                                       n = n * 100 / total;
-
-                               buffer_rrd_value(wb, n);
-                       }
-
-                       buffer_strcat(wb, post_value);
-               }
-
-               buffer_strcat(wb, post_line);
-       }
-
-       buffer_strcat(wb, finish);
-       //info("RRD2JSON(): %s: END", r->st->id);
+    //info("RRD2JSON(): %s: BEGIN", r->st->id);
+    int row_annotations = 0, dates, dates_with_new = 0;
+    char kq[2] = "",                    // key quote
+        sq[2] = "",                     // string quote
+        pre_label[101] = "",            // before each label
+        post_label[101] = "",           // after each label
+        pre_date[101] = "",             // the beginning of line, to the date
+        post_date[101] = "",            // closing the date
+        pre_value[101] = "",            // before each value
+        post_value[101] = "",           // after each value
+        post_line[101] = "",            // at the end of each row
+        normal_annotation[201] = "",    // default row annotation
+        overflow_annotation[201] = "",  // overflow row annotation
+        data_begin[101] = "",           // between labels and values
+        finish[101] = "";               // at the end of everything
+
+    if(datatable) {
+        dates = JSON_DATES_JS;
+        if( options & RRDR_OPTION_GOOGLE_JSON ) {
+            kq[0] = '\0';
+            sq[0] = '\'';
+        }
+        else {
+            kq[0] = '"';
+            sq[0] = '"';
+        }
+        row_annotations = 1;
+        snprintfz(pre_date,   100, "        {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
+        snprintfz(post_date,  100, "%s}", sq);
+        snprintfz(pre_label,  100, ",\n     {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
+        snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
+        snprintfz(pre_value,  100, ",{%sv%s:", kq, kq);
+        strcpy(post_value,         "}");
+        strcpy(post_line,          "]}");
+        snprintfz(data_begin, 100, "\n  ],\n    %srows%s:\n [\n", kq, kq);
+        strcpy(finish,             "\n  ]\n}");
+
+        snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
+        snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
+
+        buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
+        buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
+        buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
+        buffer_sprintf(wb, "        {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
+
+        // remove the valueobjects flag
+        // google wants its own keys
+        if(options & RRDR_OPTION_OBJECTSROWS)
+            options &= ~RRDR_OPTION_OBJECTSROWS;
+    }
+    else {
+        kq[0] = '"';
+        sq[0] = '"';
+        if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
+            dates = JSON_DATES_TIMESTAMP;
+            dates_with_new = 0;
+        }
+        else {
+            dates = JSON_DATES_JS;
+            dates_with_new = 1;
+        }
+        if( options & RRDR_OPTION_OBJECTSROWS )
+            strcpy(pre_date, "      { ");
+        else
+            strcpy(pre_date, "      [ ");
+        strcpy(pre_label,  ", \"");
+        strcpy(post_label, "\"");
+        strcpy(pre_value,  ", ");
+        if( options & RRDR_OPTION_OBJECTSROWS )
+            strcpy(post_line, "}");
+        else
+            strcpy(post_line, "]");
+        snprintfz(data_begin, 100, "],\n    %sdata%s:\n [\n", kq, kq);
+        strcpy(finish,             "\n  ]\n}");
+
+        buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
+        buffer_sprintf(wb, "%stime%s", sq, sq);
+    }
+
+    // -------------------------------------------------------------------------
+    // print the JSON header
+
+    long c, i;
+    RRDDIM *rd;
+
+    // print the header lines
+    for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+        if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+        buffer_strcat(wb, pre_label);
+        buffer_strcat(wb, rd->name);
+        buffer_strcat(wb, post_label);
+        i++;
+    }
+    if(!i) {
+        buffer_strcat(wb, pre_label);
+        buffer_strcat(wb, "no data");
+        buffer_strcat(wb, post_label);
+    }
+
+    // print the begin of row data
+    buffer_strcat(wb, data_begin);
+
+    // if all dimensions are hidden, print a null
+    if(!i) {
+        buffer_strcat(wb, finish);
+        return;
+    }
+
+    long start = 0, end = rrdr_rows(r), step = 1;
+    if((options & RRDR_OPTION_REVERSED)) {
+        start = rrdr_rows(r) - 1;
+        end = -1;
+        step = -1;
+    }
+
+    // for each line in the array
+    calculated_number total = 1;
+    for(i = start; i != end ;i += step) {
+        calculated_number *cn = &r->v[ i * r->d ];
+        uint8_t *co = &r->o[ i * r->d ];
+
+        time_t now = r->t[i];
+
+        if(dates == JSON_DATES_JS) {
+            // generate the local date time
+            struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
+            if(!tm) { error("localtime_r() failed."); continue; }
+
+            if(likely(i != start)) buffer_strcat(wb, ",\n");
+            buffer_strcat(wb, pre_date);
+
+            if( options & RRDR_OPTION_OBJECTSROWS )
+                buffer_sprintf(wb, "%stime%s: ", kq, kq);
+
+            if(dates_with_new)
+                buffer_strcat(wb, "new ");
+
+            buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+            buffer_strcat(wb, post_date);
+
+            if(row_annotations) {
+                // google supports one annotation per row
+                int annotation_found = 0;
+                for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
+                    if(co[c] & RRDR_RESET) {
+                        buffer_strcat(wb, overflow_annotation);
+                        annotation_found = 1;
+                        break;
+                    }
+                }
+                if(!annotation_found)
+                    buffer_strcat(wb, normal_annotation);
+            }
+        }
+        else {
+            // print the timestamp of the line
+            if(likely(i != start)) buffer_strcat(wb, ",\n");
+            buffer_strcat(wb, pre_date);
+
+            if( options & RRDR_OPTION_OBJECTSROWS )
+                buffer_sprintf(wb, "%stime%s: ", kq, kq);
+
+            buffer_rrd_value(wb, (calculated_number)r->t[i]);
+            // in ms
+            if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
+
+            buffer_strcat(wb, post_date);
+        }
+
+        if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+            total = 0;
+            for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+                calculated_number n = cn[c];
+
+                if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+                    n = -n;
+
+                total += n;
+            }
+            // prevent a division by zero
+            if(total == 0) total = 1;
+        }
+
+        // for each dimension
+        for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
+            if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+            if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+            calculated_number n = cn[c];
+
+            buffer_strcat(wb, pre_value);
+
+            if( options & RRDR_OPTION_OBJECTSROWS )
+                buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
+
+            if(co[c] & RRDR_EMPTY) {
+                if(options & RRDR_OPTION_NULL2ZERO)
+                    buffer_strcat(wb, "0");
+                else
+                    buffer_strcat(wb, "null");
+            }
+            else {
+                if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+                    n = -n;
+
+                if(unlikely(options & RRDR_OPTION_PERCENTAGE))
+                    n = n * 100 / total;
+
+                buffer_rrd_value(wb, n);
+            }
+
+            buffer_strcat(wb, post_value);
+        }
+
+        buffer_strcat(wb, post_line);
+    }
+
+    buffer_strcat(wb, finish);
+    //info("RRD2JSON(): %s: END", r->st->id);
 }
 
 static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
 {
-       //info("RRD2CSV(): %s: BEGIN", r->st->id);
-       long c, i;
-       RRDDIM *d;
-
-       // print the csv header
-       for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
-               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-               if(!i) {
-                       buffer_strcat(wb, startline);
-                       if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
-                       buffer_strcat(wb, "time");
-                       if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
-               }
-               buffer_strcat(wb, separator);
-               if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
-               buffer_strcat(wb, d->name);
-               if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
-               i++;
-       }
-       buffer_strcat(wb, endline);
-
-       if(!i) {
-               // no dimensions present
-               return;
-       }
-
-       long start = 0, end = rrdr_rows(r), step = 1;
-       if((options & RRDR_OPTION_REVERSED)) {
-               start = rrdr_rows(r) - 1;
-               end = -1;
-               step = -1;
-       }
-
-       // for each line in the array
-       calculated_number total = 1;
-       for(i = start; i != end ;i += step) {
-               calculated_number *cn = &r->v[ i * r->d ];
-               uint8_t *co = &r->o[ i * r->d ];
-
-               buffer_strcat(wb, betweenlines);
-               buffer_strcat(wb, startline);
-
-               time_t now = r->t[i];
-
-               if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
-                       // print the timestamp of the line
-                       buffer_rrd_value(wb, (calculated_number)now);
-                       // in ms
-                       if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
-               }
-               else {
-                       // generate the local date time
-                       struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
-                       if(!tm) { error("localtime() failed."); continue; }
-                       buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
-               }
-
-               if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
-                       total = 0;
-                       for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
-                               calculated_number n = cn[c];
-
-                               if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
-                                       n = -n;
-
-                               total += n;
-                       }
-                       // prevent a division by zero
-                       if(total == 0) total = 1;
-               }
-
-               // for each dimension
-               for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
-                       if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-                       if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-                       buffer_strcat(wb, separator);
-
-                       calculated_number n = cn[c];
-
-                       if(co[c] & RRDR_EMPTY) {
-                               if(options & RRDR_OPTION_NULL2ZERO)
-                                       buffer_strcat(wb, "0");
-                               else
-                                       buffer_strcat(wb, "null");
-                       }
-                       else {
-                               if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
-                                       n = -n;
-
-                               if(unlikely(options & RRDR_OPTION_PERCENTAGE))
-                                       n = n * 100 / total;
-
-                               buffer_rrd_value(wb, n);
-                       }
-               }
-
-               buffer_strcat(wb, endline);
-       }
-       //info("RRD2CSV(): %s: END", r->st->id);
+    //info("RRD2CSV(): %s: BEGIN", r->st->id);
+    long c, i;
+    RRDDIM *d;
+
+    // print the csv header
+    for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+        if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+        if(!i) {
+            buffer_strcat(wb, startline);
+            if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+            buffer_strcat(wb, "time");
+            if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+        }
+        buffer_strcat(wb, separator);
+        if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+        buffer_strcat(wb, d->name);
+        if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
+        i++;
+    }
+    buffer_strcat(wb, endline);
+
+    if(!i) {
+        // no dimensions present
+        return;
+    }
+
+    long start = 0, end = rrdr_rows(r), step = 1;
+    if((options & RRDR_OPTION_REVERSED)) {
+        start = rrdr_rows(r) - 1;
+        end = -1;
+        step = -1;
+    }
+
+    // for each line in the array
+    calculated_number total = 1;
+    for(i = start; i != end ;i += step) {
+        calculated_number *cn = &r->v[ i * r->d ];
+        uint8_t *co = &r->o[ i * r->d ];
+
+        buffer_strcat(wb, betweenlines);
+        buffer_strcat(wb, startline);
+
+        time_t now = r->t[i];
+
+        if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
+            // print the timestamp of the line
+            buffer_rrd_value(wb, (calculated_number)now);
+            // in ms
+            if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
+        }
+        else {
+            // generate the local date time
+            struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
+            if(!tm) { error("localtime() failed."); continue; }
+            buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+        }
+
+        if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+            total = 0;
+            for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+                calculated_number n = cn[c];
+
+                if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+                    n = -n;
+
+                total += n;
+            }
+            // prevent a division by zero
+            if(total == 0) total = 1;
+        }
+
+        // for each dimension
+        for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+            if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+            if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+            buffer_strcat(wb, separator);
+
+            calculated_number n = cn[c];
+
+            if(co[c] & RRDR_EMPTY) {
+                if(options & RRDR_OPTION_NULL2ZERO)
+                    buffer_strcat(wb, "0");
+                else
+                    buffer_strcat(wb, "null");
+            }
+            else {
+                if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+                    n = -n;
+
+                if(unlikely(options & RRDR_OPTION_PERCENTAGE))
+                    n = n * 100 / total;
+
+                buffer_rrd_value(wb, n);
+            }
+        }
+
+        buffer_strcat(wb, endline);
+    }
+    //info("RRD2CSV(): %s: END", r->st->id);
 }
 
 inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
-       long c;
-       RRDDIM *d;
-
-       calculated_number *cn = &r->v[ i * r->d ];
-       uint8_t *co = &r->o[ i * r->d ];
-
-       calculated_number sum = 0, min = 0, max = 0, v;
-       int all_null = 1, init = 1;
-
-       calculated_number total = 1;
-       if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
-               total = 0;
-               for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
-                       calculated_number n = cn[c];
-
-                       if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
-                               n = -n;
-
-                       total += n;
-               }
-               // prevent a division by zero
-               if(total == 0) total = 1;
-       }
-
-       // for each dimension
-       for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
-               if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
-               if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
-
-               calculated_number n = cn[c];
-
-               if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
-                       n = -n;
-
-               if(unlikely(options & RRDR_OPTION_PERCENTAGE))
-                       n = n * 100 / total;
-
-               if(unlikely(init)) {
-                       if(n > 0) {
-                               min = 0;
-                               max = n;
-                       }
-                       else {
-                               min = n;
-                               max = 0;
-                       }
-                       init = 0;
-               }
-
-               if(likely(!(co[c] & RRDR_EMPTY))) {
-                       all_null = 0;
-                       sum += n;
-               }
-
-               if(n < min) min = n;
-               if(n > max) max = n;
-       }
-
-       if(unlikely(all_null)) {
-               if(likely(*all_values_are_null))
-                       *all_values_are_null = 1;
-               return 0;
-       }
-       else {
-               if(likely(*all_values_are_null))
-                       *all_values_are_null = 0;
-       }
-
-       if(options & RRDR_OPTION_MIN2MAX)
-               v = max - min;
-       else
-               v = sum;
-
-       return v;
+    long c;
+    RRDDIM *d;
+
+    calculated_number *cn = &r->v[ i * r->d ];
+    uint8_t *co = &r->o[ i * r->d ];
+
+    calculated_number sum = 0, min = 0, max = 0, v;
+    int all_null = 1, init = 1;
+
+    calculated_number total = 1;
+    if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
+        total = 0;
+        for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+            calculated_number n = cn[c];
+
+            if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+                n = -n;
+
+            total += n;
+        }
+        // prevent a division by zero
+        if(total == 0) total = 1;
+    }
+
+    // for each dimension
+    for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
+        if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
+        if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
+
+        calculated_number n = cn[c];
+
+        if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
+            n = -n;
+
+        if(unlikely(options & RRDR_OPTION_PERCENTAGE))
+            n = n * 100 / total;
+
+        if(unlikely(init)) {
+            if(n > 0) {
+                min = 0;
+                max = n;
+            }
+            else {
+                min = n;
+                max = 0;
+            }
+            init = 0;
+        }
+
+        if(likely(!(co[c] & RRDR_EMPTY))) {
+            all_null = 0;
+            sum += n;
+        }
+
+        if(n < min) min = n;
+        if(n > max) max = n;
+    }
+
+    if(unlikely(all_null)) {
+        if(likely(all_values_are_null))
+            *all_values_are_null = 1;
+        return 0;
+    }
+    else {
+        if(likely(all_values_are_null))
+            *all_values_are_null = 0;
+    }
+
+    if(options & RRDR_OPTION_MIN2MAX)
+        v = max - min;
+    else
+        v = sum;
+
+    return v;
 }
 
 static void rrdr2ssv(RRDR *r, BUFFER *wb, uint32_t options, const char *prefix, const char *separator, const char *suffix)
 {
-       //info("RRD2SSV(): %s: BEGIN", r->st->id);
-       long i;
-
-       buffer_strcat(wb, prefix);
-       long start = 0, end = rrdr_rows(r), step = 1;
-       if((options & RRDR_OPTION_REVERSED)) {
-               start = rrdr_rows(r) - 1;
-               end = -1;
-               step = -1;
-       }
-
-       // for each line in the array
-       for(i = start; i != end ;i += step) {
-               int all_values_are_null = 0;
-               calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
-
-               if(likely(i != start)) {
-                       if(r->min > v) r->min = v;
-                       if(r->max < v) r->max = v;
-               }
-               else {
-                       r->min = v;
-                       r->max = v;
-               }
-
-               if(likely(i != start))
-                       buffer_strcat(wb, separator);
-
-               if(all_values_are_null) {
-                       if(options & RRDR_OPTION_NULL2ZERO)
-                               buffer_strcat(wb, "0");
-                       else
-                               buffer_strcat(wb, "null");
-               }
-               else
-                       buffer_rrd_value(wb, v);
-       }
-       buffer_strcat(wb, suffix);
-       //info("RRD2SSV(): %s: END", r->st->id);
+    //info("RRD2SSV(): %s: BEGIN", r->st->id);
+    long i;
+
+    buffer_strcat(wb, prefix);
+    long start = 0, end = rrdr_rows(r), step = 1;
+    if((options & RRDR_OPTION_REVERSED)) {
+        start = rrdr_rows(r) - 1;
+        end = -1;
+        step = -1;
+    }
+
+    // for each line in the array
+    for(i = start; i != end ;i += step) {
+        int all_values_are_null = 0;
+        calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
+
+        if(likely(i != start)) {
+            if(r->min > v) r->min = v;
+            if(r->max < v) r->max = v;
+        }
+        else {
+            r->min = v;
+            r->max = v;
+        }
+
+        if(likely(i != start))
+            buffer_strcat(wb, separator);
+
+        if(all_values_are_null) {
+            if(options & RRDR_OPTION_NULL2ZERO)
+                buffer_strcat(wb, "0");
+            else
+                buffer_strcat(wb, "null");
+        }
+        else
+            buffer_rrd_value(wb, v);
+    }
+    buffer_strcat(wb, suffix);
+    //info("RRD2SSV(): %s: END", r->st->id);
 }
 
 inline static calculated_number *rrdr_line_values(RRDR *r)
 {
-       return &r->v[ r->c * r->d ];
+    return &r->v[ r->c * r->d ];
 }
 
 inline static uint8_t *rrdr_line_options(RRDR *r)
 {
-       return &r->o[ r->c * r->d ];
+    return &r->o[ r->c * r->d ];
 }
 
 inline static int rrdr_line_init(RRDR *r, time_t t)
 {
-       r->c++;
+    r->c++;
 
-       if(unlikely(r->c >= r->n)) {
-               error("requested to step above RRDR size for chart %s", r->st->name);
-               r->c = r->n - 1;
-       }
+    if(unlikely(r->c >= r->n)) {
+        error("requested to step above RRDR size for chart %s", r->st->name);
+        r->c = r->n - 1;
+    }
 
-       // save the time
-       r->t[r->c] = t;
+    // save the time
+    r->t[r->c] = t;
 
-       return 1;
+    return 1;
 }
 
 inline static void rrdr_lock_rrdset(RRDR *r) {
-       if(unlikely(!r)) {
-               error("NULL value given!");
-               return;
-       }
+    if(unlikely(!r)) {
+        error("NULL value given!");
+        return;
+    }
 
-       pthread_rwlock_rdlock(&r->st->rwlock);
-       r->has_st_lock = 1;
+    pthread_rwlock_rdlock(&r->st->rwlock);
+    r->has_st_lock = 1;
 }
 
 inline static void rrdr_unlock_rrdset(RRDR *r) {
-       if(unlikely(!r)) {
-               error("NULL value given!");
-               return;
-       }
-
-       if(likely(r->has_st_lock)) {
-               pthread_rwlock_unlock(&r->st->rwlock);
-               r->has_st_lock = 0;
-       }
+    if(unlikely(!r)) {
+        error("NULL value given!");
+        return;
+    }
+
+    if(likely(r->has_st_lock)) {
+        pthread_rwlock_unlock(&r->st->rwlock);
+        r->has_st_lock = 0;
+    }
 }
 
 inline static void rrdr_free(RRDR *r)
 {
-       if(unlikely(!r)) {
-               error("NULL value given!");
-               return;
-       }
-
-       rrdr_unlock_rrdset(r);
-       if(likely(r->t)) free(r->t);
-       if(likely(r->v)) free(r->v);
-       if(likely(r->o)) free(r->o);
-       if(likely(r->od)) free(r->od);
-       free(r);
+    if(unlikely(!r)) {
+        error("NULL value given!");
+        return;
+    }
+
+    rrdr_unlock_rrdset(r);
+    freez(r->t);
+    freez(r->v);
+    freez(r->o);
+    freez(r->od);
+    freez(r);
 }
 
 inline void rrdr_done(RRDR *r)
 {
-       r->rows = r->c + 1;
-       r->c = 0;
+    r->rows = r->c + 1;
+    r->c = 0;
 }
 
 static RRDR *rrdr_create(RRDSET *st, long n)
 {
-       if(unlikely(!st)) {
-               error("NULL value given!");
-               return NULL;
-       }
-
-       RRDR *r = calloc(1, sizeof(RRDR));
-       if(unlikely(!r)) goto cleanup;
-
-       r->st = st;
-
-       rrdr_lock_rrdset(r);
-
-       RRDDIM *rd;
-       for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
-
-       r->n = n;
-
-       r->t = malloc(n * sizeof(time_t));
-       if(unlikely(!r->t)) goto cleanup;
+    if(unlikely(!st)) {
+        error("NULL value given!");
+        return NULL;
+    }
 
-       r->v = malloc(n * r->d * sizeof(calculated_number));
-       if(unlikely(!r->v)) goto cleanup;
+    RRDR *r = callocz(1, sizeof(RRDR));
+    r->st = st;
 
-       r->o = malloc(n * r->d * sizeof(uint8_t));
-       if(unlikely(!r->o)) goto cleanup;
+    rrdr_lock_rrdset(r);
 
-       r->od = malloc(r->d * sizeof(uint8_t));
-       if(unlikely(!r->od)) goto cleanup;
+    RRDDIM *rd;
+    for(rd = st->dimensions ; rd ; rd = rd->next) r->d++;
 
-       // set the hidden flag on hidden dimensions
-       int c;
-       for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
-               if(unlikely(rd->flags & RRDDIM_FLAG_HIDDEN)) r->od[c] = RRDR_HIDDEN;
-               else r->od[c] = 0;
-       }
+    r->n = n;
 
-       r->c = -1;
+    r->t = mallocz(n * sizeof(time_t));
+    r->v = mallocz(n * r->d * sizeof(calculated_number));
+    r->o = mallocz(n * r->d * sizeof(uint8_t));
+    r->od = mallocz(r->d * sizeof(uint8_t));
 
-       r->group = 1;
-       r->update_every = 1;
+    // 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;
+    }
 
-       return r;
+    r->c = -1;
+    r->group = 1;
+    r->update_every = 1;
 
-cleanup:
-       error("Cannot allocate RRDR memory for %ld entries", n);
-       if(likely(r)) rrdr_free(r);
-       return NULL;
+    return r;
 }
 
 RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, int aligned)
 {
-       int debug = st->debug;
-       int absolute_period_requested = -1;
-
-       time_t first_entry_t = rrdset_first_entry_t(st);
-       time_t last_entry_t  = rrdset_last_entry_t(st);
-
-       if(before == 0 && after == 0) {
-               before = last_entry_t;
-               after = first_entry_t;
-               absolute_period_requested = 0;
-       }
-
-       // allow relative for before and after
-       if(((before < 0)?-before:before) <= (st->update_every * st->entries)) {
-               before = last_entry_t + before;
-               absolute_period_requested = 0;
-       }
-
-       if(((after < 0)?-after:after) <= (st->update_every * st->entries)) {
-               if(after == 0) after = -st->update_every;
-               after = before + after;
-               absolute_period_requested = 0;
-       }
-
-       if(absolute_period_requested == -1)
-               absolute_period_requested = 1;
-
-       // make sure they are within our timeframe
-       if(before > last_entry_t)  before = last_entry_t;
-       if(before < first_entry_t) before = first_entry_t;
-
-       if(after > last_entry_t)  after = last_entry_t;
-       if(after < first_entry_t) after = first_entry_t;
-
-       // check if they are upside down
-       if(after > before) {
-               time_t tmp = before;
-               before = after;
-               after = tmp;
-       }
-
-       // the duration of the chart
-       time_t duration = before - after;
-       long available_points = duration / st->update_every;
-
-       if(duration <= 0 || available_points <= 0)
-               return rrdr_create(st, 1);
-
-       // check the wanted points
-       if(points < 0) points = -points;
-       if(points > available_points) points = available_points;
-       if(points == 0) points = available_points;
-
-       // calculate proper grouping of source data
-       long group = available_points / points;
-       if(group <= 0) group = 1;
-
-       // round group to the closest integer
-       if(available_points % points > points / 2) group++;
-
-       time_t after_new  = (aligned) ? (after  - (after  % (group * st->update_every))) : after;
-       time_t before_new = (aligned) ? (before - (before % (group * st->update_every))) : before;
-       long points_new   = (before_new - after_new) / st->update_every / group;
-
-       // find the starting and ending slots in our round robin db
-       long    start_at_slot = rrdset_time2slot(st, before_new),
-                       stop_at_slot  = rrdset_time2slot(st, after_new);
+    int debug = st->debug;
+    int absolute_period_requested = -1;
+
+    time_t first_entry_t = rrdset_first_entry_t(st);
+    time_t last_entry_t  = rrdset_last_entry_t(st);
+
+    if(before == 0 && after == 0) {
+        before = last_entry_t;
+        after = first_entry_t;
+        absolute_period_requested = 0;
+    }
+
+    // allow relative for before and after
+    if(((before < 0)?-before:before) <= (st->update_every * st->entries)) {
+        before = last_entry_t + before;
+        absolute_period_requested = 0;
+    }
+
+    if(((after < 0)?-after:after) <= (st->update_every * st->entries)) {
+        if(after == 0) after = -st->update_every;
+        after = before + after;
+        absolute_period_requested = 0;
+    }
+
+    if(absolute_period_requested == -1)
+        absolute_period_requested = 1;
+
+    // make sure they are within our timeframe
+    if(before > last_entry_t)  before = last_entry_t;
+    if(before < first_entry_t) before = first_entry_t;
+
+    if(after > last_entry_t)  after = last_entry_t;
+    if(after < first_entry_t) after = first_entry_t;
+
+    // check if they are upside down
+    if(after > before) {
+        time_t tmp = before;
+        before = after;
+        after = tmp;
+    }
+
+    // the duration of the chart
+    time_t duration = before - after;
+    long available_points = duration / st->update_every;
+
+    if(duration <= 0 || available_points <= 0)
+        return rrdr_create(st, 1);
+
+    // check the wanted points
+    if(points < 0) points = -points;
+    if(points > available_points) points = available_points;
+    if(points == 0) points = available_points;
+
+    // calculate proper grouping of source data
+    long group = available_points / points;
+    if(group <= 0) group = 1;
+
+    // round group to the closest integer
+    if(available_points % points > points / 2) group++;
+
+    time_t after_new  = (aligned) ? (after  - (after  % (group * st->update_every))) : after;
+    time_t before_new = (aligned) ? (before - (before % (group * st->update_every))) : before;
+    long points_new   = (before_new - after_new) / st->update_every / group;
+
+    // find the starting and ending slots in our round robin db
+    long    start_at_slot = rrdset_time2slot(st, before_new),
+            stop_at_slot  = rrdset_time2slot(st, after_new);
 
 #ifdef NETDATA_INTERNAL_CHECKS
-       if(after_new < first_entry_t) {
-               error("after_new %u is too small, minimum %u", (uint32_t)after_new, (uint32_t)first_entry_t);
-       }
-       if(after_new > last_entry_t) {
-               error("after_new %u is too big, maximum %u", (uint32_t)after_new, (uint32_t)last_entry_t);
-       }
-       if(before_new < first_entry_t) {
-               error("before_new %u is too small, minimum %u", (uint32_t)before_new, (uint32_t)first_entry_t);
-       }
-       if(before_new > last_entry_t) {
-               error("before_new %u is too big, maximum %u", (uint32_t)before_new, (uint32_t)last_entry_t);
-       }
-       if(start_at_slot < 0 || start_at_slot >= st->entries) {
-               error("start_at_slot is invalid %ld, expected 0 to %ld", start_at_slot, st->entries - 1);
-       }
-       if(stop_at_slot < 0 || stop_at_slot >= st->entries) {
-               error("stop_at_slot is invalid %ld, expected 0 to %ld", stop_at_slot, st->entries - 1);
-       }
-       if(points_new > (before_new - after_new) / group / st->update_every + 1) {
-               error("points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
-       }
+    if(after_new < first_entry_t) {
+        error("after_new %u is too small, minimum %u", (uint32_t)after_new, (uint32_t)first_entry_t);
+    }
+    if(after_new > last_entry_t) {
+        error("after_new %u is too big, maximum %u", (uint32_t)after_new, (uint32_t)last_entry_t);
+    }
+    if(before_new < first_entry_t) {
+        error("before_new %u is too small, minimum %u", (uint32_t)before_new, (uint32_t)first_entry_t);
+    }
+    if(before_new > last_entry_t) {
+        error("before_new %u is too big, maximum %u", (uint32_t)before_new, (uint32_t)last_entry_t);
+    }
+    if(start_at_slot < 0 || start_at_slot >= st->entries) {
+        error("start_at_slot is invalid %ld, expected 0 to %ld", start_at_slot, st->entries - 1);
+    }
+    if(stop_at_slot < 0 || stop_at_slot >= st->entries) {
+        error("stop_at_slot is invalid %ld, expected 0 to %ld", stop_at_slot, st->entries - 1);
+    }
+    if(points_new > (before_new - after_new) / group / st->update_every + 1) {
+        error("points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
+    }
 #endif
 
-       //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
+    //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
 
-       after = after_new;
-       before = before_new;
-       duration = before - after;
-       points = points_new;
+    after = after_new;
+    before = before_new;
+    duration = before - after;
+    points = points_new;
 
-       // Now we have:
-       // before = the end time of the calculation
-       // after = the start time of the calculation
-       // duration = the duration of the calculation
-       // group = the number of source points to aggregate / group together
-       // method = the method of grouping source points
-       // points = the number of points to generate
+    // Now we have:
+    // before = the end time of the calculation
+    // after = the start time of the calculation
+    // duration = the duration of the calculation
+    // group = the number of source points to aggregate / group together
+    // method = the method of grouping source points
+    // points = the number of points to generate
 
 
-       // -------------------------------------------------------------------------
-       // initialize our result set
+    // -------------------------------------------------------------------------
+    // initialize our result set
 
-       RRDR *r = rrdr_create(st, points);
-       if(!r) {
+    RRDR *r = rrdr_create(st, points);
+    if(!r) {
 #ifdef NETDATA_INTERNAL_CHECKS
-               error("Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
+        error("Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
 #endif
-               return NULL;
-       }
-       if(!r->d) {
+        return NULL;
+    }
+    if(!r->d) {
 #ifdef NETDATA_INTERNAL_CHECKS
-               error("Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
+        error("Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
 #endif
-               return r;
-       }
-
-       if(absolute_period_requested == 1)
-               r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
-       else
-               r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
-
-       // find how many dimensions we have
-       long dimensions = r->d;
-
-
-       // -------------------------------------------------------------------------
-       // checks for debugging
-
-       if(debug) debug(D_RRD_STATS, "INFO %s first_t: %u, last_t: %u, all_duration: %u, after: %u, before: %u, duration: %u, points: %ld, group: %ld"
-                       , st->id
-                       , (uint32_t)first_entry_t
-                       , (uint32_t)last_entry_t
-                       , (uint32_t)(last_entry_t - first_entry_t)
-                       , (uint32_t)after
-                       , (uint32_t)before
-                       , (uint32_t)duration
-                       , points
-                       , group
-                       );
-
-
-       // -------------------------------------------------------------------------
-       // temp arrays for keeping values per dimension
-
-       calculated_number       last_values[dimensions]; // keep the last value of each dimension
-       calculated_number       group_values[dimensions]; // keep sums when grouping
-       long                            group_counts[dimensions]; // keep the number of values added to group_values
-       uint8_t                         group_options[dimensions];
-       uint8_t                         found_non_zero[dimensions];
-
-
-       // initialize them
-       RRDDIM *rd;
-       long c;
-       for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
-               last_values[c] = 0;
-               group_values[c] = 0;
-               group_counts[c] = 0;
-               group_options[c] = 0;
-               found_non_zero[c] = 0;
-       }
-
-
-       // -------------------------------------------------------------------------
-       // the main loop
-
-       time_t  now = rrdset_slot2time(st, start_at_slot),
-                       dt = st->update_every,
-                       group_start_t = 0;
-
-       if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %u (stop_at_t: %ld), before_t: %u (start_at_t: %ld), start_t(now): %u, current_entry: %ld, entries: %ld"
-                       , st->id
-                       , (uint32_t)after
-                       , stop_at_slot
-                       , (uint32_t)before
-                       , start_at_slot
-                       , (uint32_t)now
-                       , st->current_entry
-                       , st->entries
-                       );
-
-       r->group = group;
-       r->update_every = group * st->update_every;
-       r->before = now;
-       r->after = now;
-
-       //info("RRD2RRDR(): %s: STARTING", st->id);
-
-       long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
-       for(; !stop_now ; now -= dt, slot--, counter++) {
-               if(unlikely(slot < 0)) slot = st->entries - 1;
-               if(unlikely(slot == stop_at_slot)) stop_now = counter;
-
-               if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %ld, %s %s"
-                               , st->id
-                               , slot
-                               , counter
-                               , group_count + 1
-                               , added
-                               , now
-                               , (group_count + 1 == group)?"PRINT":"  -  "
-                               , (now >= after && now <= before)?"RANGE":"  -  "
-                               );
-
-               // make sure we return data in the proper time range
-               if(unlikely(now > before)) continue;
-               if(unlikely(now < after)) break;
-
-               if(unlikely(group_count == 0)) {
-                       group_start_t = now;
-               }
-               group_count++;
-
-               if(unlikely(group_count == group)) {
-                       if(unlikely(added >= points)) break;
-                       add_this = 1;
-               }
-
-               // do the calculations
-               for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
-                       storage_number n = rd->values[slot];
-                       if(unlikely(!does_storage_number_exist(n))) continue;
-
-                       group_counts[c]++;
-
-                       calculated_number value = unpack_storage_number(n);
-                       if(likely(value != 0.0)) {
-                               group_options[c] |= RRDR_NONZERO;
-                               found_non_zero[c] = 1;
-                       }
-
-                       if(unlikely(did_storage_number_reset(n)))
-                               group_options[c] |= RRDR_RESET;
-
-                       switch(group_method) {
-                               case GROUP_MAX:
-                                       if(unlikely(fabsl(value) > fabsl(group_values[c])))
-                                               group_values[c] = value;
-                                       break;
-
-                               default:
-                               case GROUP_SUM:
-                               case GROUP_AVERAGE:
-                                       group_values[c] += value;
-                                       break;
-
-                               case GROUP_INCREMENTAL_SUM:
-                                       if(unlikely(slot == start_at_slot))
-                                               last_values[c] = value;
-
-                                       group_values[c] += last_values[c] - value;
-                                       last_values[c] = value;
-                                       break;
-                       }
-               }
-
-               // added it
-               if(unlikely(add_this)) {
-                       if(unlikely(!rrdr_line_init(r, group_start_t))) break;
-
-                       r->after = now;
-
-                       calculated_number *cn = rrdr_line_values(r);
-                       uint8_t *co = rrdr_line_options(r);
-
-                       for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
-
-                               // update the dimension options
-                               if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
-
-                               // store the specific point options
-                               co[c] = group_options[c];
-
-                               // store the value
-                               if(unlikely(group_counts[c] == 0)) {
-                                       cn[c] = 0.0;
-                                       co[c] |= RRDR_EMPTY;
-                               }
-                               else if(unlikely(group_method == GROUP_AVERAGE)) {
-                                       // GROUP_AVERAGE
-                                       cn[c] = group_values[c] / group_counts[c];
-                               }
-                               else {
-                                       // GROUP_SUM
-                                       // GROUP_MAX
-                                       // GROUP_INCREMENTAL_SUM
-                                       cn[c] = group_values[c];
-                               }
-
-                               if(cn[c] < r->min) r->min = cn[c];
-                               if(cn[c] > r->max) r->max = cn[c];
-
-                               // reset them for the next loop
-                               group_values[c] = 0;
-                               group_counts[c] = 0;
-                               group_options[c] = 0;
-                       }
-
-                       added++;
-                       group_count = 0;
-                       add_this = 0;
-               }
-       }
-
-       rrdr_done(r);
-       //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
-       //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
-       return r;
+        return r;
+    }
+
+    if(absolute_period_requested == 1)
+        r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
+    else
+        r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
+
+    // find how many dimensions we have
+    long dimensions = r->d;
+
+
+    // -------------------------------------------------------------------------
+    // checks for debugging
+
+    if(debug) debug(D_RRD_STATS, "INFO %s first_t: %u, last_t: %u, all_duration: %u, after: %u, before: %u, duration: %u, points: %ld, group: %ld"
+            , st->id
+            , (uint32_t)first_entry_t
+            , (uint32_t)last_entry_t
+            , (uint32_t)(last_entry_t - first_entry_t)
+            , (uint32_t)after
+            , (uint32_t)before
+            , (uint32_t)duration
+            , points
+            , group
+            );
+
+
+    // -------------------------------------------------------------------------
+    // temp arrays for keeping values per dimension
+
+    calculated_number   last_values[dimensions]; // keep the last value of each dimension
+    calculated_number   group_values[dimensions]; // keep sums when grouping
+    long                group_counts[dimensions]; // keep the number of values added to group_values
+    uint8_t             group_options[dimensions];
+    uint8_t             found_non_zero[dimensions];
+
+
+    // initialize them
+    RRDDIM *rd;
+    long c;
+    for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+        last_values[c] = 0;
+        group_values[c] = 0;
+        group_counts[c] = 0;
+        group_options[c] = 0;
+        found_non_zero[c] = 0;
+    }
+
+
+    // -------------------------------------------------------------------------
+    // the main loop
+
+    time_t  now = rrdset_slot2time(st, start_at_slot),
+            dt = st->update_every,
+            group_start_t = 0;
+
+    if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %u (stop_at_t: %ld), before_t: %u (start_at_t: %ld), start_t(now): %u, current_entry: %ld, entries: %ld"
+            , st->id
+            , (uint32_t)after
+            , stop_at_slot
+            , (uint32_t)before
+            , start_at_slot
+            , (uint32_t)now
+            , st->current_entry
+            , st->entries
+            );
+
+    r->group = group;
+    r->update_every = group * st->update_every;
+    r->before = now;
+    r->after = now;
+
+    //info("RRD2RRDR(): %s: STARTING", st->id);
+
+    long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
+    for(; !stop_now ; now -= dt, slot--, counter++) {
+        if(unlikely(slot < 0)) slot = st->entries - 1;
+        if(unlikely(slot == stop_at_slot)) stop_now = counter;
+
+        if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %ld, %s %s"
+                , st->id
+                , slot
+                , counter
+                , group_count + 1
+                , added
+                , now
+                , (group_count + 1 == group)?"PRINT":"  -  "
+                , (now >= after && now <= before)?"RANGE":"  -  "
+                );
+
+        // make sure we return data in the proper time range
+        if(unlikely(now > before)) continue;
+        if(unlikely(now < after)) break;
+
+        if(unlikely(group_count == 0)) {
+            group_start_t = now;
+        }
+        group_count++;
+
+        if(unlikely(group_count == group)) {
+            if(unlikely(added >= points)) break;
+            add_this = 1;
+        }
+
+        // do the calculations
+        for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+            storage_number n = rd->values[slot];
+            if(unlikely(!does_storage_number_exist(n))) continue;
+
+            group_counts[c]++;
+
+            calculated_number value = unpack_storage_number(n);
+            if(likely(value != 0.0)) {
+                group_options[c] |= RRDR_NONZERO;
+                found_non_zero[c] = 1;
+            }
+
+            if(unlikely(did_storage_number_reset(n)))
+                group_options[c] |= RRDR_RESET;
+
+            switch(group_method) {
+                case GROUP_MAX:
+                    if(unlikely(fabsl(value) > fabsl(group_values[c])))
+                        group_values[c] = value;
+                    break;
+
+                default:
+                case GROUP_SUM:
+                case GROUP_AVERAGE:
+                    group_values[c] += value;
+                    break;
+
+                case GROUP_INCREMENTAL_SUM:
+                    if(unlikely(slot == start_at_slot))
+                        last_values[c] = value;
+
+                    group_values[c] += last_values[c] - value;
+                    last_values[c] = value;
+                    break;
+            }
+        }
+
+        // added it
+        if(unlikely(add_this)) {
+            if(unlikely(!rrdr_line_init(r, group_start_t))) break;
+
+            r->after = now;
+
+            calculated_number *cn = rrdr_line_values(r);
+            uint8_t *co = rrdr_line_options(r);
+
+            for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+
+                // update the dimension options
+                if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
+
+                // store the specific point options
+                co[c] = group_options[c];
+
+                // store the value
+                if(unlikely(group_counts[c] == 0)) {
+                    cn[c] = 0.0;
+                    co[c] |= RRDR_EMPTY;
+                }
+                else if(unlikely(group_method == GROUP_AVERAGE)) {
+                    // GROUP_AVERAGE
+                    cn[c] = group_values[c] / group_counts[c];
+                }
+                else {
+                    // GROUP_SUM
+                    // GROUP_MAX
+                    // GROUP_INCREMENTAL_SUM
+                    cn[c] = group_values[c];
+                }
+
+                if(cn[c] < r->min) r->min = cn[c];
+                if(cn[c] > r->max) r->max = cn[c];
+
+                // reset them for the next loop
+                group_values[c] = 0;
+                group_counts[c] = 0;
+                group_options[c] = 0;
+            }
+
+            added++;
+            group_count = 0;
+            add_this = 0;
+        }
+    }
+
+    rrdr_done(r);
+    //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
+    //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
+    return r;
 }
 
-int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null)
+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 *latest_timestamp, int *value_is_null)
 {
-       RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
-       if(!r) {
-               if(value_is_null) *value_is_null = 1;
-               return 500;
-       }
+    RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
+    if(!r) {
+        if(value_is_null) *value_is_null = 1;
+        return 500;
+    }
 
-       if(rrdr_rows(r) == 0) {
-               rrdr_free(r);
-               if(value_is_null) *value_is_null = 1;
-               return 400;
-       }
+    if(rrdr_rows(r) == 0) {
+        rrdr_free(r);
+        if(value_is_null) *value_is_null = 1;
+        return 400;
+    }
 
-       if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
-               wb->options |= WB_CONTENT_NO_CACHEABLE;
-       else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
-               wb->options |= WB_CONTENT_CACHEABLE;
+    if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
+        wb->options |= WB_CONTENT_NO_CACHEABLE;
+    else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
+        wb->options |= WB_CONTENT_CACHEABLE;
 
-       options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
+    options = rrdr_check_options(r, options, dimensions);
 
-       if(dimensions)
-               rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
+    if(dimensions)
+        rrdr_disable_not_selected_dimensions(r, dimensions);
 
-       if(latest_timestamp)
-               *latest_timestamp = r->before;
+    if(latest_timestamp)
+        *latest_timestamp = r->before;
 
-       long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
-       *n = rrdr2value(r, i, options, value_is_null);
+    long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
+    *n = rrdr2value(r, i, options, value_is_null);
 
-       rrdr_free(r);
-       return 200;
+    rrdr_free(r);
+    return 200;
 }
 
 int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp)
 {
-       RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
-       if(!r) {
-               buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
-               return 500;
-       }
-
-       if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
-               wb->options |= WB_CONTENT_NO_CACHEABLE;
-       else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
-               wb->options |= WB_CONTENT_CACHEABLE;
-
-       options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
-
-       if(dimensions)
-               rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
-
-       if(latest_timestamp && rrdr_rows(r) > 0)
-               *latest_timestamp = r->before;
-
-       switch(format) {
-       case DATASOURCE_SSV:
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
-                       rrdr2ssv(r, wb, options, "", " ", "");
-                       rrdr_json_wrapper_end(r, wb, format, options, 1);
-               }
-               else {
-                       wb->contenttype = CT_TEXT_PLAIN;
-                       rrdr2ssv(r, wb, options, "", " ", "");
-               }
-               break;
-
-       case DATASOURCE_SSV_COMMA:
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
-                       rrdr2ssv(r, wb, options, "", ",", "");
-                       rrdr_json_wrapper_end(r, wb, format, options, 1);
-               }
-               else {
-                       wb->contenttype = CT_TEXT_PLAIN;
-                       rrdr2ssv(r, wb, options, "", ",", "");
-               }
-               break;
-
-       case DATASOURCE_JS_ARRAY:
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
-                       rrdr2ssv(r, wb, options, "[", ",", "]");
-                       rrdr_json_wrapper_end(r, wb, format, options, 0);
-               }
-               else {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr2ssv(r, wb, options, "[", ",", "]");
-               }
-               break;
-
-       case DATASOURCE_CSV:
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
-                       rrdr2csv(r, wb, options, "", ",", "\\n", "");
-                       rrdr_json_wrapper_end(r, wb, format, options, 1);
-               }
-               else {
-                       wb->contenttype = CT_TEXT_PLAIN;
-                       rrdr2csv(r, wb, options, "", ",", "\r\n", "");
-               }
-               break;
-
-       case DATASOURCE_CSV_JSON_ARRAY:
-               wb->contenttype = CT_APPLICATION_JSON;
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
-                       buffer_strcat(wb, "[\n");
-                       rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
-                       buffer_strcat(wb, "\n]");
-                       rrdr_json_wrapper_end(r, wb, format, options, 0);
-               }
-               else {
-                       wb->contenttype = CT_TEXT_PLAIN;
-                       buffer_strcat(wb, "[\n");
-                       rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
-                       buffer_strcat(wb, "\n]");
-               }
-               break;
-
-       case DATASOURCE_TSV:
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
-                       rrdr2csv(r, wb, options, "", "\t", "\\n", "");
-                       rrdr_json_wrapper_end(r, wb, format, options, 1);
-               }
-               else {
-                       wb->contenttype = CT_TEXT_PLAIN;
-                       rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
-               }
-               break;
-
-       case DATASOURCE_HTML:
-               if(options & RRDR_OPTION_JSON_WRAP) {
-                       wb->contenttype = CT_APPLICATION_JSON;
-                       rrdr_json_wrapper_begin(r, wb, format, options, 1);
-                       buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
-                       rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
-                       buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
-                       rrdr_json_wrapper_end(r, wb, format, options, 1);
-               }
-               else {
-                       wb->contenttype = CT_TEXT_HTML;
-                       buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
-                       rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
-                       buffer_strcat(wb, "</table>\n</center>\n</html>\n");
-               }
-               break;
-
-       case DATASOURCE_DATATABLE_JSONP:
-               wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
-               rrdr2json(r, wb, options, 1);
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_end(r, wb, format, options, 0);
-               break;
-
-       case DATASOURCE_DATATABLE_JSON:
-               wb->contenttype = CT_APPLICATION_JSON;
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
-               rrdr2json(r, wb, options, 1);
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_end(r, wb, format, options, 0);
-               break;
-
-       case DATASOURCE_JSONP:
-               wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
-               rrdr2json(r, wb, options, 0);
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_end(r, wb, format, options, 0);
-               break;
-
-       case DATASOURCE_JSON:
-       default:
-               wb->contenttype = CT_APPLICATION_JSON;
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_begin(r, wb, format, options, 0);
-
-               rrdr2json(r, wb, options, 0);
-
-               if(options & RRDR_OPTION_JSON_WRAP)
-                       rrdr_json_wrapper_end(r, wb, format, options, 0);
-               break;
-       }
-
-       rrdr_free(r);
-       return 200;
+    RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
+    if(!r) {
+        buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
+        return 500;
+    }
+
+    if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
+        wb->options |= WB_CONTENT_NO_CACHEABLE;
+    else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
+        wb->options |= WB_CONTENT_CACHEABLE;
+
+    options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
+
+    if(dimensions)
+        rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
+
+    if(latest_timestamp && rrdr_rows(r) > 0)
+        *latest_timestamp = r->before;
+
+    switch(format) {
+    case DATASOURCE_SSV:
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr_json_wrapper_begin(r, wb, format, options, 1);
+            rrdr2ssv(r, wb, options, "", " ", "");
+            rrdr_json_wrapper_end(r, wb, format, options, 1);
+        }
+        else {
+            wb->contenttype = CT_TEXT_PLAIN;
+            rrdr2ssv(r, wb, options, "", " ", "");
+        }
+        break;
+
+    case DATASOURCE_SSV_COMMA:
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr_json_wrapper_begin(r, wb, format, options, 1);
+            rrdr2ssv(r, wb, options, "", ",", "");
+            rrdr_json_wrapper_end(r, wb, format, options, 1);
+        }
+        else {
+            wb->contenttype = CT_TEXT_PLAIN;
+            rrdr2ssv(r, wb, options, "", ",", "");
+        }
+        break;
+
+    case DATASOURCE_JS_ARRAY:
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr_json_wrapper_begin(r, wb, format, options, 0);
+            rrdr2ssv(r, wb, options, "[", ",", "]");
+            rrdr_json_wrapper_end(r, wb, format, options, 0);
+        }
+        else {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr2ssv(r, wb, options, "[", ",", "]");
+        }
+        break;
+
+    case DATASOURCE_CSV:
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr_json_wrapper_begin(r, wb, format, options, 1);
+            rrdr2csv(r, wb, options, "", ",", "\\n", "");
+            rrdr_json_wrapper_end(r, wb, format, options, 1);
+        }
+        else {
+            wb->contenttype = CT_TEXT_PLAIN;
+            rrdr2csv(r, wb, options, "", ",", "\r\n", "");
+        }
+        break;
+
+    case DATASOURCE_CSV_JSON_ARRAY:
+        wb->contenttype = CT_APPLICATION_JSON;
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            rrdr_json_wrapper_begin(r, wb, format, options, 0);
+            buffer_strcat(wb, "[\n");
+            rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
+            buffer_strcat(wb, "\n]");
+            rrdr_json_wrapper_end(r, wb, format, options, 0);
+        }
+        else {
+            wb->contenttype = CT_TEXT_PLAIN;
+            buffer_strcat(wb, "[\n");
+            rrdr2csv(r, wb, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
+            buffer_strcat(wb, "\n]");
+        }
+        break;
+
+    case DATASOURCE_TSV:
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr_json_wrapper_begin(r, wb, format, options, 1);
+            rrdr2csv(r, wb, options, "", "\t", "\\n", "");
+            rrdr_json_wrapper_end(r, wb, format, options, 1);
+        }
+        else {
+            wb->contenttype = CT_TEXT_PLAIN;
+            rrdr2csv(r, wb, options, "", "\t", "\r\n", "");
+        }
+        break;
+
+    case DATASOURCE_HTML:
+        if(options & RRDR_OPTION_JSON_WRAP) {
+            wb->contenttype = CT_APPLICATION_JSON;
+            rrdr_json_wrapper_begin(r, wb, format, options, 1);
+            buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
+            rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
+            buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
+            rrdr_json_wrapper_end(r, wb, format, options, 1);
+        }
+        else {
+            wb->contenttype = CT_TEXT_HTML;
+            buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
+            rrdr2csv(r, wb, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
+            buffer_strcat(wb, "</table>\n</center>\n</html>\n");
+        }
+        break;
+
+    case DATASOURCE_DATATABLE_JSONP:
+        wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+        rrdr2json(r, wb, options, 1);
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_end(r, wb, format, options, 0);
+        break;
+
+    case DATASOURCE_DATATABLE_JSON:
+        wb->contenttype = CT_APPLICATION_JSON;
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+        rrdr2json(r, wb, options, 1);
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_end(r, wb, format, options, 0);
+        break;
+
+    case DATASOURCE_JSONP:
+        wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+        rrdr2json(r, wb, options, 0);
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_end(r, wb, format, options, 0);
+        break;
+
+    case DATASOURCE_JSON:
+    default:
+        wb->contenttype = CT_APPLICATION_JSON;
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_begin(r, wb, format, options, 0);
+
+        rrdr2json(r, wb, options, 0);
+
+        if(options & RRDR_OPTION_JSON_WRAP)
+            rrdr_json_wrapper_end(r, wb, format, options, 0);
+        break;
+    }
+
+    rrdr_free(r);
+    return 200;
 }
 
 time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long points, long group, int group_method, time_t after, time_t before, int only_non_zero)
 {
-       int c;
-       pthread_rwlock_rdlock(&st->rwlock);
+    int c;
+    pthread_rwlock_rdlock(&st->rwlock);
 
 
-       // -------------------------------------------------------------------------
-       // switch from JSON to google JSON
+    // -------------------------------------------------------------------------
+    // switch from JSON to google JSON
 
-       char kq[2] = "\"";
-       char sq[2] = "\"";
-       switch(type) {
-               case DATASOURCE_DATATABLE_JSON:
-               case DATASOURCE_DATATABLE_JSONP:
-                       kq[0] = '\0';
-                       sq[0] = '\'';
-                       break;
+    char kq[2] = "\"";
+    char sq[2] = "\"";
+    switch(type) {
+        case DATASOURCE_DATATABLE_JSON:
+        case DATASOURCE_DATATABLE_JSONP:
+            kq[0] = '\0';
+            sq[0] = '\'';
+            break;
 
-               case DATASOURCE_JSON:
-               default:
-                       break;
-       }
+        case DATASOURCE_JSON:
+        default:
+            break;
+    }
 
 
-       // -------------------------------------------------------------------------
-       // validate the parameters
+    // -------------------------------------------------------------------------
+    // validate the parameters
 
-       if(points < 1) points = 1;
-       if(group < 1) group = 1;
+    if(points < 1) points = 1;
+    if(group < 1) group = 1;
 
-       if(before == 0 || before > rrdset_last_entry_t(st)) before = rrdset_last_entry_t(st);
-       if(after  == 0 || after < rrdset_first_entry_t(st)) after = rrdset_first_entry_t(st);
+    if(before == 0 || before > rrdset_last_entry_t(st)) before = rrdset_last_entry_t(st);
+    if(after  == 0 || after < rrdset_first_entry_t(st)) after = rrdset_first_entry_t(st);
 
-       // ---
+    // ---
 
-       // our return value (the last timestamp printed)
-       // this is required to detect re-transmit in google JSONP
-       time_t last_timestamp = 0;
+    // our return value (the last timestamp printed)
+    // this is required to detect re-transmit in google JSONP
+    time_t last_timestamp = 0;
 
 
-       // -------------------------------------------------------------------------
-       // find how many dimensions we have
+    // -------------------------------------------------------------------------
+    // find how many dimensions we have
 
-       int dimensions = 0;
-       RRDDIM *rd;
-       for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
-       if(!dimensions) {
-               pthread_rwlock_unlock(&st->rwlock);
-               buffer_strcat(wb, "No dimensions yet.");
-               return 0;
-       }
+    int dimensions = 0;
+    RRDDIM *rd;
+    for( rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
+    if(!dimensions) {
+        pthread_rwlock_unlock(&st->rwlock);
+        buffer_strcat(wb, "No dimensions yet.");
+        return 0;
+    }
 
 
-       // -------------------------------------------------------------------------
-       // prepare various strings, to speed up the loop
+    // -------------------------------------------------------------------------
+    // prepare various strings, to speed up the loop
 
-       char overflow_annotation[201]; snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
-       char normal_annotation[201];   snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
-       char pre_date[51];             snprintfz(pre_date,             50, "            {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
-       char post_date[21];            snprintfz(post_date,            20, "%s}", sq);
-       char pre_value[21];            snprintfz(pre_value,            20, ",{%sv%s:", kq, kq);
-       char post_value[21];           strcpy(post_value,                  "}");
+    char overflow_annotation[201]; snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
+    char normal_annotation[201];   snprintfz(normal_annotation,   200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
+    char pre_date[51];             snprintfz(pre_date,             50, "        {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
+    char post_date[21];            snprintfz(post_date,            20, "%s}", sq);
+    char pre_value[21];            snprintfz(pre_value,            20, ",{%sv%s:", kq, kq);
+    char post_value[21];           strcpy(post_value,                  "}");
 
 
-       // -------------------------------------------------------------------------
-       // checks for debugging
+    // -------------------------------------------------------------------------
+    // checks for debugging
 
-       if(st->debug) {
-               debug(D_RRD_STATS, "%s first_entry_t = %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(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)
+            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];
+        if((before - after) > st->entries * st->update_every)
+            debug(D_RRD_STATS, "WARNING: %s The time difference between the oldest and the newest entries (%ld) is higher than the capacity of the database (%ld)", st->name, before - after, st->entries * st->update_every);
+    }
+
+
+    // -------------------------------------------------------------------------
+    // temp arrays for keeping values per dimension
+
+    calculated_number group_values[dimensions]; // keep sums when grouping
+    int               print_hidden[dimensions]; // keep hidden flags
+    int               found_non_zero[dimensions];
+    int               found_non_existing[dimensions];
 
-       // initialize them
-       for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
-               group_values[c] = 0;
-               print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
-               found_non_zero[c] = 0;
-               found_non_existing[c] = 0;
-       }
+    // initialize them
+    for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
+        group_values[c] = 0;
+        print_hidden[c] = (rd->flags & RRDDIM_FLAG_HIDDEN)?1:0;
+        found_non_zero[c] = 0;
+        found_non_existing[c] = 0;
+    }
 
 
-       // error("OLD: points=%d after=%d before=%d group=%d, duration=%d", entries_to_show, before - (st->update_every * group * entries_to_show), before, group, before - after + 1);
-       // rrd2array(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method, only_non_zero);
-       // rrd2rrdr(st, entries_to_show, before - (st->update_every * group * entries_to_show), before, group_method);
-
-       // -------------------------------------------------------------------------
-       // remove dimensions that contain only zeros
-
-       int max_loop = 1;
-       if(only_non_zero) max_loop = 2;
-
-       for(; max_loop ; max_loop--) {
-
-               // -------------------------------------------------------------------------
-               // print the JSON header
-
-               buffer_sprintf(wb, "{\n %scols%s:\n     [\n", kq, kq);
-               buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
-               buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
-               buffer_sprintf(wb, "            {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
-
-               // print the header for each dimension
-               // and update the print_hidden array for the dimensions that should be hidden
-               int pc = 0;
-               for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
-                       if(!print_hidden[c]) {
-                               pc++;
-                               buffer_sprintf(wb, ",\n         {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, rd->name, sq, kq, kq, sq, sq, kq, kq, sq, sq);
-                       }
-               }
-               if(!pc) {
-                       buffer_sprintf(wb, ",\n         {%sid%s:%s%s,%slabel%s:%s%s%s,%spattern%s:%s%s,%stype%s:%snumber%s}", kq, kq, sq, sq, kq, kq, sq, "no data", sq, kq, kq, sq, sq, kq, kq, sq, sq);
-               }
-
-               // print the begin of row data
-               buffer_sprintf(wb, "\n  ],\n    %srows%s:\n     [\n", kq, kq);
-
-
-               // -------------------------------------------------------------------------
-               // the main loop
-
-               int annotate_reset = 0;
-               int annotation_count = 0;
-
-               long    t = rrdset_time2slot(st, before),
-                               stop_at_t = rrdset_time2slot(st, after),
-                               stop_now = 0;
-
-               t -= t % group;
-
-               time_t  now = rrdset_slot2time(st, t),
-                               dt = st->update_every;
-
-               long count = 0, printed = 0, group_count = 0;
-               last_timestamp = 0;
-
-               if(st->debug) debug(D_RRD_STATS, "%s: REQUEST after:%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;
+    // 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;
 }
index 42a7e20097a74974a2ba554a73e9dfe88815c82a..99400346efd20413379a178c5b553bf468567b91 100644 (file)
@@ -1,8 +1,3 @@
-#include <time.h>
-
-#include "web_buffer.h"
-#include "rrd.h"
-
 #ifndef NETDATA_RRD2JSON_H
 #define NETDATA_RRD2JSON_H 1
 
@@ -35,24 +30,24 @@ extern char *hostname;
 #define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma"
 #define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray"
 
-#define GROUP_AVERAGE                  0
-#define GROUP_MAX                              1
-#define GROUP_SUM                              2
-#define GROUP_INCREMENTAL_SUM  3
+#define GROUP_AVERAGE           0
+#define GROUP_MAX               1
+#define GROUP_SUM               2
+#define GROUP_INCREMENTAL_SUM   3
 
-#define RRDR_OPTION_NONZERO            0x00000001 // don't output dimensions will just zero values
-#define RRDR_OPTION_REVERSED           0x00000002 // output the rows in reverse order (oldest to newest)
-#define RRDR_OPTION_ABSOLUTE           0x00000004 // values positive, for DATASOURCE_SSV before summing
-#define RRDR_OPTION_MIN2MAX                    0x00000008 // when adding dimensions, use max - min, instead of sum
-#define RRDR_OPTION_SECONDS                    0x00000010 // output seconds, instead of dates
-#define RRDR_OPTION_MILLISECONDS       0x00000020 // output milliseconds, instead of dates
-#define RRDR_OPTION_NULL2ZERO          0x00000040 // do not show nulls, convert them to zeros
-#define RRDR_OPTION_OBJECTSROWS                0x00000080 // each row of values should be an object, not an array
-#define RRDR_OPTION_GOOGLE_JSON                0x00000100 // comply with google JSON/JSONP specs
-#define RRDR_OPTION_JSON_WRAP          0x00000200 // wrap the response in a JSON header with info about the result
-#define RRDR_OPTION_LABEL_QUOTES       0x00000400 // in CSV output, wrap header labels in double quotes
-#define RRDR_OPTION_PERCENTAGE         0x00000800 // give values as percentage of total
-#define RRDR_OPTION_NOT_ALIGNED                0x00001000 // do not align charts for persistant timeframes
+#define RRDR_OPTION_NONZERO         0x00000001 // don't output dimensions will just zero values
+#define RRDR_OPTION_REVERSED        0x00000002 // output the rows in reverse order (oldest to newest)
+#define RRDR_OPTION_ABSOLUTE        0x00000004 // values positive, for DATASOURCE_SSV before summing
+#define RRDR_OPTION_MIN2MAX         0x00000008 // when adding dimensions, use max - min, instead of sum
+#define RRDR_OPTION_SECONDS         0x00000010 // output seconds, instead of dates
+#define RRDR_OPTION_MILLISECONDS    0x00000020 // output milliseconds, instead of dates
+#define RRDR_OPTION_NULL2ZERO       0x00000040 // do not show nulls, convert them to zeros
+#define RRDR_OPTION_OBJECTSROWS     0x00000080 // each row of values should be an object, not an array
+#define RRDR_OPTION_GOOGLE_JSON     0x00000100 // comply with google JSON/JSONP specs
+#define RRDR_OPTION_JSON_WRAP       0x00000200 // wrap the response in a JSON header with info about the result
+#define RRDR_OPTION_LABEL_QUOTES    0x00000400 // in CSV output, wrap header labels in double quotes
+#define RRDR_OPTION_PERCENTAGE      0x00000800 // give values as percentage of total
+#define RRDR_OPTION_NOT_ALIGNED     0x00001000 // do not align charts for persistant timeframes
 
 extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
 extern void rrd_stats_api_v1_charts(BUFFER *wb);
@@ -66,6 +61,6 @@ extern void rrd_stats_all_json(BUFFER *wb);
 extern time_t rrd_stats_json(int type, RRDSET *st, BUFFER *wb, long entries_to_show, long group, int group_method, time_t after, time_t before, int only_non_zero);
 
 extern int rrd2format(RRDSET *st, BUFFER *out, BUFFER *dimensions, uint32_t format, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp);
-extern int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null);
+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 *latest_timestamp, int *value_is_null);
 
 #endif /* NETDATA_RRD2JSON_H */
index df80bf725cfb310795a2b3c2201f3c514d0b2560..27fe5f2c722a20a62b0e0dcd45d19c48eb632840 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#ifdef STORAGE_WITH_MATH
-#include <math.h>
-#endif
-
 #include "common.h"
-#include "log.h"
-#include "storage_number.h"
-
-#if __GNUC__
-#if __x86_64__ || __ppc64__
-#define ENVIRONMENT64
-#else
-#define ENVIRONMENT32
-#endif
-#endif
 
 extern char *print_number_lu_r(char *str, unsigned long uvalue);
 extern char *print_number_llu_r(char *str, unsigned long long uvalue);
 
 storage_number pack_storage_number(calculated_number value, uint32_t flags)
 {
-       // bit 32 = sign 0:positive, 1:negative
-       // bit 31 = 0:divide, 1:multiply
-       // bit 30, 29, 28 = (multiplier or divider) 0-6 (7 total)
-       // bit 27, 26, 25 flags
-       // bit 24 to bit 1 = the value
-
-       storage_number r = get_storage_number_flags(flags);
-       if(!value) return r;
-
-       int m = 0;
-       calculated_number n = value;
-
-       // if the value is negative
-       // add the sign bit and make it positive
-       if(n < 0) {
-               r += (1 << 31); // the sign bit 32
-               n = -n;
-       }
-
-       // make its integer part fit in 0x00ffffff
-       // by dividing it by 10 up to 7 times
-       // and increasing the multiplier
-       while(m < 7 && n > (calculated_number)0x00ffffff) {
-               n /= 10;
-               m++;
-       }
-
-       if(m) {
-               // the value was too big and we divided it
-               // so we add a multiplier to unpack it
-               r += (1 << 30) + (m << 27); // the multiplier m
-
-               if(n > (calculated_number)0x00ffffff) {
-                       error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
-                       r += 0x00ffffff;
-                       return r;
-               }
-       }
-       else {
-               // 0x0019999e is the number that can be multiplied
-               // by 10 to give 0x00ffffff
-               // while the value is below 0x0019999e we can
-               // multiply it by 10, up to 7 times, increasing
-               // the multiplier
-               while(m < 7 && n < (calculated_number)0x0019999e) {
-                       n *= 10;
-                       m++;
-               }
-
-               // the value was small enough and we multiplied it
-               // so we add a divider to unpack it
-               r += (0 << 30) + (m << 27); // the divider m
-       }
+    // bit 32 = sign 0:positive, 1:negative
+    // bit 31 = 0:divide, 1:multiply
+    // bit 30, 29, 28 = (multiplier or divider) 0-6 (7 total)
+    // bit 27, 26, 25 flags
+    // bit 24 to bit 1 = the value
+
+    storage_number r = get_storage_number_flags(flags);
+    if(!value) return r;
+
+    int m = 0;
+    calculated_number n = value;
+
+    // if the value is negative
+    // add the sign bit and make it positive
+    if(n < 0) {
+        r += (1 << 31); // the sign bit 32
+        n = -n;
+    }
+
+    // make its integer part fit in 0x00ffffff
+    // by dividing it by 10 up to 7 times
+    // and increasing the multiplier
+    while(m < 7 && n > (calculated_number)0x00ffffff) {
+        n /= 10;
+        m++;
+    }
+
+    if(m) {
+        // the value was too big and we divided it
+        // so we add a multiplier to unpack it
+        r += (1 << 30) + (m << 27); // the multiplier m
+
+        if(n > (calculated_number)0x00ffffff) {
+            error("Number " CALCULATED_NUMBER_FORMAT " is too big.", value);
+            r += 0x00ffffff;
+            return r;
+        }
+    }
+    else {
+        // 0x0019999e is the number that can be multiplied
+        // by 10 to give 0x00ffffff
+        // while the value is below 0x0019999e we can
+        // multiply it by 10, up to 7 times, increasing
+        // the multiplier
+        while(m < 7 && n < (calculated_number)0x0019999e) {
+            n *= 10;
+            m++;
+        }
+
+        // the value was small enough and we multiplied it
+        // so we add a divider to unpack it
+        r += (0 << 30) + (m << 27); // the divider m
+    }
 
 #ifdef STORAGE_WITH_MATH
-       // without this there are rounding problems
-       // example: 0.9 becomes 0.89
-       r += lrint((double) n);
+    // without this there are rounding problems
+    // example: 0.9 becomes 0.89
+    r += lrint((double) n);
 #else
-       r += (storage_number)n;
+    r += (storage_number)n;
 #endif
 
-       return r;
+    return r;
 }
 
 calculated_number unpack_storage_number(storage_number value)
 {
-       if(!value) return 0;
+    if(!value) return 0;
 
-       int sign = 0, exp = 0;
+    int sign = 0, exp = 0;
 
-       value ^= get_storage_number_flags(value);
+    value ^= get_storage_number_flags(value);
 
-       if(value & (1 << 31)) {
-               sign = 1;
-               value ^= 1 << 31;
-       }
+    if(value & (1 << 31)) {
+        sign = 1;
+        value ^= 1 << 31;
+    }
 
-       if(value & (1 << 30)) {
-               exp = 1;
-               value ^= 1 << 30;
-       }
+    if(value & (1 << 30)) {
+        exp = 1;
+        value ^= 1 << 30;
+    }
 
-       int mul = value >> 27;
-       value ^= mul << 27;
+    int mul = value >> 27;
+    value ^= mul << 27;
 
-       calculated_number n = value;
+    calculated_number n = value;
 
-       // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
+    // fprintf(stderr, "UNPACK: %08X, sign = %d, exp = %d, mul = %d, n = " CALCULATED_NUMBER_FORMAT "\n", value, sign, exp, mul, n);
 
-       while(mul > 0) {
-               if(exp) n *= 10;
-               else n /= 10;
-               mul--;
-       }
+    while(mul > 0) {
+        if(exp) n *= 10;
+        else n /= 10;
+        mul--;
+    }
 
-       if(sign) n = -n;
-       return n;
+    if(sign) n = -n;
+    return n;
 }
 
 int print_calculated_number(char *str, calculated_number value)
 {
-       char *wstr = str;
+    char *wstr = str;
 
-       int sign = (value < 0) ? 1 : 0;
-       if(sign) value = -value;
+    int sign = (value < 0) ? 1 : 0;
+    if(sign) value = -value;
 
 #ifdef STORAGE_WITH_MATH
-       // without llrint() there are rounding problems
-       // for example 0.9 becomes 0.89
-       unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000);
+    // without llrint() there are rounding problems
+    // for example 0.9 becomes 0.89
+    unsigned long long uvalue = (unsigned long long int) llrint(value * (calculated_number)100000);
 #else
-       unsigned long long uvalue = value * (calculated_number)100000;
+    unsigned long long uvalue = value * (calculated_number)100000;
 #endif
 
 #ifdef ENVIRONMENT32
-       if(uvalue > (unsigned long long)0xffffffff)
-               wstr = print_number_llu_r(str, uvalue);
-       else
-               wstr = print_number_lu_r(str, uvalue);
+    if(uvalue > (unsigned long long)0xffffffff)
+        wstr = print_number_llu_r(str, uvalue);
+    else
+        wstr = print_number_lu_r(str, uvalue);
 #else
-       do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
+    do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
 #endif
 
-       // make sure we have 6 bytes at least
-       while((wstr - str) < 6) *wstr++ = '0';
+    // make sure we have 6 bytes at least
+    while((wstr - str) < 6) *wstr++ = '0';
 
-       // put the sign back
-       if(sign) *wstr++ = '-';
+    // put the sign back
+    if(sign) *wstr++ = '-';
 
-       // reverse it
+    // reverse it
     char *begin = str, *end = --wstr, aux;
     while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
-       // wstr--;
-       // strreverse(str, wstr);
-
-       // remove trailing zeros
-       int decimal = 5;
-       while(decimal > 0 && *wstr == '0') {
-               *wstr-- = '\0';
-               decimal--;
-       }
-
-       // terminate it, one position to the right
-       // to let space for a dot
-       wstr[2] = '\0';
-
-       // make space for the dot
-       int i;
-       for(i = 0; i < decimal ;i++) {
-               wstr[1] = wstr[0];
-               wstr--;
-       }
-
-       // put the dot
-       if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
-       else wstr[1] = '.';
-
-       // return the buffer length
-       return (int) ((wstr - str) + 2 + decimal );
+    // wstr--;
+    // strreverse(str, wstr);
+
+    // remove trailing zeros
+    int decimal = 5;
+    while(decimal > 0 && *wstr == '0') {
+        *wstr-- = '\0';
+        decimal--;
+    }
+
+    // terminate it, one position to the right
+    // to let space for a dot
+    wstr[2] = '\0';
+
+    // make space for the dot
+    int i;
+    for(i = 0; i < decimal ;i++) {
+        wstr[1] = wstr[0];
+        wstr--;
+    }
+
+    // put the dot
+    if(wstr[2] == '\0') { wstr[1] = '\0'; decimal--; }
+    else wstr[1] = '.';
+
+    // return the buffer length
+    return (int) ((wstr - str) + 2 + decimal );
 }
index 1b7da79f17026cc6086914f558c3927a22a13d4d..74d24a322719ef6384d76f22300943b8aa52addb 100644 (file)
@@ -1,5 +1,3 @@
-#include <stdint.h>
-
 #ifndef NETDATA_STORAGE_NUMBER_H
 #define NETDATA_STORAGE_NUMBER_H
 
@@ -19,15 +17,15 @@ typedef long double collected_number;
 typedef uint32_t storage_number;
 #define STORAGE_NUMBER_FORMAT "%u"
 
-#define SN_NOT_EXISTS          (0x0 << 24)
-#define SN_EXISTS                      (0x1 << 24)
-#define SN_EXISTS_RESET                (0x2 << 24)
-#define SN_EXISTS_UNDEF1       (0x3 << 24)
-#define SN_EXISTS_UNDEF2       (0x4 << 24)
-#define SN_EXISTS_UNDEF3       (0x5 << 24)
-#define SN_EXISTS_UNDEF4       (0x6 << 24)
+#define SN_NOT_EXISTS       (0x0 << 24)
+#define SN_EXISTS           (0x1 << 24)
+#define SN_EXISTS_RESET     (0x2 << 24)
+#define SN_EXISTS_UNDEF1    (0x3 << 24)
+#define SN_EXISTS_UNDEF2    (0x4 << 24)
+#define SN_EXISTS_UNDEF3    (0x5 << 24)
+#define SN_EXISTS_UNDEF4    (0x6 << 24)
 
-#define SN_FLAGS_MASK          (~(0x6 << 24))
+#define SN_FLAGS_MASK       (~(0x6 << 24))
 
 // extract the flags
 #define get_storage_number_flags(value) ((((storage_number)value) & (1 << 24)) | (((storage_number)value) & (2 << 24)) | (((storage_number)value) & (4 << 24)))
index 5bc408c95a58ae79afbbb876a2fb03a48e0254d2..616b806f678ef3136a573ea39cc7db15c3be7821 100644 (file)
@@ -1,23 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <string.h>
-#include <sys/stat.h>
-
 #include "common.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "log.h"
-#include "rrd.h"
-#include "main.h"
-#include "popen.h"
-#include "proc_self_mountinfo.h"
 
 // ----------------------------------------------------------------------------
 // cgroup globals
@@ -39,171 +20,171 @@ static int cgroup_root_max = 500;
 static int cgroup_max_depth = 0;
 
 void read_cgroup_plugin_configuration() {
-       cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);
-
-       cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
-       cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
-       cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
-       cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
-
-       char filename[FILENAME_MAX + 1], *s;
-       struct mountinfo *mi, *root = mountinfo_read();
-
-       mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
-       if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
-       if(!mi) {
-               error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
-               s = "/sys/fs/cgroup/cpuacct";
-       }
-       else s = mi->mount_point;
-       snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
-       cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
-
-       mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
-       if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
-       if(!mi) {
-               error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
-               s = "/sys/fs/cgroup/blkio";
-       }
-       else s = mi->mount_point;
-       snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
-       cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
-
-       mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
-       if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
-       if(!mi) {
-               error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
-               s = "/sys/fs/cgroup/memory";
-       }
-       else s = mi->mount_point;
-       snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
-       cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
-
-       mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
-       if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
-       if(!mi) {
-               error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
-               s = "/sys/fs/cgroup/devices";
-       }
-       else s = mi->mount_point;
-       snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
-       cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
-
-       cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
-       cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
-
-       cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);
-
-       mountinfo_free(root);
+    cgroup_check_for_new_every = config_get_number("plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);
+
+    cgroup_enable_cpuacct_stat = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct stat", cgroup_enable_cpuacct_stat);
+    cgroup_enable_cpuacct_usage = config_get_boolean_ondemand("plugin:cgroups", "enable cpuacct usage", cgroup_enable_cpuacct_usage);
+    cgroup_enable_memory = config_get_boolean_ondemand("plugin:cgroups", "enable memory", cgroup_enable_memory);
+    cgroup_enable_blkio = config_get_boolean_ondemand("plugin:cgroups", "enable blkio", cgroup_enable_blkio);
+
+    char filename[FILENAME_MAX + 1], *s;
+    struct mountinfo *mi, *root = mountinfo_read();
+
+    mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
+    if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
+    if(!mi) {
+        error("Cannot find cgroup cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
+        s = "/sys/fs/cgroup/cpuacct";
+    }
+    else s = mi->mount_point;
+    snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+    cgroup_cpuacct_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/cpuacct", filename);
+
+    mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
+    if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
+    if(!mi) {
+        error("Cannot find cgroup blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
+        s = "/sys/fs/cgroup/blkio";
+    }
+    else s = mi->mount_point;
+    snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+    cgroup_blkio_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/blkio", filename);
+
+    mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
+    if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
+    if(!mi) {
+        error("Cannot find cgroup memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
+        s = "/sys/fs/cgroup/memory";
+    }
+    else s = mi->mount_point;
+    snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+    cgroup_memory_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/memory", filename);
+
+    mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "devices");
+    if(!mi) mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "devices");
+    if(!mi) {
+        error("Cannot find cgroup devices mountinfo. Assuming default: /sys/fs/cgroup/devices");
+        s = "/sys/fs/cgroup/devices";
+    }
+    else s = mi->mount_point;
+    snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, s);
+    cgroup_devices_base = config_get("plugin:cgroups", "path to /sys/fs/cgroup/devices", filename);
+
+    cgroup_root_max = config_get_number("plugin:cgroups", "max cgroups to allow", cgroup_root_max);
+    cgroup_max_depth = config_get_number("plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
+
+    cgroup_enable_new_cgroups_detected_at_runtime = config_get_boolean("plugin:cgroups", "enable new cgroups detected at run time", cgroup_enable_new_cgroups_detected_at_runtime);
+
+    mountinfo_free(root);
 }
 
 // ----------------------------------------------------------------------------
 // cgroup objects
 
 struct blkio {
-       int updated;
+    int updated;
 
-       char *filename;
+    char *filename;
 
-       unsigned long long Read;
-       unsigned long long Write;
+    unsigned long long Read;
+    unsigned long long Write;
 /*
-       unsigned long long Sync;
-       unsigned long long Async;
-       unsigned long long Total;
+    unsigned long long Sync;
+    unsigned long long Async;
+    unsigned long long Total;
 */
 };
 
 // https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt
 struct memory {
-       int updated;
-
-       char *filename;
-
-       int has_dirty_swap;
-
-       unsigned long long cache;
-       unsigned long long rss;
-       unsigned long long rss_huge;
-       unsigned long long mapped_file;
-       unsigned long long writeback;
-       unsigned long long dirty;
-       unsigned long long swap;
-       unsigned long long pgpgin;
-       unsigned long long pgpgout;
-       unsigned long long pgfault;
-       unsigned long long pgmajfault;
+    int updated;
+
+    char *filename;
+
+    int has_dirty_swap;
+
+    unsigned long long cache;
+    unsigned long long rss;
+    unsigned long long rss_huge;
+    unsigned long long mapped_file;
+    unsigned long long writeback;
+    unsigned long long dirty;
+    unsigned long long swap;
+    unsigned long long pgpgin;
+    unsigned long long pgpgout;
+    unsigned long long pgfault;
+    unsigned long long pgmajfault;
 /*
-       unsigned long long inactive_anon;
-       unsigned long long active_anon;
-       unsigned long long inactive_file;
-       unsigned long long active_file;
-       unsigned long long unevictable;
-       unsigned long long hierarchical_memory_limit;
-       unsigned long long total_cache;
-       unsigned long long total_rss;
-       unsigned long long total_rss_huge;
-       unsigned long long total_mapped_file;
-       unsigned long long total_writeback;
-       unsigned long long total_dirty;
-       unsigned long long total_swap;
-       unsigned long long total_pgpgin;
-       unsigned long long total_pgpgout;
-       unsigned long long total_pgfault;
-       unsigned long long total_pgmajfault;
-       unsigned long long total_inactive_anon;
-       unsigned long long total_active_anon;
-       unsigned long long total_inactive_file;
-       unsigned long long total_active_file;
-       unsigned long long total_unevictable;
+    unsigned long long inactive_anon;
+    unsigned long long active_anon;
+    unsigned long long inactive_file;
+    unsigned long long active_file;
+    unsigned long long unevictable;
+    unsigned long long hierarchical_memory_limit;
+    unsigned long long total_cache;
+    unsigned long long total_rss;
+    unsigned long long total_rss_huge;
+    unsigned long long total_mapped_file;
+    unsigned long long total_writeback;
+    unsigned long long total_dirty;
+    unsigned long long total_swap;
+    unsigned long long total_pgpgin;
+    unsigned long long total_pgpgout;
+    unsigned long long total_pgfault;
+    unsigned long long total_pgmajfault;
+    unsigned long long total_inactive_anon;
+    unsigned long long total_active_anon;
+    unsigned long long total_inactive_file;
+    unsigned long long total_active_file;
+    unsigned long long total_unevictable;
 */
 };
 
 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
 struct cpuacct_stat {
-       int updated;
+    int updated;
 
-       char *filename;
+    char *filename;
 
-       unsigned long long user;
-       unsigned long long system;
+    unsigned long long user;
+    unsigned long long system;
 };
 
 // https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt
 struct cpuacct_usage {
-       int updated;
+    int updated;
 
-       char *filename;
+    char *filename;
 
-       unsigned int cpus;
-       unsigned long long *cpu_percpu;
+    unsigned int cpus;
+    unsigned long long *cpu_percpu;
 };
 
 struct cgroup {
-       int available;          // found in the filesystem
-       int enabled;            // enabled in the config
+    int available;      // found in the filesystem
+    int enabled;        // enabled in the config
 
-       char *id;
-       uint32_t hash;
+    char *id;
+    uint32_t hash;
 
-       char *chart_id;
-       char *chart_title;
+    char *chart_id;
+    char *chart_title;
 
-       struct cpuacct_stat cpuacct_stat;
-       struct cpuacct_usage cpuacct_usage;
+    struct cpuacct_stat cpuacct_stat;
+    struct cpuacct_usage cpuacct_usage;
 
-       struct memory memory;
+    struct memory memory;
 
-       struct blkio io_service_bytes;                          // bytes
-       struct blkio io_serviced;                                       // operations
+    struct blkio io_service_bytes;              // bytes
+    struct blkio io_serviced;                   // operations
 
-       struct blkio throttle_io_service_bytes;         // bytes
-       struct blkio throttle_io_serviced;                      // operations
+    struct blkio throttle_io_service_bytes;     // bytes
+    struct blkio throttle_io_serviced;          // operations
 
-       struct blkio io_merged;                                         // operations
-       struct blkio io_queued;                                         // operations
+    struct blkio io_merged;                     // operations
+    struct blkio io_queued;                     // operations
 
-       struct cgroup *next;
+    struct cgroup *next;
 
 } *cgroup_root = NULL;
 
@@ -211,393 +192,389 @@ struct cgroup {
 // read values from /sys
 
 void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
-       static procfile *ff = NULL;
+    static procfile *ff = NULL;
 
-       static uint32_t user_hash = 0;
-       static uint32_t system_hash = 0;
+    static uint32_t user_hash = 0;
+    static uint32_t system_hash = 0;
 
-       if(unlikely(user_hash == 0)) {
-               user_hash = simple_hash("user");
-               system_hash = simple_hash("system");
-       }
+    if(unlikely(user_hash == 0)) {
+        user_hash = simple_hash("user");
+        system_hash = simple_hash("system");
+    }
 
-       cp->updated = 0;
-       if(cp->filename) {
-               ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
-               if(!ff) return;
+    cp->updated = 0;
+    if(cp->filename) {
+        ff = procfile_reopen(ff, cp->filename, NULL, PROCFILE_FLAG_DEFAULT);
+        if(!ff) return;
 
-               ff = procfile_readall(ff);
-               if(!ff) return;
+        ff = procfile_readall(ff);
+        if(!ff) return;
 
-               unsigned long i, lines = procfile_lines(ff);
+        unsigned long i, lines = procfile_lines(ff);
 
-               if(lines < 1) {
-                       error("File '%s' should have 1+ lines.", cp->filename);
-                       return;
-               }
+        if(lines < 1) {
+            error("File '%s' should have 1+ lines.", cp->filename);
+            return;
+        }
 
-               for(i = 0; i < lines ; i++) {
-                       char *s = procfile_lineword(ff, i, 0);
-                       uint32_t hash = simple_hash(s);
+        for(i = 0; i < lines ; i++) {
+            char *s = procfile_lineword(ff, i, 0);
+            uint32_t hash = simple_hash(s);
 
-                       if(hash == user_hash && !strcmp(s, "user"))
-                               cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            if(hash == user_hash && !strcmp(s, "user"))
+                cp->user = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == system_hash && !strcmp(s, "system"))
-                               cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
-               }
+            else if(hash == system_hash && !strcmp(s, "system"))
+                cp->system = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+        }
 
-               cp->updated = 1;
+        cp->updated = 1;
 
-               // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
-       }
+        // fprintf(stderr, "READ '%s': user: %llu, system: %llu\n", cp->filename, cp->user, cp->system);
+    }
 }
 
 void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
-       static procfile *ff = NULL;
-
-       ca->updated = 0;
-       if(ca->filename) {
-               ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
-               if(!ff) return;
-
-               ff = procfile_readall(ff);
-               if(!ff) return;
-
-               if(procfile_lines(ff) < 1) {
-                       error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff));
-                       return;
-               }
-
-               unsigned long i = procfile_linewords(ff, 0);
-               if(i <= 0) return;
-
-               // we may have 1 more CPU reported
-               while(i > 0) {
-                       char *s = procfile_lineword(ff, 0, i - 1);
-                       if(!*s) i--;
-                       else break;
-               }
-
-               if(i != ca->cpus) {
-                       free(ca->cpu_percpu);
-
-                       ca->cpu_percpu = malloc(sizeof(unsigned long long) * i);
-                       if(!ca->cpu_percpu)
-                               fatal("Cannot allocate memory (%zu bytes)", sizeof(unsigned long long) * i);
-
-                       ca->cpus = i;
-               }
-
-               for(i = 0; i < ca->cpus ;i++) {
-                       ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
-                       // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
-               }
-
-               ca->updated = 1;
-       }
+    static procfile *ff = NULL;
+
+    ca->updated = 0;
+    if(ca->filename) {
+        ff = procfile_reopen(ff, ca->filename, NULL, PROCFILE_FLAG_DEFAULT);
+        if(!ff) return;
+
+        ff = procfile_readall(ff);
+        if(!ff) return;
+
+        if(procfile_lines(ff) < 1) {
+            error("File '%s' should have 1+ lines but has %u.", ca->filename, procfile_lines(ff));
+            return;
+        }
+
+        unsigned long i = procfile_linewords(ff, 0);
+        if(i <= 0) return;
+
+        // we may have 1 more CPU reported
+        while(i > 0) {
+            char *s = procfile_lineword(ff, 0, i - 1);
+            if(!*s) i--;
+            else break;
+        }
+
+        if(i != ca->cpus) {
+            freez(ca->cpu_percpu);
+            ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
+            ca->cpus = (unsigned int)i;
+        }
+
+        for(i = 0; i < ca->cpus ;i++) {
+            ca->cpu_percpu[i] = strtoull(procfile_lineword(ff, 0, i), NULL, 10);
+            // fprintf(stderr, "READ '%s': cpu%d/%d: %llu ('%s')\n", ca->filename, i, ca->cpus, ca->cpu_percpu[i], procfile_lineword(ff, 0, i));
+        }
+
+        ca->updated = 1;
+    }
 }
 
 void cgroup_read_blkio(struct blkio *io) {
-       static procfile *ff = NULL;
+    static procfile *ff = NULL;
 
-       static uint32_t Read_hash = 0;
-       static uint32_t Write_hash = 0;
+    static uint32_t Read_hash = 0;
+    static uint32_t Write_hash = 0;
 /*
-       static uint32_t Sync_hash = 0;
-       static uint32_t Async_hash = 0;
-       static uint32_t Total_hash = 0;
+    static uint32_t Sync_hash = 0;
+    static uint32_t Async_hash = 0;
+    static uint32_t Total_hash = 0;
 */
 
-       if(unlikely(Read_hash == 0)) {
-               Read_hash = simple_hash("Read");
-               Write_hash = simple_hash("Write");
+    if(unlikely(Read_hash == 0)) {
+        Read_hash = simple_hash("Read");
+        Write_hash = simple_hash("Write");
 /*
-               Sync_hash = simple_hash("Sync");
-               Async_hash = simple_hash("Async");
-               Total_hash = simple_hash("Total");
+        Sync_hash = simple_hash("Sync");
+        Async_hash = simple_hash("Async");
+        Total_hash = simple_hash("Total");
 */
-       }
+    }
 
-       io->updated = 0;
-       if(io->filename) {
-               ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
-               if(!ff) return;
+    io->updated = 0;
+    if(io->filename) {
+        ff = procfile_reopen(ff, io->filename, NULL, PROCFILE_FLAG_DEFAULT);
+        if(!ff) return;
 
-               ff = procfile_readall(ff);
-               if(!ff) return;
+        ff = procfile_readall(ff);
+        if(!ff) return;
 
-               unsigned long i, lines = procfile_lines(ff);
+        unsigned long i, lines = procfile_lines(ff);
 
-               if(lines < 1) {
-                       error("File '%s' should have 1+ lines.", io->filename);
-                       return;
-               }
+        if(lines < 1) {
+            error("File '%s' should have 1+ lines.", io->filename);
+            return;
+        }
 
-               io->Read = 0;
-               io->Write = 0;
+        io->Read = 0;
+        io->Write = 0;
 /*
-               io->Sync = 0;
-               io->Async = 0;
-               io->Total = 0;
+        io->Sync = 0;
+        io->Async = 0;
+        io->Total = 0;
 */
 
-               for(i = 0; i < lines ; i++) {
-                       char *s = procfile_lineword(ff, i, 1);
-                       uint32_t hash = simple_hash(s);
+        for(i = 0; i < lines ; i++) {
+            char *s = procfile_lineword(ff, i, 1);
+            uint32_t hash = simple_hash(s);
 
-                       if(hash == Read_hash && !strcmp(s, "Read"))
-                               io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+            if(hash == Read_hash && !strcmp(s, "Read"))
+                io->Read += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
 
-                       else if(hash == Write_hash && !strcmp(s, "Write"))
-                               io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+            else if(hash == Write_hash && !strcmp(s, "Write"))
+                io->Write += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
 
 /*
-                       else if(hash == Sync_hash && !strcmp(s, "Sync"))
-                               io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+            else if(hash == Sync_hash && !strcmp(s, "Sync"))
+                io->Sync += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
 
-                       else if(hash == Async_hash && !strcmp(s, "Async"))
-                               io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+            else if(hash == Async_hash && !strcmp(s, "Async"))
+                io->Async += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
 
-                       else if(hash == Total_hash && !strcmp(s, "Total"))
-                               io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
+            else if(hash == Total_hash && !strcmp(s, "Total"))
+                io->Total += strtoull(procfile_lineword(ff, i, 2), NULL, 10);
 */
-               }
+        }
 
-               io->updated = 1;
-               // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
-       }
+        io->updated = 1;
+        // fprintf(stderr, "READ '%s': Read: %llu, Write: %llu, Sync: %llu, Async: %llu, Total: %llu\n", io->filename, io->Read, io->Write, io->Sync, io->Async, io->Total);
+    }
 }
 
 void cgroup_read_memory(struct memory *mem) {
-       static procfile *ff = NULL;
-
-       static uint32_t cache_hash = 0;
-       static uint32_t rss_hash = 0;
-       static uint32_t rss_huge_hash = 0;
-       static uint32_t mapped_file_hash = 0;
-       static uint32_t writeback_hash = 0;
-       static uint32_t dirty_hash = 0;
-       static uint32_t swap_hash = 0;
-       static uint32_t pgpgin_hash = 0;
-       static uint32_t pgpgout_hash = 0;
-       static uint32_t pgfault_hash = 0;
-       static uint32_t pgmajfault_hash = 0;
+    static procfile *ff = NULL;
+
+    static uint32_t cache_hash = 0;
+    static uint32_t rss_hash = 0;
+    static uint32_t rss_huge_hash = 0;
+    static uint32_t mapped_file_hash = 0;
+    static uint32_t writeback_hash = 0;
+    static uint32_t dirty_hash = 0;
+    static uint32_t swap_hash = 0;
+    static uint32_t pgpgin_hash = 0;
+    static uint32_t pgpgout_hash = 0;
+    static uint32_t pgfault_hash = 0;
+    static uint32_t pgmajfault_hash = 0;
 /*
-       static uint32_t inactive_anon_hash = 0;
-       static uint32_t active_anon_hash = 0;
-       static uint32_t inactive_file_hash = 0;
-       static uint32_t active_file_hash = 0;
-       static uint32_t unevictable_hash = 0;
-       static uint32_t hierarchical_memory_limit_hash = 0;
-       static uint32_t total_cache_hash = 0;
-       static uint32_t total_rss_hash = 0;
-       static uint32_t total_rss_huge_hash = 0;
-       static uint32_t total_mapped_file_hash = 0;
-       static uint32_t total_writeback_hash = 0;
-       static uint32_t total_dirty_hash = 0;
-       static uint32_t total_swap_hash = 0;
-       static uint32_t total_pgpgin_hash = 0;
-       static uint32_t total_pgpgout_hash = 0;
-       static uint32_t total_pgfault_hash = 0;
-       static uint32_t total_pgmajfault_hash = 0;
-       static uint32_t total_inactive_anon_hash = 0;
-       static uint32_t total_active_anon_hash = 0;
-       static uint32_t total_inactive_file_hash = 0;
-       static uint32_t total_active_file_hash = 0;
-       static uint32_t total_unevictable_hash = 0;
+    static uint32_t inactive_anon_hash = 0;
+    static uint32_t active_anon_hash = 0;
+    static uint32_t inactive_file_hash = 0;
+    static uint32_t active_file_hash = 0;
+    static uint32_t unevictable_hash = 0;
+    static uint32_t hierarchical_memory_limit_hash = 0;
+    static uint32_t total_cache_hash = 0;
+    static uint32_t total_rss_hash = 0;
+    static uint32_t total_rss_huge_hash = 0;
+    static uint32_t total_mapped_file_hash = 0;
+    static uint32_t total_writeback_hash = 0;
+    static uint32_t total_dirty_hash = 0;
+    static uint32_t total_swap_hash = 0;
+    static uint32_t total_pgpgin_hash = 0;
+    static uint32_t total_pgpgout_hash = 0;
+    static uint32_t total_pgfault_hash = 0;
+    static uint32_t total_pgmajfault_hash = 0;
+    static uint32_t total_inactive_anon_hash = 0;
+    static uint32_t total_active_anon_hash = 0;
+    static uint32_t total_inactive_file_hash = 0;
+    static uint32_t total_active_file_hash = 0;
+    static uint32_t total_unevictable_hash = 0;
 */
-       if(unlikely(cache_hash == 0)) {
-               cache_hash = simple_hash("cache");
-               rss_hash = simple_hash("rss");
-               rss_huge_hash = simple_hash("rss_huge");
-               mapped_file_hash = simple_hash("mapped_file");
-               writeback_hash = simple_hash("writeback");
-               dirty_hash = simple_hash("dirty");
-               swap_hash = simple_hash("swap");
-               pgpgin_hash = simple_hash("pgpgin");
-               pgpgout_hash = simple_hash("pgpgout");
-               pgfault_hash = simple_hash("pgfault");
-               pgmajfault_hash = simple_hash("pgmajfault");
+    if(unlikely(cache_hash == 0)) {
+        cache_hash = simple_hash("cache");
+        rss_hash = simple_hash("rss");
+        rss_huge_hash = simple_hash("rss_huge");
+        mapped_file_hash = simple_hash("mapped_file");
+        writeback_hash = simple_hash("writeback");
+        dirty_hash = simple_hash("dirty");
+        swap_hash = simple_hash("swap");
+        pgpgin_hash = simple_hash("pgpgin");
+        pgpgout_hash = simple_hash("pgpgout");
+        pgfault_hash = simple_hash("pgfault");
+        pgmajfault_hash = simple_hash("pgmajfault");
 /*
-               inactive_anon_hash = simple_hash("inactive_anon");
-               active_anon_hash = simple_hash("active_anon");
-               inactive_file_hash = simple_hash("inactive_file");
-               active_file_hash = simple_hash("active_file");
-               unevictable_hash = simple_hash("unevictable");
-               hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
-               total_cache_hash = simple_hash("total_cache");
-               total_rss_hash = simple_hash("total_rss");
-               total_rss_huge_hash = simple_hash("total_rss_huge");
-               total_mapped_file_hash = simple_hash("total_mapped_file");
-               total_writeback_hash = simple_hash("total_writeback");
-               total_dirty_hash = simple_hash("total_dirty");
-               total_swap_hash = simple_hash("total_swap");
-               total_pgpgin_hash = simple_hash("total_pgpgin");
-               total_pgpgout_hash = simple_hash("total_pgpgout");
-               total_pgfault_hash = simple_hash("total_pgfault");
-               total_pgmajfault_hash = simple_hash("total_pgmajfault");
-               total_inactive_anon_hash = simple_hash("total_inactive_anon");
-               total_active_anon_hash = simple_hash("total_active_anon");
-               total_inactive_file_hash = simple_hash("total_inactive_file");
-               total_active_file_hash = simple_hash("total_active_file");
-               total_unevictable_hash = simple_hash("total_unevictable");
+        inactive_anon_hash = simple_hash("inactive_anon");
+        active_anon_hash = simple_hash("active_anon");
+        inactive_file_hash = simple_hash("inactive_file");
+        active_file_hash = simple_hash("active_file");
+        unevictable_hash = simple_hash("unevictable");
+        hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit");
+        total_cache_hash = simple_hash("total_cache");
+        total_rss_hash = simple_hash("total_rss");
+        total_rss_huge_hash = simple_hash("total_rss_huge");
+        total_mapped_file_hash = simple_hash("total_mapped_file");
+        total_writeback_hash = simple_hash("total_writeback");
+        total_dirty_hash = simple_hash("total_dirty");
+        total_swap_hash = simple_hash("total_swap");
+        total_pgpgin_hash = simple_hash("total_pgpgin");
+        total_pgpgout_hash = simple_hash("total_pgpgout");
+        total_pgfault_hash = simple_hash("total_pgfault");
+        total_pgmajfault_hash = simple_hash("total_pgmajfault");
+        total_inactive_anon_hash = simple_hash("total_inactive_anon");
+        total_active_anon_hash = simple_hash("total_active_anon");
+        total_inactive_file_hash = simple_hash("total_inactive_file");
+        total_active_file_hash = simple_hash("total_active_file");
+        total_unevictable_hash = simple_hash("total_unevictable");
 */
-       }
+    }
 
-       mem->updated = 0;
-       if(mem->filename) {
-               ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
-               if(!ff) return;
+    mem->updated = 0;
+    if(mem->filename) {
+        ff = procfile_reopen(ff, mem->filename, NULL, PROCFILE_FLAG_DEFAULT);
+        if(!ff) return;
 
-               ff = procfile_readall(ff);
-               if(!ff) return;
+        ff = procfile_readall(ff);
+        if(!ff) return;
 
-               unsigned long i, lines = procfile_lines(ff);
+        unsigned long i, lines = procfile_lines(ff);
 
-               if(lines < 1) {
-                       error("File '%s' should have 1+ lines.", mem->filename);
-                       return;
-               }
+        if(lines < 1) {
+            error("File '%s' should have 1+ lines.", mem->filename);
+            return;
+        }
 
-               for(i = 0; i < lines ; i++) {
-                       char *s = procfile_lineword(ff, i, 0);
-                       uint32_t hash = simple_hash(s);
+        for(i = 0; i < lines ; i++) {
+            char *s = procfile_lineword(ff, i, 0);
+            uint32_t hash = simple_hash(s);
 
-                       if(hash == cache_hash && !strcmp(s, "cache"))
-                               mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            if(hash == cache_hash && !strcmp(s, "cache"))
+                mem->cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == rss_hash && !strcmp(s, "rss"))
-                               mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == rss_hash && !strcmp(s, "rss"))
+                mem->rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
-                               mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == rss_huge_hash && !strcmp(s, "rss_huge"))
+                mem->rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
-                               mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == mapped_file_hash && !strcmp(s, "mapped_file"))
+                mem->mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == writeback_hash && !strcmp(s, "writeback"))
-                               mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == writeback_hash && !strcmp(s, "writeback"))
+                mem->writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == dirty_hash && !strcmp(s, "dirty")) {
-                               mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
-                               mem->has_dirty_swap = 1;
-                       }
+            else if(hash == dirty_hash && !strcmp(s, "dirty")) {
+                mem->dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+                mem->has_dirty_swap = 1;
+            }
 
-                       else if(hash == swap_hash && !strcmp(s, "swap")) {
-                               mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
-                               mem->has_dirty_swap = 1;
-                       }
+            else if(hash == swap_hash && !strcmp(s, "swap")) {
+                mem->swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+                mem->has_dirty_swap = 1;
+            }
 
-                       else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
-                               mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == pgpgin_hash && !strcmp(s, "pgpgin"))
+                mem->pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
-                               mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == pgpgout_hash && !strcmp(s, "pgpgout"))
+                mem->pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
-                               mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == pgfault_hash && !strcmp(s, "pgfault"))
+                mem->pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
-                               mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))
+                mem->pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
 /*
-                       else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
-                               mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == inactive_anon_hash && !strcmp(s, "inactive_anon"))
+                mem->inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
-                               mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == active_anon_hash && !strcmp(s, "active_anon"))
+                mem->active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
-                               mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == inactive_file_hash && !strcmp(s, "inactive_file"))
+                mem->inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == active_file_hash && !strcmp(s, "active_file"))
-                               mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == active_file_hash && !strcmp(s, "active_file"))
+                mem->active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
-                               mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == unevictable_hash && !strcmp(s, "unevictable"))
+                mem->unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
-                               mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == hierarchical_memory_limit_hash && !strcmp(s, "hierarchical_memory_limit"))
+                mem->hierarchical_memory_limit = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-               else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
-                               mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_cache_hash && !strcmp(s, "total_cache"))
+                mem->total_cache = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
-                               mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_rss_hash && !strcmp(s, "total_rss"))
+                mem->total_rss = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
-                               mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_rss_huge_hash && !strcmp(s, "total_rss_huge"))
+                mem->total_rss_huge = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
-                               mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_mapped_file_hash && !strcmp(s, "total_mapped_file"))
+                mem->total_mapped_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
-                               mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_writeback_hash && !strcmp(s, "total_writeback"))
+                mem->total_writeback = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
-                               mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_dirty_hash && !strcmp(s, "total_dirty"))
+                mem->total_dirty = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
-                               mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_swap_hash && !strcmp(s, "total_swap"))
+                mem->total_swap = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
-                               mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_pgpgin_hash && !strcmp(s, "total_pgpgin"))
+                mem->total_pgpgin = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
-                               mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_pgpgout_hash && !strcmp(s, "total_pgpgout"))
+                mem->total_pgpgout = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
-                               mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_pgfault_hash && !strcmp(s, "total_pgfault"))
+                mem->total_pgfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
-                               mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_pgmajfault_hash && !strcmp(s, "total_pgmajfault"))
+                mem->total_pgmajfault = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
-                               mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_inactive_anon_hash && !strcmp(s, "total_inactive_anon"))
+                mem->total_inactive_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
-                               mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_active_anon_hash && !strcmp(s, "total_active_anon"))
+                mem->total_active_anon = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
-                               mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_inactive_file_hash && !strcmp(s, "total_inactive_file"))
+                mem->total_inactive_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
-                               mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_active_file_hash && !strcmp(s, "total_active_file"))
+                mem->total_active_file = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 
-                       else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
-                               mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
+            else if(hash == total_unevictable_hash && !strcmp(s, "total_unevictable"))
+                mem->total_unevictable = strtoull(procfile_lineword(ff, i, 1), NULL, 10);
 */
-               }
+        }
 
-               // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
+        // fprintf(stderr, "READ: '%s', cache: %llu, rss: %llu, rss_huge: %llu, mapped_file: %llu, writeback: %llu, dirty: %llu, swap: %llu, pgpgin: %llu, pgpgout: %llu, pgfault: %llu, pgmajfault: %llu, inactive_anon: %llu, active_anon: %llu, inactive_file: %llu, active_file: %llu, unevictable: %llu, hierarchical_memory_limit: %llu, total_cache: %llu, total_rss: %llu, total_rss_huge: %llu, total_mapped_file: %llu, total_writeback: %llu, total_dirty: %llu, total_swap: %llu, total_pgpgin: %llu, total_pgpgout: %llu, total_pgfault: %llu, total_pgmajfault: %llu, total_inactive_anon: %llu, total_active_anon: %llu, total_inactive_file: %llu, total_active_file: %llu, total_unevictable: %llu\n", mem->filename, mem->cache, mem->rss, mem->rss_huge, mem->mapped_file, mem->writeback, mem->dirty, mem->swap, mem->pgpgin, mem->pgpgout, mem->pgfault, mem->pgmajfault, mem->inactive_anon, mem->active_anon, mem->inactive_file, mem->active_file, mem->unevictable, mem->hierarchical_memory_limit, mem->total_cache, mem->total_rss, mem->total_rss_huge, mem->total_mapped_file, mem->total_writeback, mem->total_dirty, mem->total_swap, mem->total_pgpgin, mem->total_pgpgout, mem->total_pgfault, mem->total_pgmajfault, mem->total_inactive_anon, mem->total_active_anon, mem->total_inactive_file, mem->total_active_file, mem->total_unevictable);
 
-               mem->updated = 1;
-       }
+        mem->updated = 1;
+    }
 }
 
 void cgroup_read(struct cgroup *cg) {
-       debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
-
-       cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
-       cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
-       cgroup_read_memory(&cg->memory);
-       cgroup_read_blkio(&cg->io_service_bytes);
-       cgroup_read_blkio(&cg->io_serviced);
-       cgroup_read_blkio(&cg->throttle_io_service_bytes);
-       cgroup_read_blkio(&cg->throttle_io_serviced);
-       cgroup_read_blkio(&cg->io_merged);
-       cgroup_read_blkio(&cg->io_queued);
+    debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
+
+    cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
+    cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
+    cgroup_read_memory(&cg->memory);
+    cgroup_read_blkio(&cg->io_service_bytes);
+    cgroup_read_blkio(&cg->io_serviced);
+    cgroup_read_blkio(&cg->throttle_io_service_bytes);
+    cgroup_read_blkio(&cg->throttle_io_serviced);
+    cgroup_read_blkio(&cg->io_merged);
+    cgroup_read_blkio(&cg->io_queued);
 }
 
 void read_all_cgroups(struct cgroup *root) {
-       debug(D_CGROUP, "reading metrics for all cgroups");
+    debug(D_CGROUP, "reading metrics for all cgroups");
 
-       struct cgroup *cg;
+    struct cgroup *cg;
 
-       for(cg = root; cg ; cg = cg->next)
-               if(cg->enabled && cg->available)
-                       cgroup_read(cg);
+    for(cg = root; cg ; cg = cg->next)
+        if(cg->enabled && cg->available)
+            cgroup_read(cg);
 }
 
 // ----------------------------------------------------------------------------
@@ -606,178 +583,167 @@ void read_all_cgroups(struct cgroup *root) {
 #define CGROUP_CHARTID_LINE_MAX 1024
 
 void cgroup_get_chart_id(struct cgroup *cg) {
-       debug(D_CGROUP, "getting the name of cgroup '%s'", cg->id);
-
-       pid_t cgroup_pid;
-       char buffer[CGROUP_CHARTID_LINE_MAX + 1];
+    debug(D_CGROUP, "getting the name of cgroup '%s'", cg->id);
 
-       snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'",
-                config_get("plugin:cgroups", "script to get cgroup names", PLUGINS_DIR "/cgroup-name.sh"), cg->chart_id);
+    pid_t cgroup_pid;
+    char buffer[CGROUP_CHARTID_LINE_MAX + 1];
 
-       debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
-       FILE *fp = mypopen(buffer, &cgroup_pid);
-       if(!fp) {
-               error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
-               return;
-       }
-       debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
-       char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
-       debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
-       mypclose(fp, cgroup_pid);
-       debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
+    snprintfz(buffer, CGROUP_CHARTID_LINE_MAX, "exec %s '%s'",
+             config_get("plugin:cgroups", "script to get cgroup names", PLUGINS_DIR "/cgroup-name.sh"), cg->chart_id);
 
-       if(s && *s && *s != '\n') {
-               debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
+    debug(D_CGROUP, "executing command '%s' for cgroup '%s'", buffer, cg->id);
+    FILE *fp = mypopen(buffer, &cgroup_pid);
+    if(!fp) {
+        error("CGROUP: Cannot popen(\"%s\", \"r\").", buffer);
+        return;
+    }
+    debug(D_CGROUP, "reading from command '%s' for cgroup '%s'", buffer, cg->id);
+    char *s = fgets(buffer, CGROUP_CHARTID_LINE_MAX, fp);
+    debug(D_CGROUP, "closing command for cgroup '%s'", cg->id);
+    mypclose(fp, cgroup_pid);
+    debug(D_CGROUP, "closed command for cgroup '%s'", cg->id);
 
-               trim(s);
+    if(s && *s && *s != '\n') {
+        debug(D_CGROUP, "cgroup '%s' should be renamed to '%s'", cg->id, s);
 
-               free(cg->chart_title);
-               cg->chart_title = strdup(s);
-               if(!cg->chart_title)
-                       fatal("CGROUP: Cannot allocate memory for chart name of cgroup '%s' chart name: '%s'", cg->id, s);
+        trim(s);
 
-               netdata_fix_chart_name(cg->chart_title);
+        freez(cg->chart_title);
+        cg->chart_title = strdupz(s);
+        netdata_fix_chart_name(cg->chart_title);
 
-               free(cg->chart_id);
-               cg->chart_id = strdup(s);
-               if(!cg->chart_id)
-                       fatal("CGROUP: Cannot allocate memory for chart id of cgroup '%s' chart id: '%s'", cg->id, s);
+        freez(cg->chart_id);
+        cg->chart_id = strdupz(s);
 
-               netdata_fix_chart_id(cg->chart_id);
+        netdata_fix_chart_id(cg->chart_id);
 
-               debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
-       }
-       else debug(D_CGROUP, "cgroup '%s' is not to be renamed (will be shown as '%s')", cg->id, cg->chart_id);
+        debug(D_CGROUP, "cgroup '%s' renamed to '%s' (title: '%s')", cg->id, cg->chart_id, cg->chart_title);
+    }
+    else debug(D_CGROUP, "cgroup '%s' is not to be renamed (will be shown as '%s')", cg->id, cg->chart_id);
 }
 
 struct cgroup *cgroup_add(const char *id) {
-       debug(D_CGROUP, "adding cgroup '%s'", id);
-
-       if(cgroup_root_count >= cgroup_root_max) {
-               info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
-               return NULL;
-       }
-
-       int def = cgroup_enable_new_cgroups_detected_at_runtime;
-       const char *chart_id = id;
-       if(!*chart_id) {
-               chart_id = "/";
-
-               // disable by default the root cgroup
-               def = 0;
-               debug(D_CGROUP, "cgroup '%s' is the root container (by default %s)", id, (def)?"enabled":"disabled");
-       }
-       else {
-               if(*chart_id == '/') chart_id++;
-
-               size_t len = strlen(chart_id);
-
-               // disable by default the parent cgroup
-               // for known cgroup managers
-               if(!strcmp(chart_id, "lxc") ||
-                               !strcmp(chart_id, "docker") ||
-                               !strcmp(chart_id, "libvirt") ||
-                               !strcmp(chart_id, "qemu") ||
-                               !strcmp(chart_id, "systemd") ||
-                               !strcmp(chart_id, "system.slice") ||
-                               !strcmp(chart_id, "machine.slice") ||
-                               !strcmp(chart_id, "init.scope") ||
-                               !strcmp(chart_id, "user") ||
-                               !strcmp(chart_id, "system") ||
-                               !strcmp(chart_id, "machine") ||
-                               // starts with them
-                               (len >  6 && !strncmp(chart_id, "user/", 6)) ||
-                               (len > 11 && !strncmp(chart_id, "user.slice/", 11)) ||
-                               // ends with them
-                               (len >  5 && !strncmp(&chart_id[len -  5], ".user", 5)) ||
-                               (len >  5 && !strncmp(&chart_id[len -  5], ".swap", 5)) ||
-                               (len >  6 && !strncmp(&chart_id[len -  6], ".slice", 6)) ||
-                               (len >  6 && !strncmp(&chart_id[len -  6], ".mount", 6)) ||
-                               (len >  8 && !strncmp(&chart_id[len -  8], ".session", 8)) ||
-                               (len >  8 && !strncmp(&chart_id[len -  8], ".service", 8)) ||
-                               (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10))
-                               ) {
-                       def = 0;
-                       debug(D_CGROUP, "cgroup '%s' is %s (by default)", id, (def)?"enabled":"disabled");
-               }
-       }
-
-       struct cgroup *cg = calloc(1, sizeof(struct cgroup));
-       if(!cg) fatal("Cannot allocate memory for cgroup '%s'", id);
-
-       debug(D_CGROUP, "adding cgroup '%s'", id);
-
-       cg->id = strdup(id);
-       if(!cg->id) fatal("Cannot allocate memory for cgroup '%s'", id);
-
-       cg->hash = simple_hash(cg->id);
-
-       cg->chart_id = strdup(chart_id);
-       if(!cg->chart_id) fatal("Cannot allocate memory for cgroup '%s'", id);
-
-       cg->chart_title = strdup(chart_id);
-       if(!cg->chart_title) fatal("Cannot allocate memory for cgroup '%s'", id);
-
-       if(!cgroup_root)
-               cgroup_root = cg;
-       else {
-               // append it
-               struct cgroup *e;
-               for(e = cgroup_root; e->next ;e = e->next) ;
-               e->next = cg;
-       }
-
-       cgroup_root_count++;
-
-       // fix the name by calling the external script
-       cgroup_get_chart_id(cg);
-
-       char option[FILENAME_MAX + 1];
-       snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
-       cg->enabled = config_get_boolean("plugin:cgroups", option, def);
-
-       debug(D_CGROUP, "Added cgroup '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
-
-       return cg;
+    debug(D_CGROUP, "adding cgroup '%s'", id);
+
+    if(cgroup_root_count >= cgroup_root_max) {
+        info("Maximum number of cgroups reached (%d). Not adding cgroup '%s'", cgroup_root_count, id);
+        return NULL;
+    }
+
+    int def = cgroup_enable_new_cgroups_detected_at_runtime;
+    const char *chart_id = id;
+    if(!*chart_id) {
+        chart_id = "/";
+
+        // disable by default the root cgroup
+        def = 0;
+        debug(D_CGROUP, "cgroup '%s' is the root container (by default %s)", id, (def)?"enabled":"disabled");
+    }
+    else {
+        if(*chart_id == '/') chart_id++;
+
+        size_t len = strlen(chart_id);
+
+        // disable by default the parent cgroup
+        // for known cgroup managers
+        if(!strcmp(chart_id, "lxc") ||
+                !strcmp(chart_id, "docker") ||
+                !strcmp(chart_id, "libvirt") ||
+                !strcmp(chart_id, "qemu") ||
+                !strcmp(chart_id, "systemd") ||
+                !strcmp(chart_id, "system.slice") ||
+                !strcmp(chart_id, "machine.slice") ||
+                !strcmp(chart_id, "init.scope") ||
+                !strcmp(chart_id, "user") ||
+                !strcmp(chart_id, "system") ||
+                !strcmp(chart_id, "machine") ||
+                // starts with them
+                (len >  6 && !strncmp(chart_id, "user/", 6)) ||
+                (len > 11 && !strncmp(chart_id, "user.slice/", 11)) ||
+                // ends with them
+                (len >  5 && !strncmp(&chart_id[len -  5], ".user", 5)) ||
+                (len >  5 && !strncmp(&chart_id[len -  5], ".swap", 5)) ||
+                (len >  6 && !strncmp(&chart_id[len -  6], ".slice", 6)) ||
+                (len >  6 && !strncmp(&chart_id[len -  6], ".mount", 6)) ||
+                (len >  8 && !strncmp(&chart_id[len -  8], ".session", 8)) ||
+                (len >  8 && !strncmp(&chart_id[len -  8], ".service", 8)) ||
+                (len > 10 && !strncmp(&chart_id[len - 10], ".partition", 10))
+                ) {
+            def = 0;
+            debug(D_CGROUP, "cgroup '%s' is %s (by default)", id, (def)?"enabled":"disabled");
+        }
+    }
+
+    struct cgroup *cg = callocz(1, sizeof(struct cgroup));
+
+    debug(D_CGROUP, "adding cgroup '%s'", id);
+
+    cg->id = strdupz(id);
+    cg->hash = simple_hash(cg->id);
+
+    cg->chart_id = strdupz(chart_id);
+    cg->chart_title = strdupz(chart_id);
+
+    if(!cgroup_root)
+        cgroup_root = cg;
+    else {
+        // append it
+        struct cgroup *e;
+        for(e = cgroup_root; e->next ;e = e->next) ;
+        e->next = cg;
+    }
+
+    cgroup_root_count++;
+
+    // fix the name by calling the external script
+    cgroup_get_chart_id(cg);
+
+    char option[FILENAME_MAX + 1];
+    snprintfz(option, FILENAME_MAX, "enable cgroup %s", cg->chart_title);
+    cg->enabled = config_get_boolean("plugin:cgroups", option, def);
+
+    debug(D_CGROUP, "Added cgroup '%s' with chart id '%s' and title '%s' as %s (default was %s)", cg->id, cg->chart_id, cg->chart_title, (cg->enabled)?"enabled":"disabled", (def)?"enabled":"disabled");
+
+    return cg;
 }
 
 void cgroup_free(struct cgroup *cg) {
-       debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
-
-       free(cg->cpuacct_usage.cpu_percpu);
-
-       free(cg->cpuacct_stat.filename);
-       free(cg->cpuacct_usage.filename);
-       free(cg->memory.filename);
-       free(cg->io_service_bytes.filename);
-       free(cg->io_serviced.filename);
-       free(cg->throttle_io_service_bytes.filename);
-       free(cg->throttle_io_serviced.filename);
-       free(cg->io_merged.filename);
-       free(cg->io_queued.filename);
-
-       free(cg->id);
-       free(cg->chart_id);
-       free(cg->chart_title);
-       free(cg);
-
-       cgroup_root_count--;
+    debug(D_CGROUP, "Removing cgroup '%s' with chart id '%s' (was %s and %s)", cg->id, cg->chart_id, (cg->enabled)?"enabled":"disabled", (cg->available)?"available":"not available");
+
+    freez(cg->cpuacct_usage.cpu_percpu);
+
+    freez(cg->cpuacct_stat.filename);
+    freez(cg->cpuacct_usage.filename);
+    freez(cg->memory.filename);
+    freez(cg->io_service_bytes.filename);
+    freez(cg->io_serviced.filename);
+    freez(cg->throttle_io_service_bytes.filename);
+    freez(cg->throttle_io_serviced.filename);
+    freez(cg->io_merged.filename);
+    freez(cg->io_queued.filename);
+
+    freez(cg->id);
+    freez(cg->chart_id);
+    freez(cg->chart_title);
+    freez(cg);
+
+    cgroup_root_count--;
 }
 
 // find if a given cgroup exists
 struct cgroup *cgroup_find(const char *id) {
-       debug(D_CGROUP, "searching for cgroup '%s'", id);
+    debug(D_CGROUP, "searching for cgroup '%s'", id);
 
-       uint32_t hash = simple_hash(id);
+    uint32_t hash = simple_hash(id);
 
-       struct cgroup *cg;
-       for(cg = cgroup_root; cg ; cg = cg->next) {
-               if(hash == cg->hash && strcmp(id, cg->id) == 0)
-                       break;
-       }
+    struct cgroup *cg;
+    for(cg = cgroup_root; cg ; cg = cg->next) {
+        if(hash == cg->hash && strcmp(id, cg->id) == 0)
+            break;
+    }
 
-       debug(D_CGROUP, "cgroup_find('%s') %s", id, (cg)?"found":"not found");
-       return cg;
+    debug(D_CGROUP, "cgroup_find('%s') %s", id, (cg)?"found":"not found");
+    return cg;
 }
 
 // ----------------------------------------------------------------------------
@@ -785,235 +751,232 @@ struct cgroup *cgroup_find(const char *id) {
 
 // callback for find_file_in_subdirs()
 void found_subdir_in_dir(const char *dir) {
-       debug(D_CGROUP, "examining cgroup dir '%s'", dir);
-
-       struct cgroup *cg = cgroup_find(dir);
-       if(!cg) {
-               if(*dir && cgroup_max_depth > 0) {
-                       int depth = 0;
-                       const char *s;
-
-                       for(s = dir; *s ;s++)
-                               if(unlikely(*s == '/'))
-                                       depth++;
-
-                       if(depth > cgroup_max_depth) {
-                               info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
-                               return;
-                       }
-               }
-               debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
-               cg = cgroup_add(dir);
-       }
-
-       if(cg) cg->available = 1;
+    debug(D_CGROUP, "examining cgroup dir '%s'", dir);
+
+    struct cgroup *cg = cgroup_find(dir);
+    if(!cg) {
+        if(*dir && cgroup_max_depth > 0) {
+            int depth = 0;
+            const char *s;
+
+            for(s = dir; *s ;s++)
+                if(unlikely(*s == '/'))
+                    depth++;
+
+            if(depth > cgroup_max_depth) {
+                info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
+                return;
+            }
+        }
+        debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
+        cg = cgroup_add(dir);
+    }
+
+    if(cg) cg->available = 1;
 }
 
 void find_dir_in_subdirs(const char *base, const char *this, void (*callback)(const char *)) {
-       debug(D_CGROUP, "searching for directories in '%s'", base);
-
-       int enabled = -1;
-       if(!this) this = base;
-       size_t dirlen = strlen(this), baselen = strlen(base);
-       const char *relative_path = &this[baselen];
-
-       DIR *dir = opendir(this);
-       if(!dir) {
-               error("Cannot read cgroups directory '%s'", base);
-               return;
-       }
-
-       callback(relative_path);
-
-       struct dirent *de = NULL;
-       while((de = readdir(dir))) {
-               if(de->d_type == DT_DIR
-                       && (
-                               (de->d_name[0] == '.' && de->d_name[1] == '\0')
-                               || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
-                               ))
-                       continue;
-
-               debug(D_CGROUP, "examining '%s/%s'", this, de->d_name);
-
-               if(de->d_type == DT_DIR) {
-                       if(enabled == -1) {
-                               const char *r = relative_path;
-                               if(*r == '\0') r = "/";
-                               else if (*r == '/') r++;
-
-                               // we check for this option here
-                               // so that the config will not have settings
-                               // for leaf directories
-                               char option[FILENAME_MAX + 1];
-                               snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
-                               option[FILENAME_MAX] = '\0';
-                               enabled = config_get_boolean("plugin:cgroups", option, 1);
-                       }
-
-                       if(enabled) {
-                               char *s = malloc(dirlen + strlen(de->d_name) + 2);
-                               if(s) {
-                                       strcpy(s, this);
-                                       strcat(s, "/");
-                                       strcat(s, de->d_name);
-                                       find_dir_in_subdirs(base, s, callback);
-                                       free(s);
-                               }
-                               else error("Cannot allocate memory.");
-                       }
-               }
-       }
-
-       closedir(dir);
+    debug(D_CGROUP, "searching for directories in '%s'", base);
+
+    int enabled = -1;
+    if(!this) this = base;
+    size_t dirlen = strlen(this), baselen = strlen(base);
+    const char *relative_path = &this[baselen];
+
+    DIR *dir = opendir(this);
+    if(!dir) {
+        error("Cannot read cgroups directory '%s'", base);
+        return;
+    }
+
+    callback(relative_path);
+
+    struct dirent *de = NULL;
+    while((de = readdir(dir))) {
+        if(de->d_type == DT_DIR
+            && (
+                (de->d_name[0] == '.' && de->d_name[1] == '\0')
+                || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
+                ))
+            continue;
+
+        debug(D_CGROUP, "examining '%s/%s'", this, de->d_name);
+
+        if(de->d_type == DT_DIR) {
+            if(enabled == -1) {
+                const char *r = relative_path;
+                if(*r == '\0') r = "/";
+                else if (*r == '/') r++;
+
+                // we check for this option here
+                // so that the config will not have settings
+                // for leaf directories
+                char option[FILENAME_MAX + 1];
+                snprintfz(option, FILENAME_MAX, "search for cgroups under %s", r);
+                option[FILENAME_MAX] = '\0';
+                enabled = config_get_boolean("plugin:cgroups", option, 1);
+            }
+
+            if(enabled) {
+                char *s = mallocz(dirlen + strlen(de->d_name) + 2);
+                strcpy(s, this);
+                strcat(s, "/");
+                strcat(s, de->d_name);
+                find_dir_in_subdirs(base, s, callback);
+                freez(s);
+            }
+        }
+    }
+
+    closedir(dir);
 }
 
 void mark_all_cgroups_as_not_available() {
-       debug(D_CGROUP, "marking all cgroups as not available");
+    debug(D_CGROUP, "marking all cgroups as not available");
 
-       struct cgroup *cg;
+    struct cgroup *cg;
 
-       // mark all as not available
-       for(cg = cgroup_root; cg ; cg = cg->next)
-               cg->available = 0;
+    // mark all as not available
+    for(cg = cgroup_root; cg ; cg = cg->next)
+        cg->available = 0;
 }
 
 void cleanup_all_cgroups() {
-       struct cgroup *cg = cgroup_root, *last = NULL;
-
-       for(; cg ;) {
-               if(!cg->available) {
-
-                       if(!last)
-                               cgroup_root = cg->next;
-                       else
-                               last->next = cg->next;
-
-                       cgroup_free(cg);
-
-                       if(!last)
-                               cg = cgroup_root;
-                       else
-                               cg = last->next;
-               }
-               else {
-                       last = cg;
-                       cg = cg->next;
-               }
-       }
+    struct cgroup *cg = cgroup_root, *last = NULL;
+
+    for(; cg ;) {
+        if(!cg->available) {
+
+            if(!last)
+                cgroup_root = cg->next;
+            else
+                last->next = cg->next;
+
+            cgroup_free(cg);
+
+            if(!last)
+                cg = cgroup_root;
+            else
+                cg = last->next;
+        }
+        else {
+            last = cg;
+            cg = cg->next;
+        }
+    }
 }
 
 void find_all_cgroups() {
-       debug(D_CGROUP, "searching for cgroups");
-
-       mark_all_cgroups_as_not_available();
-
-       if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage)
-               find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir);
-
-       if(cgroup_enable_blkio)
-               find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir);
-
-       if(cgroup_enable_memory)
-               find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir);
-
-       if(cgroup_enable_devices)
-               find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir);
-
-       // remove any non-existing cgroups
-       cleanup_all_cgroups();
-
-       struct cgroup *cg;
-       struct stat buf;
-       for(cg = cgroup_root; cg ; cg = cg->next) {
-               // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
-
-               if(unlikely(!cg->available))
-                       continue;
-
-               debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
-
-               // check for newly added cgroups
-               // and update the filenames they read
-               char filename[FILENAME_MAX + 1];
-               if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
-                       snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
-                       if(stat(filename, &buf) != -1) {
-                               cg->cpuacct_stat.filename = strdup(filename);
-                               debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
-                       }
-                       else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-               }
-               if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
-                       snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
-                       if(stat(filename, &buf) != -1) {
-                               cg->cpuacct_usage.filename = strdup(filename);
-                               debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
-                       }
-                       else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-               }
-               if(cgroup_enable_memory && !cg->memory.filename) {
-                       snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
-                       if(stat(filename, &buf) != -1) {
-                               cg->memory.filename = strdup(filename);
-                               debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
-                       }
-                       else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-               }
-               if(cgroup_enable_blkio) {
-                       if(!cg->io_service_bytes.filename) {
-                               snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
-                               if(stat(filename, &buf) != -1) {
-                                       cg->io_service_bytes.filename = strdup(filename);
-                                       debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
-                               }
-                               else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-                       }
-                       if(!cg->io_serviced.filename) {
-                               snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
-                               if(stat(filename, &buf) != -1) {
-                                       cg->io_serviced.filename = strdup(filename);
-                                       debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
-                               }
-                               else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-                       }
-                       if(!cg->throttle_io_service_bytes.filename) {
-                               snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
-                               if(stat(filename, &buf) != -1) {
-                                       cg->throttle_io_service_bytes.filename = strdup(filename);
-                                       debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
-                               }
-                               else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-                       }
-                       if(!cg->throttle_io_serviced.filename) {
-                               snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
-                               if(stat(filename, &buf) != -1) {
-                                       cg->throttle_io_serviced.filename = strdup(filename);
-                                       debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
-                               }
-                               else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-                       }
-                       if(!cg->io_merged.filename) {
-                               snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
-                               if(stat(filename, &buf) != -1) {
-                                       cg->io_merged.filename = strdup(filename);
-                                       debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
-                               }
-                               else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-                       }
-                       if(!cg->io_queued.filename) {
-                               snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
-                               if(stat(filename, &buf) != -1) {
-                                       cg->io_queued.filename = strdup(filename);
-                                       debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
-                               }
-                               else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
-                       }
-               }
-       }
-
-       debug(D_CGROUP, "done searching for cgroups");
-       return;
+    debug(D_CGROUP, "searching for cgroups");
+
+    mark_all_cgroups_as_not_available();
+
+    if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage)
+        find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir);
+
+    if(cgroup_enable_blkio)
+        find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir);
+
+    if(cgroup_enable_memory)
+        find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir);
+
+    if(cgroup_enable_devices)
+        find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir);
+
+    // remove any non-existing cgroups
+    cleanup_all_cgroups();
+
+    struct cgroup *cg;
+    struct stat buf;
+    for(cg = cgroup_root; cg ; cg = cg->next) {
+        // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
+
+        if(unlikely(!cg->available))
+            continue;
+
+        debug(D_CGROUP, "checking paths for cgroup '%s'", cg->id);
+
+        // check for newly added cgroups
+        // and update the filenames they read
+        char filename[FILENAME_MAX + 1];
+        if(cgroup_enable_cpuacct_stat && !cg->cpuacct_stat.filename) {
+            snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.stat", cgroup_cpuacct_base, cg->id);
+            if(stat(filename, &buf) != -1) {
+                cg->cpuacct_stat.filename = strdupz(filename);
+                debug(D_CGROUP, "cpuacct.stat filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_stat.filename);
+            }
+            else debug(D_CGROUP, "cpuacct.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+        }
+        if(cgroup_enable_cpuacct_usage && !cg->cpuacct_usage.filename) {
+            snprintfz(filename, FILENAME_MAX, "%s%s/cpuacct.usage_percpu", cgroup_cpuacct_base, cg->id);
+            if(stat(filename, &buf) != -1) {
+                cg->cpuacct_usage.filename = strdupz(filename);
+                debug(D_CGROUP, "cpuacct.usage_percpu filename for cgroup '%s': '%s'", cg->id, cg->cpuacct_usage.filename);
+            }
+            else debug(D_CGROUP, "cpuacct.usage_percpu file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+        }
+        if(cgroup_enable_memory && !cg->memory.filename) {
+            snprintfz(filename, FILENAME_MAX, "%s%s/memory.stat", cgroup_memory_base, cg->id);
+            if(stat(filename, &buf) != -1) {
+                cg->memory.filename = strdupz(filename);
+                debug(D_CGROUP, "memory.stat filename for cgroup '%s': '%s'", cg->id, cg->memory.filename);
+            }
+            else debug(D_CGROUP, "memory.stat file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+        }
+        if(cgroup_enable_blkio) {
+            if(!cg->io_service_bytes.filename) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_service_bytes", cgroup_blkio_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->io_service_bytes.filename = strdupz(filename);
+                    debug(D_CGROUP, "io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->io_service_bytes.filename);
+                }
+                else debug(D_CGROUP, "io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(!cg->io_serviced.filename) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_serviced", cgroup_blkio_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->io_serviced.filename = strdupz(filename);
+                    debug(D_CGROUP, "io_serviced filename for cgroup '%s': '%s'", cg->id, cg->io_serviced.filename);
+                }
+                else debug(D_CGROUP, "io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(!cg->throttle_io_service_bytes.filename) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_service_bytes", cgroup_blkio_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->throttle_io_service_bytes.filename = strdupz(filename);
+                    debug(D_CGROUP, "throttle_io_service_bytes filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_service_bytes.filename);
+                }
+                else debug(D_CGROUP, "throttle_io_service_bytes file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(!cg->throttle_io_serviced.filename) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/blkio.throttle.io_serviced", cgroup_blkio_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->throttle_io_serviced.filename = strdupz(filename);
+                    debug(D_CGROUP, "throttle_io_serviced filename for cgroup '%s': '%s'", cg->id, cg->throttle_io_serviced.filename);
+                }
+                else debug(D_CGROUP, "throttle_io_serviced file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(!cg->io_merged.filename) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_merged", cgroup_blkio_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->io_merged.filename = strdupz(filename);
+                    debug(D_CGROUP, "io_merged filename for cgroup '%s': '%s'", cg->id, cg->io_merged.filename);
+                }
+                else debug(D_CGROUP, "io_merged file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+            if(!cg->io_queued.filename) {
+                snprintfz(filename, FILENAME_MAX, "%s%s/blkio.io_queued", cgroup_blkio_base, cg->id);
+                if(stat(filename, &buf) != -1) {
+                    cg->io_queued.filename = strdupz(filename);
+                    debug(D_CGROUP, "io_queued filename for cgroup '%s': '%s'", cg->id, cg->io_queued.filename);
+                }
+                else debug(D_CGROUP, "io_queued file for cgroup '%s': '%s' does not exist.", cg->id, filename);
+            }
+        }
+    }
+
+    debug(D_CGROUP, "done searching for cgroups");
+    return;
 }
 
 // ----------------------------------------------------------------------------
@@ -1022,349 +985,349 @@ void find_all_cgroups() {
 #define CHART_TITLE_MAX 300
 
 void update_cgroup_charts(int update_every) {
-       debug(D_CGROUP, "updating cgroups charts");
-
-       char type[RRD_ID_LENGTH_MAX + 1];
-       char title[CHART_TITLE_MAX + 1];
-
-       struct cgroup *cg;
-       RRDSET *st;
-
-       for(cg = cgroup_root; cg ; cg = cg->next) {
-               if(!cg->available || !cg->enabled)
-                       continue;
-
-               if(cg->id[0] == '\0')
-                       strcpy(type, "cgroup_root");
-               else if(cg->id[0] == '/')
-                       snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
-               else
-                       snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
-
-               netdata_fix_chart_id(type);
-
-               if(cg->cpuacct_stat.updated) {
-                       st = rrdset_find_bytype(type, "cpu");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
-
-                               rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "user", cg->cpuacct_stat.user);
-                       rrddim_set(st, "system", cg->cpuacct_stat.system);
-                       rrdset_done(st);
-               }
-
-               if(cg->cpuacct_usage.updated) {
-                       char id[RRD_ID_LENGTH_MAX + 1];
-                       unsigned int i;
-
-                       st = rrdset_find_bytype(type, "cpu_per_core");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
-
-                               for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
-                                       snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
-                                       rrddim_add(st, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
-                               }
-                       }
-                       else rrdset_next(st);
-
-                       for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
-                               snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
-                               rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
-                       }
-                       rrdset_done(st);
-               }
-
-               if(cg->memory.updated) {
-                       if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
-                               st = rrdset_find_bytype(type, "mem");
-                               if(!st) {
-                                       snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
-                                       st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
-                                                          RRDSET_TYPE_STACKED);
-
-                                       rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                                       if(cg->memory.has_dirty_swap)
-                                               rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                                       rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "cache", cg->memory.cache);
-                               rrddim_set(st, "rss", cg->memory.rss);
-                               if(cg->memory.has_dirty_swap)
-                                       rrddim_set(st, "swap", cg->memory.swap);
-                               rrddim_set(st, "rss_huge", cg->memory.rss_huge);
-                               rrddim_set(st, "mapped_file", cg->memory.mapped_file);
-                               rrdset_done(st);
-                       }
-
-                       st = rrdset_find_bytype(type, "writeback");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
-                                                  update_every, RRDSET_TYPE_AREA);
-
-                               if(cg->memory.has_dirty_swap)
-                                       rrddim_add(st, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                               rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next(st);
-
-                       if(cg->memory.has_dirty_swap)
-                               rrddim_set(st, "dirty", cg->memory.dirty);
-                       rrddim_set(st, "writeback", cg->memory.writeback);
-                       rrdset_done(st);
-
-                       if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
-                               st = rrdset_find_bytype(type, "mem_activity");
-                               if(!st) {
-                                       snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
-                                       st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
-                                                          40400, update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "pgpgin", cg->memory.pgpgin);
-                               rrddim_set(st, "pgpgout", cg->memory.pgpgout);
-                               rrdset_done(st);
-                       }
-
-                       if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
-                               st = rrdset_find_bytype(type, "pgfaults");
-                               if(!st) {
-                                       snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
-                                       st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
-                                                          update_every, RRDSET_TYPE_LINE);
-
-                                       rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
-                                       rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
-                               }
-                               else rrdset_next(st);
-
-                               rrddim_set(st, "pgfault", cg->memory.pgfault);
-                               rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
-                               rrdset_done(st);
-                       }
-               }
-
-               if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
-                       st = rrdset_find_bytype(type, "io");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
-                                                  update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "read", cg->io_service_bytes.Read);
-                       rrddim_set(st, "write", cg->io_service_bytes.Write);
-                       rrdset_done(st);
-               }
-
-               if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
-                       st = rrdset_find_bytype(type, "serviced_ops");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
-                                                  update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "read", cg->io_serviced.Read);
-                       rrddim_set(st, "write", cg->io_serviced.Write);
-                       rrdset_done(st);
-               }
-
-               if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
-                       st = rrdset_find_bytype(type, "io");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
-                                                  update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
-                       rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
-                       rrdset_done(st);
-               }
-
-
-               if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
-                       st = rrdset_find_bytype(type, "throttle_serviced_ops");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
-                                                  update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "read", cg->throttle_io_serviced.Read);
-                       rrddim_set(st, "write", cg->throttle_io_serviced.Write);
-                       rrdset_done(st);
-               }
-
-               if(cg->io_queued.updated) {
-                       st = rrdset_find_bytype(type, "queued_ops");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
-                                                  update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
-                               rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "read", cg->io_queued.Read);
-                       rrddim_set(st, "write", cg->io_queued.Write);
-                       rrdset_done(st);
-               }
-
-               if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
-                       st = rrdset_find_bytype(type, "merged_ops");
-                       if(!st) {
-                               snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
-                               st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
-                                                  update_every, RRDSET_TYPE_LINE);
-
-                               rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
-                               rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(st);
-
-                       rrddim_set(st, "read", cg->io_merged.Read);
-                       rrddim_set(st, "write", cg->io_merged.Write);
-                       rrdset_done(st);
-               }
-       }
-
-       debug(D_CGROUP, "done updating cgroups charts");
+    debug(D_CGROUP, "updating cgroups charts");
+
+    char type[RRD_ID_LENGTH_MAX + 1];
+    char title[CHART_TITLE_MAX + 1];
+
+    struct cgroup *cg;
+    RRDSET *st;
+
+    for(cg = cgroup_root; cg ; cg = cg->next) {
+        if(!cg->available || !cg->enabled)
+            continue;
+
+        if(cg->id[0] == '\0')
+            strcpy(type, "cgroup_root");
+        else if(cg->id[0] == '/')
+            snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
+        else
+            snprintfz(type, RRD_ID_LENGTH_MAX, "cgroup_%s", cg->chart_id);
+
+        netdata_fix_chart_id(type);
+
+        if(cg->cpuacct_stat.updated) {
+            st = rrdset_find_bytype(type, "cpu");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "CPU Usage for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "cpu", NULL, "cpu", "cgroup.cpu", title, "%", 40000, update_every, RRDSET_TYPE_STACKED);
+
+                rrddim_add(st, "user", NULL, 100, hz, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "system", NULL, 100, hz, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "user", cg->cpuacct_stat.user);
+            rrddim_set(st, "system", cg->cpuacct_stat.system);
+            rrdset_done(st);
+        }
+
+        if(cg->cpuacct_usage.updated) {
+            char id[RRD_ID_LENGTH_MAX + 1];
+            unsigned int i;
+
+            st = rrdset_find_bytype(type, "cpu_per_core");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "CPU Usage Per Core for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "cpu_per_core", NULL, "cpu", "cgroup.cpu_per_core", title, "%", 40100, update_every, RRDSET_TYPE_STACKED);
+
+                for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
+                    snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
+                    rrddim_add(st, id, NULL, 100, 1000000000, RRDDIM_INCREMENTAL);
+                }
+            }
+            else rrdset_next(st);
+
+            for(i = 0; i < cg->cpuacct_usage.cpus ;i++) {
+                snprintfz(id, CHART_TITLE_MAX, "cpu%u", i);
+                rrddim_set(st, id, cg->cpuacct_usage.cpu_percpu[i]);
+            }
+            rrdset_done(st);
+        }
+
+        if(cg->memory.updated) {
+            if(cg->memory.cache + cg->memory.rss + cg->memory.rss_huge + cg->memory.mapped_file > 0) {
+                st = rrdset_find_bytype(type, "mem");
+                if(!st) {
+                    snprintfz(title, CHART_TITLE_MAX, "Memory Usage for cgroup %s", cg->chart_title);
+                    st = rrdset_create(type, "mem", NULL, "mem", "cgroup.mem", title, "MB", 40200, update_every,
+                                       RRDSET_TYPE_STACKED);
+
+                    rrddim_add(st, "cache", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "rss", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                    if(cg->memory.has_dirty_swap)
+                        rrddim_add(st, "swap", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "rss_huge", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                    rrddim_add(st, "mapped_file", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "cache", cg->memory.cache);
+                rrddim_set(st, "rss", cg->memory.rss);
+                if(cg->memory.has_dirty_swap)
+                    rrddim_set(st, "swap", cg->memory.swap);
+                rrddim_set(st, "rss_huge", cg->memory.rss_huge);
+                rrddim_set(st, "mapped_file", cg->memory.mapped_file);
+                rrdset_done(st);
+            }
+
+            st = rrdset_find_bytype(type, "writeback");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Writeback Memory for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "writeback", NULL, "mem", "cgroup.writeback", title, "MB", 40300,
+                                   update_every, RRDSET_TYPE_AREA);
+
+                if(cg->memory.has_dirty_swap)
+                    rrddim_add(st, "dirty", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "writeback", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+            }
+            else rrdset_next(st);
+
+            if(cg->memory.has_dirty_swap)
+                rrddim_set(st, "dirty", cg->memory.dirty);
+            rrddim_set(st, "writeback", cg->memory.writeback);
+            rrdset_done(st);
+
+            if(cg->memory.pgpgin + cg->memory.pgpgout > 0) {
+                st = rrdset_find_bytype(type, "mem_activity");
+                if(!st) {
+                    snprintfz(title, CHART_TITLE_MAX, "Memory Activity for cgroup %s", cg->chart_title);
+                    st = rrdset_create(type, "mem_activity", NULL, "mem", "cgroup.mem_activity", title, "MB/s",
+                                       40400, update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "pgpgin", "in", sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "pgpgout", "out", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "pgpgin", cg->memory.pgpgin);
+                rrddim_set(st, "pgpgout", cg->memory.pgpgout);
+                rrdset_done(st);
+            }
+
+            if(cg->memory.pgfault + cg->memory.pgmajfault > 0) {
+                st = rrdset_find_bytype(type, "pgfaults");
+                if(!st) {
+                    snprintfz(title, CHART_TITLE_MAX, "Memory Page Faults for cgroup %s", cg->chart_title);
+                    st = rrdset_create(type, "pgfaults", NULL, "mem", "cgroup.pgfaults", title, "MB/s", 40500,
+                                       update_every, RRDSET_TYPE_LINE);
+
+                    rrddim_add(st, "pgfault", NULL, sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+                    rrddim_add(st, "pgmajfault", "swap", -sysconf(_SC_PAGESIZE), 1024 * 1024, RRDDIM_INCREMENTAL);
+                }
+                else rrdset_next(st);
+
+                rrddim_set(st, "pgfault", cg->memory.pgfault);
+                rrddim_set(st, "pgmajfault", cg->memory.pgmajfault);
+                rrdset_done(st);
+            }
+        }
+
+        if(cg->io_service_bytes.updated && cg->io_service_bytes.Read + cg->io_service_bytes.Write > 0) {
+            st = rrdset_find_bytype(type, "io");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "read", cg->io_service_bytes.Read);
+            rrddim_set(st, "write", cg->io_service_bytes.Write);
+            rrdset_done(st);
+        }
+
+        if(cg->io_serviced.updated && cg->io_serviced.Read + cg->io_serviced.Write > 0) {
+            st = rrdset_find_bytype(type, "serviced_ops");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "serviced_ops", NULL, "disk", "cgroup.serviced_ops", title, "operations/s", 41200,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "read", cg->io_serviced.Read);
+            rrddim_set(st, "write", cg->io_serviced.Write);
+            rrdset_done(st);
+        }
+
+        if(cg->throttle_io_service_bytes.updated && cg->throttle_io_service_bytes.Read + cg->throttle_io_service_bytes.Write > 0) {
+            st = rrdset_find_bytype(type, "io");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Throttle I/O Bandwidth (all disks) for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "io", NULL, "disk", "cgroup.io", title, "KB/s", 41200,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "read", cg->throttle_io_service_bytes.Read);
+            rrddim_set(st, "write", cg->throttle_io_service_bytes.Write);
+            rrdset_done(st);
+        }
+
+
+        if(cg->throttle_io_serviced.updated && cg->throttle_io_serviced.Read + cg->throttle_io_serviced.Write > 0) {
+            st = rrdset_find_bytype(type, "throttle_serviced_ops");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Throttle Serviced I/O Operations (all disks) for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "throttle_serviced_ops", NULL, "disk", "cgroup.throttle_serviced_ops", title, "operations/s", 41200,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "read", NULL, 1, 1, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "write", NULL, -1, 1, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "read", cg->throttle_io_serviced.Read);
+            rrddim_set(st, "write", cg->throttle_io_serviced.Write);
+            rrdset_done(st);
+        }
+
+        if(cg->io_queued.updated) {
+            st = rrdset_find_bytype(type, "queued_ops");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Queued I/O Operations (all disks) for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "queued_ops", NULL, "disk", "cgroup.queued_ops", title, "operations", 42000,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "read", NULL, 1, 1, RRDDIM_ABSOLUTE);
+                rrddim_add(st, "write", NULL, -1, 1, RRDDIM_ABSOLUTE);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "read", cg->io_queued.Read);
+            rrddim_set(st, "write", cg->io_queued.Write);
+            rrdset_done(st);
+        }
+
+        if(cg->io_merged.updated && cg->io_merged.Read + cg->io_merged.Write > 0) {
+            st = rrdset_find_bytype(type, "merged_ops");
+            if(!st) {
+                snprintfz(title, CHART_TITLE_MAX, "Merged I/O Operations (all disks) for cgroup %s", cg->chart_title);
+                st = rrdset_create(type, "merged_ops", NULL, "disk", "cgroup.merged_ops", title, "operations/s", 42100,
+                                   update_every, RRDSET_TYPE_LINE);
+
+                rrddim_add(st, "read", NULL, 1, 1024, RRDDIM_INCREMENTAL);
+                rrddim_add(st, "write", NULL, -1, 1024, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(st);
+
+            rrddim_set(st, "read", cg->io_merged.Read);
+            rrddim_set(st, "write", cg->io_merged.Write);
+            rrdset_done(st);
+        }
+    }
+
+    debug(D_CGROUP, "done updating cgroups charts");
 }
 
 // ----------------------------------------------------------------------------
 // cgroups main
 
 int do_sys_fs_cgroup(int update_every, unsigned long long dt) {
-       (void)dt;
+    (void)dt;
 
-       static int cgroup_global_config_read = 0;
-       static time_t last_run = 0;
-       time_t now = time(NULL);
+    static int cgroup_global_config_read = 0;
+    static time_t last_run = 0;
+    time_t now = time(NULL);
 
-       if(unlikely(!cgroup_global_config_read)) {
-               read_cgroup_plugin_configuration();
-               cgroup_global_config_read = 1;
-       }
+    if(unlikely(!cgroup_global_config_read)) {
+        read_cgroup_plugin_configuration();
+        cgroup_global_config_read = 1;
+    }
 
-       if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
-               find_all_cgroups();
-               last_run = now;
-       }
+    if(unlikely(cgroup_enable_new_cgroups_detected_at_runtime && now - last_run > cgroup_check_for_new_every)) {
+        find_all_cgroups();
+        last_run = now;
+    }
 
-       read_all_cgroups(cgroup_root);
-       update_cgroup_charts(update_every);
+    read_all_cgroups(cgroup_root);
+    update_cgroup_charts(update_every);
 
-       return 0;
+    return 0;
 }
 
 void *cgroups_main(void *ptr)
 {
-       if(ptr) { ; }
+    if(ptr) { ; }
 
-       info("CGROUP Plugin thread created with task id %d", gettid());
+    info("CGROUP Plugin thread created with task id %d", gettid());
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
 
-       struct rusage thread;
+    struct rusage thread;
 
-       // when ZERO, attempt to do it
-       int vdo_sys_fs_cgroup                   = 0;
-       int vdo_cpu_netdata                     = !config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
+    // when ZERO, attempt to do it
+    int vdo_sys_fs_cgroup           = 0;
+    int vdo_cpu_netdata             = !config_get_boolean("plugin:cgroups", "cgroups plugin resources", 1);
 
-       // keep track of the time each module was called
-       unsigned long long sutime_sys_fs_cgroup = 0ULL;
+    // keep track of the time each module was called
+    unsigned long long sutime_sys_fs_cgroup = 0ULL;
 
-       // the next time we will run - aligned properly
-       unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
-       unsigned long long sunow;
+    // the next time we will run - aligned properly
+    unsigned long long sunext = (time(NULL) - (time(NULL) % rrd_update_every) + rrd_update_every) * 1000000ULL;
+    unsigned long long sunow;
 
-       RRDSET *stcpu_thread = NULL;
+    RRDSET *stcpu_thread = NULL;
 
-       for(;1;) {
-               if(unlikely(netdata_exit)) break;
+    for(;1;) {
+        if(unlikely(netdata_exit)) break;
 
-               // delay until it is our time to run
-               while((sunow = timems()) < sunext)
-                       usecsleep(sunext - sunow);
+        // delay until it is our time to run
+        while((sunow = time_usec()) < sunext)
+            sleep_usec(sunext - sunow);
 
-               // find the next time we need to run
-               while(timems() > sunext)
-                       sunext += rrd_update_every * 1000000ULL;
+        // find the next time we need to run
+        while(time_usec() > sunext)
+            sunext += rrd_update_every * 1000000ULL;
 
-               if(unlikely(netdata_exit)) break;
+        if(unlikely(netdata_exit)) break;
 
-               // BEGIN -- the job to be done
+        // BEGIN -- the job to be done
 
-               if(!vdo_sys_fs_cgroup) {
-                       debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
-                       sunow = timems();
-                       vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL);
-                       sutime_sys_fs_cgroup = sunow;
-               }
-               if(unlikely(netdata_exit)) break;
+        if(!vdo_sys_fs_cgroup) {
+            debug(D_PROCNETDEV_LOOP, "PROCNETDEV: calling do_sys_fs_cgroup().");
+            sunow = time_usec();
+            vdo_sys_fs_cgroup = do_sys_fs_cgroup(rrd_update_every, (sutime_sys_fs_cgroup > 0)?sunow - sutime_sys_fs_cgroup:0ULL);
+            sutime_sys_fs_cgroup = sunow;
+        }
+        if(unlikely(netdata_exit)) break;
 
-               // END -- the job is done
+        // END -- the job is done
 
-               // --------------------------------------------------------------------
+        // --------------------------------------------------------------------
 
-               if(!vdo_cpu_netdata) {
-                       getrusage(RUSAGE_THREAD, &thread);
+        if(!vdo_cpu_netdata) {
+            getrusage(RUSAGE_THREAD, &thread);
 
-                       if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
-                       if(!stcpu_thread) {
-                               stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "proc.internal", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
+            if(!stcpu_thread) stcpu_thread = rrdset_find("netdata.plugin_cgroups_cpu");
+            if(!stcpu_thread) {
+                stcpu_thread = rrdset_create("netdata", "plugin_cgroups_cpu", NULL, "proc.internal", NULL, "NetData CGroups Plugin CPU usage", "milliseconds/s", 132000, rrd_update_every, RRDSET_TYPE_STACKED);
 
-                               rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
-                               rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
-                       }
-                       else rrdset_next(stcpu_thread);
+                rrddim_add(stcpu_thread, "user",  NULL,  1, 1000, RRDDIM_INCREMENTAL);
+                rrddim_add(stcpu_thread, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL);
+            }
+            else rrdset_next(stcpu_thread);
 
-                       rrddim_set(stcpu_thread, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
-                       rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
-                       rrdset_done(stcpu_thread);
-               }
-       }
+            rrddim_set(stcpu_thread, "user"  , thread.ru_utime.tv_sec * 1000000ULL + thread.ru_utime.tv_usec);
+            rrddim_set(stcpu_thread, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec);
+            rrdset_done(stcpu_thread);
+        }
+    }
 
-       pthread_exit(NULL);
-       return NULL;
+    pthread_exit(NULL);
+    return NULL;
 }
index 8064cb1818e5742a02d67ab87cf3118b9a899f2b..8c51be1df5e0b08337b61656d2682634eefdf35f 100644 (file)
@@ -1,21 +1,9 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "procfile.h"
-#include "rrd.h"
-#include "plugin_proc.h"
 
-typedef struct name_value {
-       char filename[FILENAME_MAX + 1];
-       unsigned long long value;
-} NAME_VALUE;
+typedef struct ksm_name_value {
+    char filename[FILENAME_MAX + 1];
+    unsigned long long value;
+} KSM_NAME_VALUE;
 
 #define PAGES_SHARED 0
 #define PAGES_SHARING 1
@@ -23,128 +11,128 @@ typedef struct name_value {
 #define PAGES_VOLATILE 3
 #define PAGES_TO_SCAN 4
 
-NAME_VALUE values[] = {
-               [PAGES_SHARED] = { "/sys/kernel/mm/ksm/pages_shared", 0ULL },
-               [PAGES_SHARING] = { "/sys/kernel/mm/ksm/pages_sharing", 0ULL },
-               [PAGES_UNSHARED] = { "/sys/kernel/mm/ksm/pages_unshared", 0ULL },
-               [PAGES_VOLATILE] = { "/sys/kernel/mm/ksm/pages_volatile", 0ULL },
-               [PAGES_TO_SCAN] = { "/sys/kernel/mm/ksm/pages_to_scan", 0ULL },
+KSM_NAME_VALUE values[] = {
+        [PAGES_SHARED] = { "/sys/kernel/mm/ksm/pages_shared", 0ULL },
+        [PAGES_SHARING] = { "/sys/kernel/mm/ksm/pages_sharing", 0ULL },
+        [PAGES_UNSHARED] = { "/sys/kernel/mm/ksm/pages_unshared", 0ULL },
+        [PAGES_VOLATILE] = { "/sys/kernel/mm/ksm/pages_volatile", 0ULL },
+        [PAGES_TO_SCAN] = { "/sys/kernel/mm/ksm/pages_to_scan", 0ULL },
 };
 
 int do_sys_kernel_mm_ksm(int update_every, unsigned long long dt) {
-       static procfile *ff_pages_shared = NULL, *ff_pages_sharing = NULL, *ff_pages_unshared = NULL, *ff_pages_volatile = NULL, *ff_pages_to_scan = NULL;
-       static long page_size = -1;
+    static procfile *ff_pages_shared = NULL, *ff_pages_sharing = NULL, *ff_pages_unshared = NULL, *ff_pages_volatile = NULL, *ff_pages_to_scan = NULL;
+    static long page_size = -1;
 
-       if(dt) {};
-
-       if(page_size == -1)
-               page_size = sysconf(_SC_PAGESIZE);
-
-       if(!ff_pages_shared) {
-               snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_shared");
-               snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_shared", values[PAGES_SHARED].filename));
-               ff_pages_shared = procfile_open(values[PAGES_SHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
-       }
+    if(dt) {};
+
+    if(page_size == -1)
+        page_size = sysconf(_SC_PAGESIZE);
+
+    if(!ff_pages_shared) {
+        snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_shared");
+        snprintfz(values[PAGES_SHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_shared", values[PAGES_SHARED].filename));
+        ff_pages_shared = procfile_open(values[PAGES_SHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+    }
 
-       if(!ff_pages_sharing) {
-               snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_sharing");
-               snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_sharing", values[PAGES_SHARING].filename));
-               ff_pages_sharing = procfile_open(values[PAGES_SHARING].filename, " \t:", PROCFILE_FLAG_DEFAULT);
-       }
+    if(!ff_pages_sharing) {
+        snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_sharing");
+        snprintfz(values[PAGES_SHARING].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_sharing", values[PAGES_SHARING].filename));
+        ff_pages_sharing = procfile_open(values[PAGES_SHARING].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+    }
 
-       if(!ff_pages_unshared) {
-               snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_unshared");
-               snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_unshared", values[PAGES_UNSHARED].filename));
-               ff_pages_unshared = procfile_open(values[PAGES_UNSHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
-       }
+    if(!ff_pages_unshared) {
+        snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_unshared");
+        snprintfz(values[PAGES_UNSHARED].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_unshared", values[PAGES_UNSHARED].filename));
+        ff_pages_unshared = procfile_open(values[PAGES_UNSHARED].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+    }
 
-       if(!ff_pages_volatile) {
-               snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_volatile");
-               snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_volatile", values[PAGES_VOLATILE].filename));
-               ff_pages_volatile = procfile_open(values[PAGES_VOLATILE].filename, " \t:", PROCFILE_FLAG_DEFAULT);
-       }
+    if(!ff_pages_volatile) {
+        snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_volatile");
+        snprintfz(values[PAGES_VOLATILE].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_volatile", values[PAGES_VOLATILE].filename));
+        ff_pages_volatile = procfile_open(values[PAGES_VOLATILE].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+    }
 
-       if(!ff_pages_to_scan) {
-               snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_to_scan");
-               snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_to_scan", values[PAGES_TO_SCAN].filename));
-               ff_pages_to_scan = procfile_open(values[PAGES_TO_SCAN].filename, " \t:", PROCFILE_FLAG_DEFAULT);
-       }
+    if(!ff_pages_to_scan) {
+        snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/kernel/mm/ksm/pages_to_scan");
+        snprintfz(values[PAGES_TO_SCAN].filename, FILENAME_MAX, "%s", config_get("plugin:proc:/sys/kernel/mm/ksm", "/sys/kernel/mm/ksm/pages_to_scan", values[PAGES_TO_SCAN].filename));
+        ff_pages_to_scan = procfile_open(values[PAGES_TO_SCAN].filename, " \t:", PROCFILE_FLAG_DEFAULT);
+    }
 
-       if(!ff_pages_shared || !ff_pages_sharing || !ff_pages_unshared || !ff_pages_volatile || !ff_pages_to_scan) return 1;
+    if(!ff_pages_shared || !ff_pages_sharing || !ff_pages_unshared || !ff_pages_volatile || !ff_pages_to_scan) return 1;
 
-       unsigned long long pages_shared = 0, pages_sharing = 0, pages_unshared = 0, pages_volatile = 0, pages_to_scan = 0, offered = 0, saved = 0;
+    unsigned long long pages_shared = 0, pages_sharing = 0, pages_unshared = 0, pages_volatile = 0, pages_to_scan = 0, offered = 0, saved = 0;
 
-       ff_pages_shared = procfile_readall(ff_pages_shared);
-       if(!ff_pages_shared) return 0; // we return 0, so that we will retry to open it next time
-       pages_shared = strtoull(procfile_lineword(ff_pages_shared, 0, 0), NULL, 10);
+    ff_pages_shared = procfile_readall(ff_pages_shared);
+    if(!ff_pages_shared) return 0; // we return 0, so that we will retry to open it next time
+    pages_shared = strtoull(procfile_lineword(ff_pages_shared, 0, 0), NULL, 10);
 
-       ff_pages_sharing = procfile_readall(ff_pages_sharing);
-       if(!ff_pages_sharing) return 0; // we return 0, so that we will retry to open it next time
-       pages_sharing = strtoull(procfile_lineword(ff_pages_sharing, 0, 0), NULL, 10);
+    ff_pages_sharing = procfile_readall(ff_pages_sharing);
+    if(!ff_pages_sharing) return 0; // we return 0, so that we will retry to open it next time
+    pages_sharing = strtoull(procfile_lineword(ff_pages_sharing, 0, 0), NULL, 10);
 
-       ff_pages_unshared = procfile_readall(ff_pages_unshared);
-       if(!ff_pages_unshared) return 0; // we return 0, so that we will retry to open it next time
-       pages_unshared = strtoull(procfile_lineword(ff_pages_unshared, 0, 0), NULL, 10);
+    ff_pages_unshared = procfile_readall(ff_pages_unshared);
+    if(!ff_pages_unshared) return 0; // we return 0, so that we will retry to open it next time
+    pages_unshared = strtoull(procfile_lineword(ff_pages_unshared, 0, 0), NULL, 10);
 
-       ff_pages_volatile = procfile_readall(ff_pages_volatile);
-       if(!ff_pages_volatile) return 0; // we return 0, so that we will retry to open it next time
-       pages_volatile = strtoull(procfile_lineword(ff_pages_volatile, 0, 0), NULL, 10);
+    ff_pages_volatile = procfile_readall(ff_pages_volatile);
+    if(!ff_pages_volatile) return 0; // we return 0, so that we will retry to open it next time
+    pages_volatile = strtoull(procfile_lineword(ff_pages_volatile, 0, 0), NULL, 10);
 
-       ff_pages_to_scan = procfile_readall(ff_pages_to_scan);
-       if(!ff_pages_to_scan) return 0; // we return 0, so that we will retry to open it next time
-       pages_to_scan = strtoull(procfile_lineword(ff_pages_to_scan, 0, 0), NULL, 10);
+    ff_pages_to_scan = procfile_readall(ff_pages_to_scan);
+    if(!ff_pages_to_scan) return 0; // we return 0, so that we will retry to open it next time
+    pages_to_scan = strtoull(procfile_lineword(ff_pages_to_scan, 0, 0), NULL, 10);
 
-       offered = pages_sharing + pages_shared + pages_unshared + pages_volatile;
-       saved = pages_sharing - pages_shared;
+    offered = pages_sharing + pages_shared + pages_unshared + pages_volatile;
+    saved = pages_sharing - pages_shared;
 
-       if(!offered || !pages_to_scan) return 0;
+    if(!offered || !pages_to_scan) return 0;
 
-       RRDSET *st;
+    RRDSET *st;
 
-       // --------------------------------------------------------------------
+    // --------------------------------------------------------------------
 
-       st = rrdset_find("mem.ksm");
-       if(!st) {
-               st = rrdset_create("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000, update_every, RRDSET_TYPE_AREA);
+    st = rrdset_find("mem.ksm");
+    if(!st) {
+        st = rrdset_create("mem", "ksm", NULL, "ksm", NULL, "Kernel Same Page Merging", "MB", 5000, update_every, RRDSET_TYPE_AREA);
 
-               rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(st);
+        rrddim_add(st, "shared", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "unshared", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "sharing", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "volatile", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "to_scan", "to scan", -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(st);
 
-       rrddim_set(st, "shared", pages_shared * page_size);
-       rrddim_set(st, "unshared", pages_unshared * page_size);
-       rrddim_set(st, "sharing", pages_sharing * page_size);
-       rrddim_set(st, "volatile", pages_volatile * page_size);
-       rrddim_set(st, "to_scan", pages_to_scan * page_size);
-       rrdset_done(st);
+    rrddim_set(st, "shared", pages_shared * page_size);
+    rrddim_set(st, "unshared", pages_unshared * page_size);
+    rrddim_set(st, "sharing", pages_sharing * page_size);
+    rrddim_set(st, "volatile", pages_volatile * page_size);
+    rrddim_set(st, "to_scan", pages_to_scan * page_size);
+    rrdset_done(st);
 
-       st = rrdset_find("mem.ksm_savings");
-       if(!st) {
-               st = rrdset_create("mem", "ksm_savings", NULL, "ksm", NULL, "Kernel Same Page Merging Savings", "MB", 5001, update_every, RRDSET_TYPE_AREA);
+    st = rrdset_find("mem.ksm_savings");
+    if(!st) {
+        st = rrdset_create("mem", "ksm_savings", NULL, "ksm", NULL, "Kernel Same Page Merging Savings", "MB", 5001, update_every, RRDSET_TYPE_AREA);
 
-               rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
-               rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(st);
+        rrddim_add(st, "savings", NULL, -1, 1024 * 1024, RRDDIM_ABSOLUTE);
+        rrddim_add(st, "offered", NULL, 1, 1024 * 1024, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(st);
 
-       rrddim_set(st, "savings", saved * page_size);
-       rrddim_set(st, "offered", offered * page_size);
-       rrdset_done(st);
+    rrddim_set(st, "savings", saved * page_size);
+    rrddim_set(st, "offered", offered * page_size);
+    rrdset_done(st);
 
-       st = rrdset_find("mem.ksm_ratios");
-       if(!st) {
-               st = rrdset_create("mem", "ksm_ratios", NULL, "ksm", NULL, "Kernel Same Page Merging Effectiveness", "percentage", 5002, update_every, RRDSET_TYPE_LINE);
-
-               rrddim_add(st, "savings", NULL, 1, 10000, RRDDIM_ABSOLUTE);
-       }
-       else rrdset_next(st);
-
-       rrddim_set(st, "savings", (saved * 1000000) / offered);
-       rrdset_done(st);
+    st = rrdset_find("mem.ksm_ratios");
+    if(!st) {
+        st = rrdset_create("mem", "ksm_ratios", NULL, "ksm", NULL, "Kernel Same Page Merging Effectiveness", "percentage", 5002, update_every, RRDSET_TYPE_LINE);
+
+        rrddim_add(st, "savings", NULL, 1, 10000, RRDDIM_ABSOLUTE);
+    }
+    else rrdset_next(st);
+
+    rrddim_set(st, "savings", (saved * 1000000) / offered);
+    rrdset_done(st);
 
-       return 0;
+    return 0;
 }
index dbf9190ba4c7d66de2b9ace17379700a08d0ec03..a77fdb1349b60a98228b7fc64c3da54f7915c2f8 100644 (file)
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <math.h>
-
 #include "common.h"
-#include "storage_number.h"
-#include "rrd.h"
-#include "log.h"
-#include "web_buffer.h"
 
 int check_storage_number(calculated_number n, int debug) {
-       char buffer[100];
-       uint32_t flags = SN_EXISTS;
-
-       storage_number s = pack_storage_number(n, flags);
-       calculated_number d = unpack_storage_number(s);
-
-       if(!does_storage_number_exist(s)) {
-               fprintf(stderr, "Exists flags missing for number " CALCULATED_NUMBER_FORMAT "!\n", n);
-               return 5;
-       }
-
-       calculated_number ddiff = d - n;
-       calculated_number dcdiff = ddiff * 100.0 / n;
-
-       if(dcdiff < 0) dcdiff = -dcdiff;
-
-       size_t len = print_calculated_number(buffer, d);
-       calculated_number p = strtold(buffer, NULL);
-       calculated_number pdiff = n - p;
-       calculated_number pcdiff = pdiff * 100.0 / n;
-       if(pcdiff < 0) pcdiff = -pcdiff;
-
-       if(debug) {
-               fprintf(stderr,
-                       CALCULATED_NUMBER_FORMAT " original\n"
-                       CALCULATED_NUMBER_FORMAT " packed and unpacked, (stored as 0x%08X, diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n"
-                       "%s printed after unpacked (%zu bytes)\n"
-                       CALCULATED_NUMBER_FORMAT " re-parsed from printed (diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n\n",
-                       n,
-                       d, s, ddiff, dcdiff,
-                       buffer,
-                       len, p, pdiff, pcdiff
-               );
-               if(len != strlen(buffer)) fprintf(stderr, "ERROR: printed number %s is reported to have length %zu but it has %zu\n", buffer, len, strlen(buffer));
-               if(dcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: packing number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, dcdiff);
-               if(pcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: re-parsing the packed, unpacked and printed number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, pcdiff);
-       }
-
-       if(len != strlen(buffer)) return 1;
-       if(dcdiff > ACCURACY_LOSS) return 3;
-       if(pcdiff > ACCURACY_LOSS) return 4;
-       return 0;
+    char buffer[100];
+    uint32_t flags = SN_EXISTS;
+
+    storage_number s = pack_storage_number(n, flags);
+    calculated_number d = unpack_storage_number(s);
+
+    if(!does_storage_number_exist(s)) {
+        fprintf(stderr, "Exists flags missing for number " CALCULATED_NUMBER_FORMAT "!\n", n);
+        return 5;
+    }
+
+    calculated_number ddiff = d - n;
+    calculated_number dcdiff = ddiff * 100.0 / n;
+
+    if(dcdiff < 0) dcdiff = -dcdiff;
+
+    size_t len = print_calculated_number(buffer, d);
+    calculated_number p = strtold(buffer, NULL);
+    calculated_number pdiff = n - p;
+    calculated_number pcdiff = pdiff * 100.0 / n;
+    if(pcdiff < 0) pcdiff = -pcdiff;
+
+    if(debug) {
+        fprintf(stderr,
+            CALCULATED_NUMBER_FORMAT " original\n"
+            CALCULATED_NUMBER_FORMAT " packed and unpacked, (stored as 0x%08X, diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n"
+            "%s printed after unpacked (%zu bytes)\n"
+            CALCULATED_NUMBER_FORMAT " re-parsed from printed (diff " CALCULATED_NUMBER_FORMAT ", " CALCULATED_NUMBER_FORMAT "%%)\n\n",
+            n,
+            d, s, ddiff, dcdiff,
+            buffer,
+            len, p, pdiff, pcdiff
+        );
+        if(len != strlen(buffer)) fprintf(stderr, "ERROR: printed number %s is reported to have length %zu but it has %zu\n", buffer, len, strlen(buffer));
+        if(dcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: packing number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, dcdiff);
+        if(pcdiff > ACCURACY_LOSS) fprintf(stderr, "WARNING: re-parsing the packed, unpacked and printed number " CALCULATED_NUMBER_FORMAT " has accuracy loss %0.7Lf %%\n", n, pcdiff);
+    }
+
+    if(len != strlen(buffer)) return 1;
+    if(dcdiff > ACCURACY_LOSS) return 3;
+    if(pcdiff > ACCURACY_LOSS) return 4;
+    return 0;
 }
 
 void benchmark_storage_number(int loop, int multiplier) {
-       int i, j;
-       calculated_number n, d;
-       storage_number s;
-       unsigned long long user, system, total, mine, their;
+    int i, j;
+    calculated_number n, d;
+    storage_number s;
+    unsigned long long user, system, total, mine, their;
 
-       char buffer[100];
+    char buffer[100];
 
-       struct rusage now, last;
+    struct rusage now, last;
 
-       fprintf(stderr, "\n\nBenchmarking %d numbers, please wait...\n\n", loop);
+    fprintf(stderr, "\n\nBenchmarking %d numbers, please wait...\n\n", loop);
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
-       fprintf(stderr, "SYSTEM  LONG DOUBLE    SIZE: %zu bytes\n", sizeof(calculated_number));
-       fprintf(stderr, "NETDATA FLOATING POINT SIZE: %zu bytes\n", sizeof(storage_number));
+    fprintf(stderr, "SYSTEM  LONG DOUBLE    SIZE: %zu bytes\n", sizeof(calculated_number));
+    fprintf(stderr, "NETDATA FLOATING POINT SIZE: %zu bytes\n", sizeof(storage_number));
 
-       mine = (calculated_number)sizeof(storage_number) * (calculated_number)loop;
-       their = (calculated_number)sizeof(calculated_number) * (calculated_number)loop;
+    mine = (calculated_number)sizeof(storage_number) * (calculated_number)loop;
+    their = (calculated_number)sizeof(calculated_number) * (calculated_number)loop;
 
-       if(mine > their) {
-               fprintf(stderr, "\nNETDATA NEEDS %0.2Lf TIMES MORE MEMORY. Sorry!\n", (long double)(mine / their));
-       }
-       else {
-               fprintf(stderr, "\nNETDATA INTERNAL FLOATING POINT ARITHMETICS NEEDS %0.2Lf TIMES LESS MEMORY.\n", (long double)(their / mine));
-       }
+    if(mine > their) {
+        fprintf(stderr, "\nNETDATA NEEDS %0.2Lf TIMES MORE MEMORY. Sorry!\n", (long double)(mine / their));
+    }
+    else {
+        fprintf(stderr, "\nNETDATA INTERNAL FLOATING POINT ARITHMETICS NEEDS %0.2Lf TIMES LESS MEMORY.\n", (long double)(their / mine));
+    }
 
-       fprintf(stderr, "\nNETDATA FLOATING POINT\n");
-       fprintf(stderr, "MIN POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MIN);
-       fprintf(stderr, "MAX POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MAX);
-       fprintf(stderr, "MIN NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MIN);
-       fprintf(stderr, "MAX NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MAX);
-       fprintf(stderr, "Maximum accuracy loss: " CALCULATED_NUMBER_FORMAT "%%\n\n\n", (calculated_number)ACCURACY_LOSS);
+    fprintf(stderr, "\nNETDATA FLOATING POINT\n");
+    fprintf(stderr, "MIN POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MIN);
+    fprintf(stderr, "MAX POSITIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_POSITIVE_MAX);
+    fprintf(stderr, "MIN NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MIN);
+    fprintf(stderr, "MAX NEGATIVE VALUE " CALCULATED_NUMBER_FORMAT "\n", (calculated_number)STORAGE_NUMBER_NEGATIVE_MAX);
+    fprintf(stderr, "Maximum accuracy loss: " CALCULATED_NUMBER_FORMAT "%%\n\n\n", (calculated_number)ACCURACY_LOSS);
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
-       fprintf(stderr, "INTERNAL LONG DOUBLE PRINTING: ");
-       getrusage(RUSAGE_SELF, &last);
+    fprintf(stderr, "INTERNAL LONG DOUBLE PRINTING: ");
+    getrusage(RUSAGE_SELF, &last);
 
-       // do the job
-       for(j = 1; j < 11 ;j++) {
-               n = STORAGE_NUMBER_POSITIVE_MIN * j;
+    // do the job
+    for(j = 1; j < 11 ;j++) {
+        n = STORAGE_NUMBER_POSITIVE_MIN * j;
 
-               for(i = 0; i < loop ;i++) {
-                       n *= multiplier;
-                       if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
+        for(i = 0; i < loop ;i++) {
+            n *= multiplier;
+            if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
 
-                       print_calculated_number(buffer, n);
-               }
-       }
+            print_calculated_number(buffer, n);
+        }
+    }
 
-       getrusage(RUSAGE_SELF, &now);
-       user   = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
-       system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
-       total  = user + system;
-       mine = total;
+    getrusage(RUSAGE_SELF, &now);
+    user   = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
+    system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
+    total  = user + system;
+    mine = total;
 
-       fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
+    fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
-       fprintf(stderr, "SYSTEM   LONG DOUBLE PRINTING: ");
-       getrusage(RUSAGE_SELF, &last);
+    fprintf(stderr, "SYSTEM   LONG DOUBLE PRINTING: ");
+    getrusage(RUSAGE_SELF, &last);
 
-       // do the job
-       for(j = 1; j < 11 ;j++) {
-               n = STORAGE_NUMBER_POSITIVE_MIN * j;
+    // do the job
+    for(j = 1; j < 11 ;j++) {
+        n = STORAGE_NUMBER_POSITIVE_MIN * j;
 
-               for(i = 0; i < loop ;i++) {
-                       n *= multiplier;
-                       if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
-                       snprintfz(buffer, 100, CALCULATED_NUMBER_FORMAT, n);
-               }
-       }
+        for(i = 0; i < loop ;i++) {
+            n *= multiplier;
+            if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
+            snprintfz(buffer, 100, CALCULATED_NUMBER_FORMAT, n);
+        }
+    }
 
-       getrusage(RUSAGE_SELF, &now);
-       user   = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
-       system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
-       total  = user + system;
-       their = total;
+    getrusage(RUSAGE_SELF, &now);
+    user   = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
+    system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
+    total  = user + system;
+    their = total;
 
-       fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
+    fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
 
-       if(mine > total) {
-               fprintf(stderr, "NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
-       }
-       else {
-               fprintf(stderr, "NETDATA CODE IS  F A S T E R  %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
-       }
+    if(mine > total) {
+        fprintf(stderr, "NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
+    }
+    else {
+        fprintf(stderr, "NETDATA CODE IS  F A S T E R  %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
+    }
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
-       fprintf(stderr, "\nINTERNAL LONG DOUBLE PRINTING WITH PACK / UNPACK: ");
-       getrusage(RUSAGE_SELF, &last);
+    fprintf(stderr, "\nINTERNAL LONG DOUBLE PRINTING WITH PACK / UNPACK: ");
+    getrusage(RUSAGE_SELF, &last);
 
-       // do the job
-       for(j = 1; j < 11 ;j++) {
-               n = STORAGE_NUMBER_POSITIVE_MIN * j;
+    // do the job
+    for(j = 1; j < 11 ;j++) {
+        n = STORAGE_NUMBER_POSITIVE_MIN * j;
 
-               for(i = 0; i < loop ;i++) {
-                       n *= multiplier;
-                       if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
+        for(i = 0; i < loop ;i++) {
+            n *= multiplier;
+            if(n > STORAGE_NUMBER_POSITIVE_MAX) n = STORAGE_NUMBER_POSITIVE_MIN;
 
-                       s = pack_storage_number(n, 1);
-                       d = unpack_storage_number(s);
-                       print_calculated_number(buffer, d);
-               }
-       }
+            s = pack_storage_number(n, 1);
+            d = unpack_storage_number(s);
+            print_calculated_number(buffer, d);
+        }
+    }
 
-       getrusage(RUSAGE_SELF, &now);
-       user   = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
-       system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
-       total  = user + system;
-       mine = total;
-
-       fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
+    getrusage(RUSAGE_SELF, &now);
+    user   = now.ru_utime.tv_sec * 1000000ULL + now.ru_utime.tv_usec - last.ru_utime.tv_sec * 1000000ULL + last.ru_utime.tv_usec;
+    system = now.ru_stime.tv_sec * 1000000ULL + now.ru_stime.tv_usec - last.ru_stime.tv_sec * 1000000ULL + last.ru_stime.tv_usec;
+    total  = user + system;
+    mine = total;
+
+    fprintf(stderr, "user %0.5Lf, system %0.5Lf, total %0.5Lf\n", (long double)(user / 1000000.0), (long double)(system / 1000000.0), (long double)(total / 1000000.0));
 
-       if(mine > their) {
-               fprintf(stderr, "WITH PACKING UNPACKING NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
-       }
-       else {
-               fprintf(stderr, "EVEN WITH PACKING AND UNPACKING, NETDATA CODE IS  F A S T E R  %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
-       }
+    if(mine > their) {
+        fprintf(stderr, "WITH PACKING UNPACKING NETDATA CODE IS SLOWER %0.2Lf %%\n", (long double)(mine * 100.0 / their - 100.0));
+    }
+    else {
+        fprintf(stderr, "EVEN WITH PACKING AND UNPACKING, NETDATA CODE IS  F A S T E R  %0.2Lf %%\n", (long double)(their * 100.0 / mine - 100.0));
+    }
 
-       // ------------------------------------------------------------------------
+    // ------------------------------------------------------------------------
 
 }
 
 static int check_storage_number_exists() {
-       uint32_t flags = SN_EXISTS;
-
-
-       for(flags = 0; flags < 7 ; flags++) {
-               if(get_storage_number_flags(flags << 24) != flags << 24) {
-                       fprintf(stderr, "Flag 0x%08x is not checked correctly. It became 0x%08x\n", flags << 24, get_storage_number_flags(flags << 24));
-                       return 1;
-               }
-       }
-
-       flags = SN_EXISTS;
-       calculated_number n = 0.0;
-
-       storage_number s = pack_storage_number(n, flags);
-       calculated_number d = unpack_storage_number(s);
-       if(get_storage_number_flags(s) != flags) {
-               fprintf(stderr, "Wrong flags. Given %08x, Got %08x!\n", flags, get_storage_number_flags(s));
-               return 1;
-       }
-       if(n != d) {
-               fprintf(stderr, "Wrong number returned. Expected " CALCULATED_NUMBER_FORMAT ", returned " CALCULATED_NUMBER_FORMAT "!\n", n, d);
-               return 1;
-       }
-
-       return 0;
+    uint32_t flags = SN_EXISTS;
+
+
+    for(flags = 0; flags < 7 ; flags++) {
+        if(get_storage_number_flags(flags << 24) != flags << 24) {
+            fprintf(stderr, "Flag 0x%08x is not checked correctly. It became 0x%08x\n", flags << 24, get_storage_number_flags(flags << 24));
+            return 1;
+        }
+    }
+
+    flags = SN_EXISTS;
+    calculated_number n = 0.0;
+
+    storage_number s = pack_storage_number(n, flags);
+    calculated_number d = unpack_storage_number(s);
+    if(get_storage_number_flags(s) != flags) {
+        fprintf(stderr, "Wrong flags. Given %08x, Got %08x!\n", flags, get_storage_number_flags(s));
+        return 1;
+    }
+    if(n != d) {
+        fprintf(stderr, "Wrong number returned. Expected " CALCULATED_NUMBER_FORMAT ", returned " CALCULATED_NUMBER_FORMAT "!\n", n, d);
+        return 1;
+    }
+
+    return 0;
 }
 
 int unit_test_storage()
 {
-       if(check_storage_number_exists()) return 0;
+    if(check_storage_number_exists()) return 0;
 
-       calculated_number c, a = 0;
-       int i, j, g, r = 0;
+    calculated_number c, a = 0;
+    int i, j, g, r = 0;
 
-       for(g = -1; g <= 1 ; g++) {
-               a = 0;
+    for(g = -1; g <= 1 ; g++) {
+        a = 0;
 
-               if(!g) continue;
+        if(!g) continue;
 
-               for(j = 0; j < 9 ;j++) {
-                       a += 0.0000001;
-                       c = a * g;
-                       for(i = 0; i < 21 ;i++, c *= 10) {
-                               if(c > 0 && c < STORAGE_NUMBER_POSITIVE_MIN) continue;
-                               if(c < 0 && c > STORAGE_NUMBER_NEGATIVE_MAX) continue;
+        for(j = 0; j < 9 ;j++) {
+            a += 0.0000001;
+            c = a * g;
+            for(i = 0; i < 21 ;i++, c *= 10) {
+                if(c > 0 && c < STORAGE_NUMBER_POSITIVE_MIN) continue;
+                if(c < 0 && c > STORAGE_NUMBER_NEGATIVE_MAX) continue;
 
-                               if(check_storage_number(c, 1)) return 1;
-                       }
-               }
-       }
+                if(check_storage_number(c, 1)) return 1;
+            }
+        }
+    }
 
-       benchmark_storage_number(1000000, 2);
-       return r;
+    benchmark_storage_number(1000000, 2);
+    return r;
 }
 
 
 // --------------------------------------------------------------------------------------------------------------------
 
 struct feed_values {
-               unsigned long long microseconds;
-               collected_number value;
+        unsigned long long microseconds;
+        collected_number value;
 };
 
 struct test {
-       char name[100];
-       char description[1024];
+    char name[100];
+    char description[1024];
 
-       int update_every;
-       unsigned long long multiplier;
-       unsigned long long divisor;
-       int algorithm;
+    int update_every;
+    unsigned long long multiplier;
+    unsigned long long divisor;
+    int algorithm;
 
-       unsigned long feed_entries;
-       unsigned long result_entries;
-       struct feed_values *feed;
-       calculated_number *results;
+    unsigned long feed_entries;
+    unsigned long result_entries;
+    struct feed_values *feed;
+    calculated_number *results;
 
-       collected_number *feed2;
-       calculated_number *results2;
+    collected_number *feed2;
+    calculated_number *results2;
 };
 
 // --------------------------------------------------------------------------------------------------------------------
@@ -273,35 +260,35 @@ struct test {
 // test absolute values stored
 
 struct feed_values test1_feed[] = {
-               { 0, 10 },
-               { 1000000, 20 },
-               { 1000000, 30 },
-               { 1000000, 40 },
-               { 1000000, 50 },
-               { 1000000, 60 },
-               { 1000000, 70 },
-               { 1000000, 80 },
-               { 1000000, 90 },
-               { 1000000, 100 },
+        { 0, 10 },
+        { 1000000, 20 },
+        { 1000000, 30 },
+        { 1000000, 40 },
+        { 1000000, 50 },
+        { 1000000, 60 },
+        { 1000000, 70 },
+        { 1000000, 80 },
+        { 1000000, 90 },
+        { 1000000, 100 },
 };
 
 calculated_number test1_results[] = {
-               20, 30, 40, 50, 60, 70, 80, 90, 100
+        20, 30, 40, 50, 60, 70, 80, 90, 100
 };
 
 struct test test1 = {
-               "test1",                        // name
-               "test absolute values stored at exactly second boundaries",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_ABSOLUTE,        // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test1_feed,                     // feed
-               test1_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test1",            // name
+        "test absolute values stored at exactly second boundaries",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_ABSOLUTE,    // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test1_feed,         // feed
+        test1_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
@@ -309,667 +296,667 @@ struct test test1 = {
 // test absolute values stored in the middle of second boundaries
 
 struct feed_values test2_feed[] = {
-               { 500000, 10 },
-               { 1000000, 20 },
-               { 1000000, 30 },
-               { 1000000, 40 },
-               { 1000000, 50 },
-               { 1000000, 60 },
-               { 1000000, 70 },
-               { 1000000, 80 },
-               { 1000000, 90 },
-               { 1000000, 100 },
+        { 500000, 10 },
+        { 1000000, 20 },
+        { 1000000, 30 },
+        { 1000000, 40 },
+        { 1000000, 50 },
+        { 1000000, 60 },
+        { 1000000, 70 },
+        { 1000000, 80 },
+        { 1000000, 90 },
+        { 1000000, 100 },
 };
 
 calculated_number test2_results[] = {
-               20, 30, 40, 50, 60, 70, 80, 90, 100
+        20, 30, 40, 50, 60, 70, 80, 90, 100
 };
 
 struct test test2 = {
-               "test2",                        // name
-               "test absolute values stored in the middle of second boundaries",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_ABSOLUTE,        // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test2_feed,                     // feed
-               test2_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test2",            // name
+        "test absolute values stored in the middle of second boundaries",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_ABSOLUTE,    // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test2_feed,         // feed
+        test2_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test3
 
 struct feed_values test3_feed[] = {
-               { 0, 10 },
-               { 1000000, 20 },
-               { 1000000, 30 },
-               { 1000000, 40 },
-               { 1000000, 50 },
-               { 1000000, 60 },
-               { 1000000, 70 },
-               { 1000000, 80 },
-               { 1000000, 90 },
-               { 1000000, 100 },
+        { 0, 10 },
+        { 1000000, 20 },
+        { 1000000, 30 },
+        { 1000000, 40 },
+        { 1000000, 50 },
+        { 1000000, 60 },
+        { 1000000, 70 },
+        { 1000000, 80 },
+        { 1000000, 90 },
+        { 1000000, 100 },
 };
 
 calculated_number test3_results[] = {
-               10, 10, 10, 10, 10, 10, 10, 10, 10
+        10, 10, 10, 10, 10, 10, 10, 10, 10
 };
 
 struct test test3 = {
-               "test3",                        // name
-               "test incremental values stored at exactly second boundaries",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_INCREMENTAL,     // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test3_feed,                     // feed
-               test3_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test3",            // name
+        "test incremental values stored at exactly second boundaries",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_INCREMENTAL, // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test3_feed,         // feed
+        test3_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test4
 
 struct feed_values test4_feed[] = {
-               { 500000, 10 },
-               { 1000000, 20 },
-               { 1000000, 30 },
-               { 1000000, 40 },
-               { 1000000, 50 },
-               { 1000000, 60 },
-               { 1000000, 70 },
-               { 1000000, 80 },
-               { 1000000, 90 },
-               { 1000000, 100 },
+        { 500000, 10 },
+        { 1000000, 20 },
+        { 1000000, 30 },
+        { 1000000, 40 },
+        { 1000000, 50 },
+        { 1000000, 60 },
+        { 1000000, 70 },
+        { 1000000, 80 },
+        { 1000000, 90 },
+        { 1000000, 100 },
 };
 
 calculated_number test4_results[] = {
-               5, 10, 10, 10, 10, 10, 10, 10, 10
+        5, 10, 10, 10, 10, 10, 10, 10, 10
 };
 
 struct test test4 = {
-               "test4",                        // name
-               "test incremental values stored in the middle of second boundaries",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_INCREMENTAL,     // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test4_feed,                     // feed
-               test4_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test4",            // name
+        "test incremental values stored in the middle of second boundaries",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_INCREMENTAL, // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test4_feed,         // feed
+        test4_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test5
 
 struct feed_values test5_feed[] = {
-               { 500000, 1000 },
-               { 1000000, 2000 },
-               { 1000000, 2000 },
-               { 1000000, 2000 },
-               { 1000000, 3000 },
-               { 1000000, 2000 },
-               { 1000000, 2000 },
-               { 1000000, 2000 },
-               { 1000000, 2000 },
-               { 1000000, 2000 },
+        { 500000, 1000 },
+        { 1000000, 2000 },
+        { 1000000, 2000 },
+        { 1000000, 2000 },
+        { 1000000, 3000 },
+        { 1000000, 2000 },
+        { 1000000, 2000 },
+        { 1000000, 2000 },
+        { 1000000, 2000 },
+        { 1000000, 2000 },
 };
 
 calculated_number test5_results[] = {
-               500, 500, 0, 500, 500, 0, 0, 0, 0
+        500, 500, 0, 500, 500, 0, 0, 0, 0
 };
 
 struct test test5 = {
-               "test5",                        // name
-               "test incremental values ups and downs",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_INCREMENTAL,     // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test5_feed,                     // feed
-               test5_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test5",            // name
+        "test incremental values ups and downs",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_INCREMENTAL, // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test5_feed,         // feed
+        test5_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test6
 
 struct feed_values test6_feed[] = {
-               { 250000, 1000 },
-               { 250000, 2000 },
-               { 250000, 3000 },
-               { 250000, 4000 },
-               { 250000, 5000 },
-               { 250000, 6000 },
-               { 250000, 7000 },
-               { 250000, 8000 },
-               { 250000, 9000 },
-               { 250000, 10000 },
-               { 250000, 11000 },
-               { 250000, 12000 },
-               { 250000, 13000 },
-               { 250000, 14000 },
-               { 250000, 15000 },
-               { 250000, 16000 },
+        { 250000, 1000 },
+        { 250000, 2000 },
+        { 250000, 3000 },
+        { 250000, 4000 },
+        { 250000, 5000 },
+        { 250000, 6000 },
+        { 250000, 7000 },
+        { 250000, 8000 },
+        { 250000, 9000 },
+        { 250000, 10000 },
+        { 250000, 11000 },
+        { 250000, 12000 },
+        { 250000, 13000 },
+        { 250000, 14000 },
+        { 250000, 15000 },
+        { 250000, 16000 },
 };
 
 calculated_number test6_results[] = {
-               3000, 4000, 4000, 4000
+        3000, 4000, 4000, 4000
 };
 
 struct test test6 = {
-               "test6",                        // name
-               "test incremental values updated within the same second",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_INCREMENTAL,     // algorithm
-               16,                                     // feed entries
-               4,                                      // result entries
-               test6_feed,                     // feed
-               test6_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test6",            // name
+        "test incremental values updated within the same second",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_INCREMENTAL, // algorithm
+        16,                 // feed entries
+        4,                  // result entries
+        test6_feed,         // feed
+        test6_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test7
 
 struct feed_values test7_feed[] = {
-               { 500000, 1000 },
-               { 2000000, 2000 },
-               { 2000000, 3000 },
-               { 2000000, 4000 },
-               { 2000000, 5000 },
-               { 2000000, 6000 },
-               { 2000000, 7000 },
-               { 2000000, 8000 },
-               { 2000000, 9000 },
-               { 2000000, 10000 },
+        { 500000, 1000 },
+        { 2000000, 2000 },
+        { 2000000, 3000 },
+        { 2000000, 4000 },
+        { 2000000, 5000 },
+        { 2000000, 6000 },
+        { 2000000, 7000 },
+        { 2000000, 8000 },
+        { 2000000, 9000 },
+        { 2000000, 10000 },
 };
 
 calculated_number test7_results[] = {
-               250, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
+        250, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500
 };
 
 struct test test7 = {
-               "test7",                        // name
-               "test incremental values updated in long durations",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_INCREMENTAL,     // algorithm
-               10,                                     // feed entries
-               18,                                     // result entries
-               test7_feed,                     // feed
-               test7_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test7",            // name
+        "test incremental values updated in long durations",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_INCREMENTAL, // algorithm
+        10,                 // feed entries
+        18,                 // result entries
+        test7_feed,         // feed
+        test7_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test8
 
 struct feed_values test8_feed[] = {
-               { 500000, 1000 },
-               { 2000000, 2000 },
-               { 2000000, 3000 },
-               { 2000000, 4000 },
-               { 2000000, 5000 },
-               { 2000000, 6000 },
+        { 500000, 1000 },
+        { 2000000, 2000 },
+        { 2000000, 3000 },
+        { 2000000, 4000 },
+        { 2000000, 5000 },
+        { 2000000, 6000 },
 };
 
 calculated_number test8_results[] = {
-               1250, 2000, 2250, 3000, 3250, 4000, 4250, 5000, 5250, 6000
+        1250, 2000, 2250, 3000, 3250, 4000, 4250, 5000, 5250, 6000
 };
 
 struct test test8 = {
-               "test8",                        // name
-               "test absolute values updated in long durations",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_ABSOLUTE,        // algorithm
-               6,                                      // feed entries
-               10,                                     // result entries
-               test8_feed,                     // feed
-               test8_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test8",            // name
+        "test absolute values updated in long durations",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_ABSOLUTE,    // algorithm
+        6,                  // feed entries
+        10,                 // result entries
+        test8_feed,         // feed
+        test8_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test9
 
 struct feed_values test9_feed[] = {
-               { 250000, 1000 },
-               { 250000, 2000 },
-               { 250000, 3000 },
-               { 250000, 4000 },
-               { 250000, 5000 },
-               { 250000, 6000 },
-               { 250000, 7000 },
-               { 250000, 8000 },
-               { 250000, 9000 },
-               { 250000, 10000 },
-               { 250000, 11000 },
-               { 250000, 12000 },
-               { 250000, 13000 },
-               { 250000, 14000 },
-               { 250000, 15000 },
-               { 250000, 16000 },
+        { 250000, 1000 },
+        { 250000, 2000 },
+        { 250000, 3000 },
+        { 250000, 4000 },
+        { 250000, 5000 },
+        { 250000, 6000 },
+        { 250000, 7000 },
+        { 250000, 8000 },
+        { 250000, 9000 },
+        { 250000, 10000 },
+        { 250000, 11000 },
+        { 250000, 12000 },
+        { 250000, 13000 },
+        { 250000, 14000 },
+        { 250000, 15000 },
+        { 250000, 16000 },
 };
 
 calculated_number test9_results[] = {
-               4000, 8000, 12000, 16000
+        4000, 8000, 12000, 16000
 };
 
 struct test test9 = {
-               "test9",                        // name
-               "test absolute values updated within the same second",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_ABSOLUTE,        // algorithm
-               16,                                     // feed entries
-               4,                                      // result entries
-               test9_feed,                     // feed
-               test9_results,          // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test9",            // name
+        "test absolute values updated within the same second",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_ABSOLUTE,    // algorithm
+        16,                 // feed entries
+        4,                  // result entries
+        test9_feed,         // feed
+        test9_results,      // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test10
 
 struct feed_values test10_feed[] = {
-               { 500000,  1000 },
-               { 600000,  1000 +  600 },
-               { 200000,  1600 +  200 },
-               { 1000000, 1800 + 1000 },
-               { 200000,  2800 +  200 },
-               { 2000000, 3000 + 2000 },
-               { 600000,  5000 +  600 },
-               { 400000,  5600 +  400 },
-               { 900000,  6000 +  900 },
-               { 1000000, 6900 + 1000 },
+        { 500000,  1000 },
+        { 600000,  1000 +  600 },
+        { 200000,  1600 +  200 },
+        { 1000000, 1800 + 1000 },
+        { 200000,  2800 +  200 },
+        { 2000000, 3000 + 2000 },
+        { 600000,  5000 +  600 },
+        { 400000,  5600 +  400 },
+        { 900000,  6000 +  900 },
+        { 1000000, 6900 + 1000 },
 };
 
 calculated_number test10_results[] = {
-               500, 1000, 1000, 1000, 1000, 1000, 1000
+        500, 1000, 1000, 1000, 1000, 1000, 1000
 };
 
 struct test test10 = {
-               "test10",                       // name
-               "test incremental values updated in short and long durations",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_INCREMENTAL,     // algorithm
-               10,                                     // feed entries
-               7,                                      // result entries
-               test10_feed,            // feed
-               test10_results,         // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test10",           // name
+        "test incremental values updated in short and long durations",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_INCREMENTAL, // algorithm
+        10,                 // feed entries
+        7,                  // result entries
+        test10_feed,        // feed
+        test10_results,     // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test11
 
 struct feed_values test11_feed[] = {
-               { 0, 10 },
-               { 1000000, 20 },
-               { 1000000, 30 },
-               { 1000000, 40 },
-               { 1000000, 50 },
-               { 1000000, 60 },
-               { 1000000, 70 },
-               { 1000000, 80 },
-               { 1000000, 90 },
-               { 1000000, 100 },
+        { 0, 10 },
+        { 1000000, 20 },
+        { 1000000, 30 },
+        { 1000000, 40 },
+        { 1000000, 50 },
+        { 1000000, 60 },
+        { 1000000, 70 },
+        { 1000000, 80 },
+        { 1000000, 90 },
+        { 1000000, 100 },
 };
 
 collected_number test11_feed2[] = {
-       10, 20, 30, 40, 50, 60, 70, 80, 90, 100
+    10, 20, 30, 40, 50, 60, 70, 80, 90, 100
 };
 
 calculated_number test11_results[] = {
-               50, 50, 50, 50, 50, 50, 50, 50, 50
+        50, 50, 50, 50, 50, 50, 50, 50, 50
 };
 
 calculated_number test11_results2[] = {
-               50, 50, 50, 50, 50, 50, 50, 50, 50
+        50, 50, 50, 50, 50, 50, 50, 50, 50
 };
 
 struct test test11 = {
-               "test11",                       // name
-               "test percentage-of-incremental-row with equal values",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test11_feed,            // feed
-               test11_results,         // results
-               test11_feed2,           // feed2
-               test11_results2         // results2
+        "test11",           // name
+        "test percentage-of-incremental-row with equal values",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test11_feed,        // feed
+        test11_results,     // results
+        test11_feed2,       // feed2
+        test11_results2     // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test12
 
 struct feed_values test12_feed[] = {
-               { 0, 10 },
-               { 1000000, 20 },
-               { 1000000, 30 },
-               { 1000000, 40 },
-               { 1000000, 50 },
-               { 1000000, 60 },
-               { 1000000, 70 },
-               { 1000000, 80 },
-               { 1000000, 90 },
-               { 1000000, 100 },
+        { 0, 10 },
+        { 1000000, 20 },
+        { 1000000, 30 },
+        { 1000000, 40 },
+        { 1000000, 50 },
+        { 1000000, 60 },
+        { 1000000, 70 },
+        { 1000000, 80 },
+        { 1000000, 90 },
+        { 1000000, 100 },
 };
 
 collected_number test12_feed2[] = {
-       10*3, 20*3, 30*3, 40*3, 50*3, 60*3, 70*3, 80*3, 90*3, 100*3
+    10*3, 20*3, 30*3, 40*3, 50*3, 60*3, 70*3, 80*3, 90*3, 100*3
 };
 
 calculated_number test12_results[] = {
-               25, 25, 25, 25, 25, 25, 25, 25, 25
+        25, 25, 25, 25, 25, 25, 25, 25, 25
 };
 
 calculated_number test12_results2[] = {
-               75, 75, 75, 75, 75, 75, 75, 75, 75
+        75, 75, 75, 75, 75, 75, 75, 75, 75
 };
 
 struct test test12 = {
-               "test12",                       // name
-               "test percentage-of-incremental-row with equal values",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
-               10,                                     // feed entries
-               9,                                      // result entries
-               test12_feed,            // feed
-               test12_results,         // results
-               test12_feed2,           // feed2
-               test12_results2         // results2
+        "test12",           // name
+        "test percentage-of-incremental-row with equal values",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
+        10,                 // feed entries
+        9,                  // result entries
+        test12_feed,        // feed
+        test12_results,     // results
+        test12_feed2,       // feed2
+        test12_results2     // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 // test13
 
 struct feed_values test13_feed[] = {
-               { 500000,  1000 },
-               { 600000,  1000 +  600 },
-               { 200000,  1600 +  200 },
-               { 1000000, 1800 + 1000 },
-               { 200000,  2800 +  200 },
-               { 2000000, 3000 + 2000 },
-               { 600000,  5000 +  600 },
-               { 400000,  5600 +  400 },
-               { 900000,  6000 +  900 },
-               { 1000000, 6900 + 1000 },
+        { 500000,  1000 },
+        { 600000,  1000 +  600 },
+        { 200000,  1600 +  200 },
+        { 1000000, 1800 + 1000 },
+        { 200000,  2800 +  200 },
+        { 2000000, 3000 + 2000 },
+        { 600000,  5000 +  600 },
+        { 400000,  5600 +  400 },
+        { 900000,  6000 +  900 },
+        { 1000000, 6900 + 1000 },
 };
 
 calculated_number test13_results[] = {
-               83.3333300, 100, 100, 100, 100, 100, 100
+        83.3333300, 100, 100, 100, 100, 100, 100
 };
 
 struct test test13 = {
-               "test13",                       // name
-               "test incremental values updated in short and long durations",
-               1,                                      // update_every
-               1,                                      // multiplier
-               1,                                      // divisor
-               RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
-               10,                                     // feed entries
-               7,                                      // result entries
-               test13_feed,            // feed
-               test13_results,         // results
-               NULL,                           // feed2
-               NULL                            // results2
+        "test13",           // name
+        "test incremental values updated in short and long durations",
+        1,                  // update_every
+        1,                  // multiplier
+        1,                  // divisor
+        RRDDIM_PCENT_OVER_DIFF_TOTAL,   // algorithm
+        10,                 // feed entries
+        7,                  // result entries
+        test13_feed,        // feed
+        test13_results,     // results
+        NULL,               // feed2
+        NULL                // results2
 };
 
 // --------------------------------------------------------------------------------------------------------------------
 
 int run_test(struct test *test)
 {
-       fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
-
-       rrd_memory_mode = RRD_MEMORY_MODE_RAM;
-       rrd_update_every = test->update_every;
-
-       char name[101];
-       snprintfz(name, 100, "unittest-%s", test->name);
-
-       // create the chart
-       RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
-       RRDDIM *rd = rrddim_add(st, "dim1", NULL, test->multiplier, test->divisor, test->algorithm);
-       
-       RRDDIM *rd2 = NULL;
-       if(test->feed2)
-               rd2 = rrddim_add(st, "dim2", NULL, test->multiplier, test->divisor, test->algorithm);
-
-       st->debug = 1;
-
-       // feed it with the test data
-       unsigned long c;
-       for(c = 0; c < test->feed_entries; c++) {
-               if(debug_flags) fprintf(stderr, "\n\n");
-
-               if(c) {
-                       fprintf(stderr, "    > %s: feeding position %lu, after %llu microseconds\n", test->name, c+1, test->feed[c].microseconds);
-                       rrdset_next_usec(st, test->feed[c].microseconds);
-               }
-               else {
-                       fprintf(stderr, "    > %s: feeding position %lu\n", test->name, c+1);
-               }
-
-               fprintf(stderr, "       >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd->name, test->feed[c].value);
-               rrddim_set(st, "dim1", test->feed[c].value);
-
-               if(rd2) {
-                       fprintf(stderr, "       >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd2->name, test->feed2[c]);
-                       rrddim_set(st, "dim2", test->feed2[c]);
-               }
-
-               rrdset_done(st);
-
-               // align the first entry to second boundary
-               if(!c) {
-                       fprintf(stderr, "    > %s: fixing first collection time to be %llu microseconds to second boundary\n", test->name, test->feed[c].microseconds);
-                       rd->last_collected_time.tv_usec = st->last_collected_time.tv_usec = st->last_updated.tv_usec = test->feed[c].microseconds;
-               }
-       }
-
-       // check the result
-       int errors = 0;
-
-       if(st->counter != test->result_entries) {
-               fprintf(stderr, "    %s stored %lu entries, but we were expecting %lu, ### E R R O R ###\n", test->name, st->counter, test->result_entries);
-               errors++;
-       }
-
-       unsigned long max = (st->counter < test->result_entries)?st->counter:test->result_entries;
-       for(c = 0 ; c < max ; c++) {
-               calculated_number v = unpack_storage_number(rd->values[c]);
-               calculated_number n = test->results[c];
-               int same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
-               fprintf(stderr, "    %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
-               if(!same) errors++;
-
-               if(rd2) {
-                       v = unpack_storage_number(rd2->values[c]);
-                       n = test->results2[c];
-                       same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
-                       fprintf(stderr, "    %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd2->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
-                       if(!same) errors++;
-               }
-       }
-
-       return errors;
+    fprintf(stderr, "\nRunning test '%s':\n%s\n", test->name, test->description);
+
+    rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+    rrd_update_every = test->update_every;
+
+    char name[101];
+    snprintfz(name, 100, "unittest-%s", test->name);
+
+    // create the chart
+    RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
+    RRDDIM *rd = rrddim_add(st, "dim1", NULL, test->multiplier, test->divisor, test->algorithm);
+    
+    RRDDIM *rd2 = NULL;
+    if(test->feed2)
+        rd2 = rrddim_add(st, "dim2", NULL, test->multiplier, test->divisor, test->algorithm);
+
+    st->debug = 1;
+
+    // feed it with the test data
+    unsigned long c;
+    for(c = 0; c < test->feed_entries; c++) {
+        if(debug_flags) fprintf(stderr, "\n\n");
+
+        if(c) {
+            fprintf(stderr, "    > %s: feeding position %lu, after %llu microseconds\n", test->name, c+1, test->feed[c].microseconds);
+            rrdset_next_usec(st, test->feed[c].microseconds);
+        }
+        else {
+            fprintf(stderr, "    > %s: feeding position %lu\n", test->name, c+1);
+        }
+
+        fprintf(stderr, "       >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd->name, test->feed[c].value);
+        rrddim_set(st, "dim1", test->feed[c].value);
+
+        if(rd2) {
+            fprintf(stderr, "       >> %s with value " COLLECTED_NUMBER_FORMAT "\n", rd2->name, test->feed2[c]);
+            rrddim_set(st, "dim2", test->feed2[c]);
+        }
+
+        rrdset_done(st);
+
+        // align the first entry to second boundary
+        if(!c) {
+            fprintf(stderr, "    > %s: fixing first collection time to be %llu microseconds to second boundary\n", test->name, test->feed[c].microseconds);
+            rd->last_collected_time.tv_usec = st->last_collected_time.tv_usec = st->last_updated.tv_usec = test->feed[c].microseconds;
+        }
+    }
+
+    // check the result
+    int errors = 0;
+
+    if(st->counter != test->result_entries) {
+        fprintf(stderr, "    %s stored %lu entries, but we were expecting %lu, ### E R R O R ###\n", test->name, st->counter, test->result_entries);
+        errors++;
+    }
+
+    unsigned long max = (st->counter < test->result_entries)?st->counter:test->result_entries;
+    for(c = 0 ; c < max ; c++) {
+        calculated_number v = unpack_storage_number(rd->values[c]);
+        calculated_number n = test->results[c];
+        int same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
+        fprintf(stderr, "    %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
+        if(!same) errors++;
+
+        if(rd2) {
+            v = unpack_storage_number(rd2->values[c]);
+            n = test->results2[c];
+            same = (roundl(v * 10000000.0) == roundl(n * 10000000.0))?1:0;
+            fprintf(stderr, "    %s/%s: checking position %lu, expecting value " CALCULATED_NUMBER_FORMAT ", found " CALCULATED_NUMBER_FORMAT ", %s\n", test->name, rd2->name, c+1, n, v, (same)?"OK":"### E R R O R ###");
+            if(!same) errors++;
+        }
+    }
+
+    return errors;
 }
 
 int run_all_mockup_tests(void)
 {
-       if(run_test(&test1))
-               return 1;
+    if(run_test(&test1))
+        return 1;
 
-       if(run_test(&test2))
-               return 1;
+    if(run_test(&test2))
+        return 1;
 
-       if(run_test(&test3))
-               return 1;
+    if(run_test(&test3))
+        return 1;
 
-       if(run_test(&test4))
-               return 1;
+    if(run_test(&test4))
+        return 1;
 
-       if(run_test(&test5))
-               return 1;
+    if(run_test(&test5))
+        return 1;
 
-       if(run_test(&test6))
-               return 1;
+    if(run_test(&test6))
+        return 1;
 
-       if(run_test(&test7))
-               return 1;
+    if(run_test(&test7))
+        return 1;
 
-       if(run_test(&test8))
-               return 1;
+    if(run_test(&test8))
+        return 1;
 
-       if(run_test(&test9))
-               return 1;
+    if(run_test(&test9))
+        return 1;
 
-       if(run_test(&test10))
-               return 1;
+    if(run_test(&test10))
+        return 1;
 
-       if(run_test(&test11))
-               return 1;
+    if(run_test(&test11))
+        return 1;
 
-       if(run_test(&test12))
-               return 1;
+    if(run_test(&test12))
+        return 1;
 
-       if(run_test(&test13))
-               return 1;
+    if(run_test(&test13))
+        return 1;
 
-       return 0;
+    return 0;
 }
 
 int unit_test(long delay, long shift)
 {
-       static int repeat = 0;
-       repeat++;
-
-       char name[101];
-       snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
-
-       //debug_flags = 0xffffffff;
-       rrd_memory_mode = RRD_MEMORY_MODE_RAM;
-       rrd_update_every = 1;
-
-       int do_abs = 1;
-       int do_inc = 1;
-       int do_abst = 0;
-       int do_absi = 0;
-
-       RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
-       st->debug = 1;
-
-       RRDDIM *rdabs = NULL;
-       RRDDIM *rdinc = NULL;
-       RRDDIM *rdabst = NULL;
-       RRDDIM *rdabsi = NULL;
-
-       if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRDDIM_ABSOLUTE);
-       if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRDDIM_INCREMENTAL);
-       if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRDDIM_PCENT_OVER_ROW_TOTAL);
-       if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
-
-       long increment = 1000;
-       collected_number i = 0;
-
-       unsigned long c, dimensions = 0;
-       RRDDIM *rd;
-       for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
-
-       for(c = 0; c < 20 ;c++) {
-               i += increment;
-
-               fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i);
-               if(c) {
-                       rrdset_next_usec(st, delay);
-               }
-               if(do_abs) rrddim_set(st, "absolute", i);
-               if(do_inc) rrddim_set(st, "incremental", i);
-               if(do_abst) rrddim_set(st, "percentage-of-absolute-row", i);
-               if(do_absi) rrddim_set(st, "percentage-of-incremental-row", i);
-
-               if(!c) {
-                       gettimeofday(&st->last_collected_time, NULL);
-                       st->last_collected_time.tv_usec = shift;
-               }
-
-               // prevent it from deleting the dimensions
-               for(rd = st->dimensions ; rd ; rd = rd->next)
-                       rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec;
-
-               rrdset_done(st);
-       }
-
-       unsigned long oincrement = increment;
-       increment = increment * st->update_every * 1000000 / delay;
-       fprintf(stderr, "\n\nORIGINAL INCREMENT: %lu, INCREMENT %ld, DELAY %ld, SHIFT %ld\n", oincrement * 10, increment * 10, delay, shift);
-
-       int ret = 0;
-       storage_number sn;
-       calculated_number cn, v;
-       for(c = 0 ; c < st->counter ; c++) {
-               fprintf(stderr, "\nPOSITION: c = %lu, EXPECTED VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10);
-
-               for(rd = st->dimensions ; rd ; rd = rd->next) {
-                       sn = rd->values[c];
-                       cn = unpack_storage_number(sn);
-                       fprintf(stderr, "\t %s " CALCULATED_NUMBER_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ")   ->   ", rd->id, cn, sn);
-
-                       if(rd == rdabs) v =
-                               (         oincrement
-                                       // + (increment * (1000000 - shift) / 1000000)
-                                       + (c + 1) * increment
-                               );
-
-                       else if(rd == rdinc) v = (c?(increment):(increment * (1000000 - shift) / 1000000));
-                       else if(rd == rdabst) v = oincrement / dimensions / 10;
-                       else if(rd == rdabsi) v = oincrement / dimensions / 10;
-                       else v = 0;
-
-                       if(v == cn) fprintf(stderr, "passed.\n");
-                       else {
-                               fprintf(stderr, "ERROR! (expected " CALCULATED_NUMBER_FORMAT ")\n", v);
-                               ret = 1;
-                       }
-               }
-       }
-
-       if(ret)
-               fprintf(stderr, "\n\nUNIT TEST(%ld, %ld) FAILED\n\n", delay, shift);
-
-       return ret;
+    static int repeat = 0;
+    repeat++;
+
+    char name[101];
+    snprintfz(name, 100, "unittest-%d-%ld-%ld", repeat, delay, shift);
+
+    //debug_flags = 0xffffffff;
+    rrd_memory_mode = RRD_MEMORY_MODE_RAM;
+    rrd_update_every = 1;
+
+    int do_abs = 1;
+    int do_inc = 1;
+    int do_abst = 0;
+    int do_absi = 0;
+
+    RRDSET *st = rrdset_create("netdata", name, name, "netdata", NULL, "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE);
+    st->debug = 1;
+
+    RRDDIM *rdabs = NULL;
+    RRDDIM *rdinc = NULL;
+    RRDDIM *rdabst = NULL;
+    RRDDIM *rdabsi = NULL;
+
+    if(do_abs) rdabs = rrddim_add(st, "absolute", "absolute", 1, 1, RRDDIM_ABSOLUTE);
+    if(do_inc) rdinc = rrddim_add(st, "incremental", "incremental", 1, 1, RRDDIM_INCREMENTAL);
+    if(do_abst) rdabst = rrddim_add(st, "percentage-of-absolute-row", "percentage-of-absolute-row", 1, 1, RRDDIM_PCENT_OVER_ROW_TOTAL);
+    if(do_absi) rdabsi = rrddim_add(st, "percentage-of-incremental-row", "percentage-of-incremental-row", 1, 1, RRDDIM_PCENT_OVER_DIFF_TOTAL);
+
+    long increment = 1000;
+    collected_number i = 0;
+
+    unsigned long c, dimensions = 0;
+    RRDDIM *rd;
+    for(rd = st->dimensions ; rd ; rd = rd->next) dimensions++;
+
+    for(c = 0; c < 20 ;c++) {
+        i += increment;
+
+        fprintf(stderr, "\n\nLOOP = %lu, DELAY = %ld, VALUE = " COLLECTED_NUMBER_FORMAT "\n", c, delay, i);
+        if(c) {
+            rrdset_next_usec(st, delay);
+        }
+        if(do_abs) rrddim_set(st, "absolute", i);
+        if(do_inc) rrddim_set(st, "incremental", i);
+        if(do_abst) rrddim_set(st, "percentage-of-absolute-row", i);
+        if(do_absi) rrddim_set(st, "percentage-of-incremental-row", i);
+
+        if(!c) {
+            gettimeofday(&st->last_collected_time, NULL);
+            st->last_collected_time.tv_usec = shift;
+        }
+
+        // prevent it from deleting the dimensions
+        for(rd = st->dimensions ; rd ; rd = rd->next)
+            rd->last_collected_time.tv_sec = st->last_collected_time.tv_sec;
+
+        rrdset_done(st);
+    }
+
+    unsigned long oincrement = increment;
+    increment = increment * st->update_every * 1000000 / delay;
+    fprintf(stderr, "\n\nORIGINAL INCREMENT: %lu, INCREMENT %ld, DELAY %ld, SHIFT %ld\n", oincrement * 10, increment * 10, delay, shift);
+
+    int ret = 0;
+    storage_number sn;
+    calculated_number cn, v;
+    for(c = 0 ; c < st->counter ; c++) {
+        fprintf(stderr, "\nPOSITION: c = %lu, EXPECTED VALUE %lu\n", c, (oincrement + c * increment + increment * (1000000 - shift) / 1000000 )* 10);
+
+        for(rd = st->dimensions ; rd ; rd = rd->next) {
+            sn = rd->values[c];
+            cn = unpack_storage_number(sn);
+            fprintf(stderr, "\t %s " CALCULATED_NUMBER_FORMAT " (PACKED AS " STORAGE_NUMBER_FORMAT ")   ->   ", rd->id, cn, sn);
+
+            if(rd == rdabs) v =
+                (     oincrement
+                    // + (increment * (1000000 - shift) / 1000000)
+                    + (c + 1) * increment
+                );
+
+            else if(rd == rdinc) v = (c?(increment):(increment * (1000000 - shift) / 1000000));
+            else if(rd == rdabst) v = oincrement / dimensions / 10;
+            else if(rd == rdabsi) v = oincrement / dimensions / 10;
+            else v = 0;
+
+            if(v == cn) fprintf(stderr, "passed.\n");
+            else {
+                fprintf(stderr, "ERROR! (expected " CALCULATED_NUMBER_FORMAT ")\n", v);
+                ret = 1;
+            }
+        }
+    }
+
+    if(ret)
+        fprintf(stderr, "\n\nUNIT TEST(%ld, %ld) FAILED\n\n", delay, shift);
+
+    return ret;
 }
index bd22b8187fe05d335d3400a26386bd6c0c7cdbd8..6be4d96489351d84374642169f38e94d7f132996 100644 (file)
--- a/src/url.c
+++ b/src/url.c
@@ -1,13 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
 #include "common.h"
-#include "log.h"
-#include "url.h"
 
 // ----------------------------------------------------------------------------
 // URL encode / decode
 
 /* Converts a hex character to its integer value */
 char from_hex(char ch) {
-       return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
+    return (char)(isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10);
 }
 
 /* Converts an integer value to its hex character*/
 char to_hex(char code) {
-       static char hex[] = "0123456789abcdef";
-       return hex[code & 15];
+    static char hex[] = "0123456789abcdef";
+    return hex[code & 15];
 }
 
 /* Returns a url-encoded version of str */
 /* IMPORTANT: be sure to free() the returned string after use */
 char *url_encode(char *str) {
-       char *buf, *pbuf;
-
-       pbuf = buf = malloc(strlen(str) * 3 + 1);
+    char *buf, *pbuf;
 
-       if(!buf)
-               fatal("Cannot allocate memory.");
+    pbuf = buf = mallocz(strlen(str) * 3 + 1);
 
-       while (*str) {
-               if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
-                       *pbuf++ = *str;
+    while (*str) {
+        if (isalnum(*str) || *str == '-' || *str == '_' || *str == '.' || *str == '~')
+            *pbuf++ = *str;
 
-               else if (*str == ' ')
-                       *pbuf++ = '+';
+        else if (*str == ' ')
+            *pbuf++ = '+';
 
-               else
-                       *pbuf++ = '%', *pbuf++ = to_hex(*str >> 4), *pbuf++ = to_hex(*str & 15);
+        else
+            *pbuf++ = '%', *pbuf++ = to_hex(*str >> 4), *pbuf++ = to_hex(*str & 15);
 
-               str++;
-       }
-       *pbuf = '\0';
+        str++;
+    }
+    *pbuf = '\0';
 
-       // FIX: I think this is prudent. URLs can be as long as 2 KiB or more.
-       //      We allocated 3 times more space to accomodate %NN encoding of
-       //      non ASCII chars. If URL has none of these kind of chars we will
-       //      end up with a big unused buffer.
-       //
-       //      Try to shrink the buffer...
-       if (!!(pbuf = (char *)realloc(buf, strlen(buf)+1)))
-               buf = pbuf;
-
-       return buf;
+    pbuf = strdupz(buf);
+    freez(buf);
+    return pbuf;
 }
 
 /* Returns a url-decoded version of str */
 /* IMPORTANT: be sure to free() the returned string after use */
 char *url_decode(char *str) {
-       size_t size = strlen(str) + 1;
-
-       char *buf = malloc(size);
-       if(!buf)
-               fatal("Cannot allocate %zu bytes of memory.", size);
+    size_t size = strlen(str) + 1;
 
-       return url_decode_r(buf, str, size);
+    char *buf = mallocz(size);
+    return url_decode_r(buf, str, size);
 }
 
 char *url_decode_r(char *to, char *url, size_t size) {
-       char *s = url,           // source
-                *d = to,            // destination
-                *e = &to[size - 1]; // destination end
+    char *s = url,           // source
+         *d = to,            // destination
+         *e = &to[size - 1]; // destination end
 
-       while(*s && d < e) {
-               if(unlikely(*s == '%')) {
-                       if(likely(s[1] && s[2])) {
-                               *d++ = from_hex(s[1]) << 4 | from_hex(s[2]);
-                               s += 2;
-                       }
-               }
-               else if(unlikely(*s == '+'))
-                       *d++ = ' ';
+    while(*s && d < e) {
+        if(unlikely(*s == '%')) {
+            if(likely(s[1] && s[2])) {
+                *d++ = from_hex(s[1]) << 4 | from_hex(s[2]);
+                s += 2;
+            }
+        }
+        else if(unlikely(*s == '+'))
+            *d++ = ' ';
 
-               else
-                       *d++ = *s;
+        else
+            *d++ = *s;
 
-               s++;
-       }
+        s++;
+    }
 
-       *d = '\0';
+    *d = '\0';
 
-       return to;
+    return to;
 }
index 582890e1b566abe794a67c05447dab69da69d094..5b4f24623ba86ef230fc1c90e95a2bf283d34e94 100644 (file)
@@ -1,23 +1,11 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef STORAGE_WITH_MATH
-#include <math.h>
-#endif
-
 #include "common.h"
-#include "web_buffer.h"
-#include "log.h"
 
 #define BUFFER_OVERFLOW_EOF "EOF"
 
 static inline void buffer_overflow_init(BUFFER *b)
 {
-       b->buffer[b->size] = '\0';
-       strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF);
+    b->buffer[b->size] = '\0';
+    strcpy(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF);
 }
 
 #ifdef NETDATA_INTERNAL_CHECKS
@@ -28,49 +16,49 @@ static inline void buffer_overflow_init(BUFFER *b)
 
 static inline void _buffer_overflow_check(BUFFER *b, const char *file, const char *function, const unsigned long line)
 {
-       if(b->len > b->size) {
-               error("BUFFER: length %zu is above size %zu, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file);
-               b->len = b->size;
-       }
-
-       if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF)) {
-               error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file);
-               buffer_overflow_init(b);
-       }
+    if(b->len > b->size) {
+        error("BUFFER: length %zu is above size %zu, at line %lu, at function %s() of file '%s'.", b->len, b->size, line, function, file);
+        b->len = b->size;
+    }
+
+    if(b->buffer[b->size] != '\0' || strcmp(&b->buffer[b->size + 1], BUFFER_OVERFLOW_EOF)) {
+        error("BUFFER: detected overflow at line %lu, at function %s() of file '%s'.", line, function, file);
+        buffer_overflow_init(b);
+    }
 }
 
 
 void buffer_reset(BUFFER *wb)
 {
-       buffer_flush(wb);
+    buffer_flush(wb);
 
-       wb->contenttype = CT_TEXT_PLAIN;
-       wb->options = 0;
-       wb->date = 0;
+    wb->contenttype = CT_TEXT_PLAIN;
+    wb->options = 0;
+    wb->date = 0;
 
-       buffer_overflow_check(wb);
+    buffer_overflow_check(wb);
 }
 
 const char *buffer_tostring(BUFFER *wb)
 {
-       buffer_need_bytes(wb, 1);
-       wb->buffer[wb->len] = '\0';
+    buffer_need_bytes(wb, 1);
+    wb->buffer[wb->len] = '\0';
 
-       buffer_overflow_check(wb);
+    buffer_overflow_check(wb);
 
-       return(wb->buffer);
+    return(wb->buffer);
 }
 
 void buffer_char_replace(BUFFER *wb, char from, char to)
 {
-       char *s = wb->buffer, *end = &wb->buffer[wb->len];
+    char *s = wb->buffer, *end = &wb->buffer[wb->len];
 
-       while(s != end) {
-               if(*s == from) *s = to;
-               s++;
-       }
+    while(s != end) {
+        if(*s == from) *s = to;
+        s++;
+    }
 
-       buffer_overflow_check(wb);
+    buffer_overflow_check(wb);
 }
 
 // This trick seems to give an 80% speed increase in 32bit systems
@@ -79,171 +67,171 @@ void buffer_char_replace(BUFFER *wb, char from, char to)
 // print_calculated_number_lu_r() to print the rest with 32 bit arithmetic.
 
 inline char *print_number_lu_r(char *str, unsigned long uvalue) {
-       char *wstr = str;
+    char *wstr = str;
 
-       // print each digit
-       do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
-       return wstr;
+    // print each digit
+    do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
+    return wstr;
 }
 
 inline char *print_number_llu_r(char *str, unsigned long long uvalue) {
-       char *wstr = str;
+    char *wstr = str;
 
-       // print each digit
-       do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
-       if(uvalue) return print_number_lu_r(wstr, uvalue);
-       return wstr;
+    // print each digit
+    do *wstr++ = (char)('0' + (uvalue % 10)); while((uvalue /= 10) && uvalue > (unsigned long long)0xffffffff);
+    if(uvalue) return print_number_lu_r(wstr, uvalue);
+    return wstr;
 }
 
 void buffer_print_llu(BUFFER *wb, unsigned long long uvalue)
 {
-       buffer_need_bytes(wb, 50);
+    buffer_need_bytes(wb, 50);
 
-       char *str = &wb->buffer[wb->len];
-       char *wstr = str;
+    char *str = &wb->buffer[wb->len];
+    char *wstr = str;
 
 #ifdef ENVIRONMENT32
-       if(uvalue > (unsigned long long)0xffffffff)
-               wstr = print_number_llu_r(wstr, uvalue);
-       else
-               wstr = print_number_lu_r(wstr, uvalue);
+    if(uvalue > (unsigned long long)0xffffffff)
+        wstr = print_number_llu_r(wstr, uvalue);
+    else
+        wstr = print_number_lu_r(wstr, uvalue);
 #else
-       do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
+    do *wstr++ = (char)('0' + (uvalue % 10)); while(uvalue /= 10);
 #endif
 
-       // terminate it
-       *wstr = '\0';
+    // terminate it
+    *wstr = '\0';
 
-       // reverse it
-       char *begin = str, *end = wstr - 1, aux;
-       while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
+    // reverse it
+    char *begin = str, *end = wstr - 1, aux;
+    while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
 
-       // return the buffer length
-       wb->len += wstr - str;
+    // return the buffer length
+    wb->len += wstr - str;
 }
 
 void buffer_strcat(BUFFER *wb, const char *txt)
 {
-       if(unlikely(!txt || !*txt)) return;
-
-       buffer_need_bytes(wb, 1);
-
-       char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size];
-       long len = wb->len;
-
-       start = s;
-       while(*txt && s != end)
-               *s++ = *txt++;
-
-       len += s - start;
-
-       wb->len = len;
-       buffer_overflow_check(wb);
-
-       if(*txt) {
-               debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
-               len = strlen(txt);
-               buffer_increase(wb, len);
-               buffer_strcat(wb, txt);
-       }
-       else {
-               // terminate the string
-               // without increasing the length
-               buffer_need_bytes(wb, (size_t)1);
-               wb->buffer[wb->len] = '\0';
-       }
+    if(unlikely(!txt || !*txt)) return;
+
+    buffer_need_bytes(wb, 1);
+
+    char *s = &wb->buffer[wb->len], *start, *end = &wb->buffer[wb->size];
+    long len = wb->len;
+
+    start = s;
+    while(*txt && s != end)
+        *s++ = *txt++;
+
+    len += s - start;
+
+    wb->len = len;
+    buffer_overflow_check(wb);
+
+    if(*txt) {
+        debug(D_WEB_BUFFER, "strcat(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
+        len = strlen(txt);
+        buffer_increase(wb, len);
+        buffer_strcat(wb, txt);
+    }
+    else {
+        // terminate the string
+        // without increasing the length
+        buffer_need_bytes(wb, (size_t)1);
+        wb->buffer[wb->len] = '\0';
+    }
 }
 
 
 void buffer_snprintf(BUFFER *wb, size_t len, const char *fmt, ...)
 {
-       if(unlikely(!fmt || !*fmt)) return;
+    if(unlikely(!fmt || !*fmt)) return;
 
-       buffer_need_bytes(wb, len + 1);
+    buffer_need_bytes(wb, len + 1);
 
-       va_list args;
-       va_start(args, fmt);
-       wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
-       va_end(args);
+    va_list args;
+    va_start(args, fmt);
+    wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
+    va_end(args);
 
-       buffer_overflow_check(wb);
+    buffer_overflow_check(wb);
 
-       // the buffer is \0 terminated by vsnprintfz
+    // the buffer is \0 terminated by vsnprintfz
 }
 
 void buffer_vsprintf(BUFFER *wb, const char *fmt, va_list args)
 {
-       if(unlikely(!fmt || !*fmt)) return;
+    if(unlikely(!fmt || !*fmt)) return;
 
-       buffer_need_bytes(wb, 2);
+    buffer_need_bytes(wb, 2);
 
-       size_t len = wb->size - wb->len - 1;
+    size_t len = wb->size - wb->len - 1;
 
-       wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
+    wb->len += vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
 
-       buffer_overflow_check(wb);
+    buffer_overflow_check(wb);
 
-       // the buffer is \0 terminated by vsnprintfz
+    // the buffer is \0 terminated by vsnprintfz
 }
 
 void buffer_sprintf(BUFFER *wb, const char *fmt, ...)
 {
-       if(unlikely(!fmt || !*fmt)) return;
+    if(unlikely(!fmt || !*fmt)) return;
 
-       buffer_need_bytes(wb, 2);
+    buffer_need_bytes(wb, 2);
 
-       size_t len = wb->size - wb->len - 1;
-       size_t wrote;
+    size_t len = wb->size - wb->len - 1;
+    size_t wrote;
 
-       va_list args;
-       va_start(args, fmt);
-       wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
-       va_end(args);
+    va_list args;
+    va_start(args, fmt);
+    wrote = (size_t) vsnprintfz(&wb->buffer[wb->len], len, fmt, args);
+    va_end(args);
 
-       if(unlikely(wrote >= len)) {
-               // there is bug in vsnprintf() and it returns
-               // a number higher to len, but it does not
-               // overflow the buffer.
-               // our buffer overflow detector will log it
-               // if it does.
-               buffer_overflow_check(wb);
+    if(unlikely(wrote >= len)) {
+        // there is bug in vsnprintf() and it returns
+        // a number higher to len, but it does not
+        // overflow the buffer.
+        // our buffer overflow detector will log it
+        // if it does.
+        buffer_overflow_check(wb);
 
-               debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
-               buffer_need_bytes(wb, len + WEB_DATA_LENGTH_INCREASE_STEP);
+        debug(D_WEB_BUFFER, "web_buffer_sprintf(): increasing web_buffer at position %zu, size = %zu\n", wb->len, wb->size);
+        buffer_need_bytes(wb, len + WEB_DATA_LENGTH_INCREASE_STEP);
 
-               va_start(args, fmt);
-               buffer_vsprintf(wb, fmt, args);
-               va_end(args);
-       }
-       else
-               wb->len += wrote;
+        va_start(args, fmt);
+        buffer_vsprintf(wb, fmt, args);
+        va_end(args);
+    }
+    else
+        wb->len += wrote;
 
-       // the buffer is \0 terminated by vsnprintf
+    // the buffer is \0 terminated by vsnprintf
 }
 
 
 void buffer_rrd_value(BUFFER *wb, calculated_number value)
 {
-       buffer_need_bytes(wb, 50);
-       wb->len += print_calculated_number(&wb->buffer[wb->len], value);
+    buffer_need_bytes(wb, 50);
+    wb->len += print_calculated_number(&wb->buffer[wb->len], value);
 
-       // terminate it
-       buffer_need_bytes(wb, 1);
-       wb->buffer[wb->len] = '\0';
+    // terminate it
+    buffer_need_bytes(wb, 1);
+    wb->buffer[wb->len] = '\0';
 
-       buffer_overflow_check(wb);
+    buffer_overflow_check(wb);
 }
 
 // generate a javascript date, the fastest possible way...
 void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
 {
   //         10        20        30      = 35
-       // 01234567890123456789012345678901234
-       // Date(2014,04,01,03,28,20)
+    // 01234567890123456789012345678901234
+    // Date(2014,04,01,03,28,20)
 
-       buffer_need_bytes(wb, 30);
+    buffer_need_bytes(wb, 30);
 
-       char *b = &wb->buffer[wb->len], *p;
+    char *b = &wb->buffer[wb->len], *p;
   unsigned int *q = (unsigned int *)b;  
 
   #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -282,23 +270,23 @@ void buffer_jsdate(BUFFER *wb, int year, int month, int day, int hours, int minu
     *r++ = 0x2900;  // ")\0"
   #endif
 
-       wb->len += (size_t)((char *)r - b - 1);
+    wb->len += (size_t)((char *)r - b - 1);
 
-       // terminate it
-       wb->buffer[wb->len] = '\0';
-       buffer_overflow_check(wb);
+    // terminate it
+    wb->buffer[wb->len] = '\0';
+    buffer_overflow_check(wb);
 }
 
 // generate a date, the fastest possible way...
 void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minutes, int seconds)
 {
-       //         10        20        30      = 35
-       // 01234567890123456789012345678901234
-       // 2014-04-01 03:28:20
+    //         10        20        30      = 35
+    // 01234567890123456789012345678901234
+    // 2014-04-01 03:28:20
 
-       buffer_need_bytes(wb, 36);
+    buffer_need_bytes(wb, 36);
 
-       char *b = &wb->buffer[wb->len];
+    char *b = &wb->buffer[wb->len];
   char *p = b;
 
   *p++ = '0' + year / 1000; year %= 1000;
@@ -322,71 +310,56 @@ void buffer_date(BUFFER *wb, int year, int month, int day, int hours, int minute
   *p++ = '0' + seconds % 10;
   *p = '\0';
 
-       wb->len += (size_t)(p - b);
+    wb->len += (size_t)(p - b);
 
-       // terminate it
-       wb->buffer[wb->len] = '\0';
-       buffer_overflow_check(wb);
+    // terminate it
+    wb->buffer[wb->len] = '\0';
+    buffer_overflow_check(wb);
 }
 
 BUFFER *buffer_create(size_t size)
 {
-       BUFFER *b;
-
-       debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size);
-
-       b = calloc(1, sizeof(BUFFER));
-       if(!b) {
-               error("Cannot allocate a web_buffer.");
-               return NULL;
-       }
-
-       b->buffer = malloc(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
-       if(!b->buffer) {
-               error("Cannot allocate a buffer of size %zu.", size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
-               free(b);
-               return NULL;
-       }
-       b->buffer[0] = '\0';
-       b->size = size;
-       b->contenttype = CT_TEXT_PLAIN;
-       buffer_overflow_init(b);
-       buffer_overflow_check(b);
-
-       return(b);
+    BUFFER *b;
+
+    debug(D_WEB_BUFFER, "Creating new web buffer of size %zu.", size);
+
+    b = callocz(1, sizeof(BUFFER));
+    b->buffer = mallocz(size + sizeof(BUFFER_OVERFLOW_EOF) + 2);
+    b->buffer[0] = '\0';
+    b->size = size;
+    b->contenttype = CT_TEXT_PLAIN;
+    buffer_overflow_init(b);
+    buffer_overflow_check(b);
+
+    return(b);
 }
 
 void buffer_free(BUFFER *b)
 {
-       buffer_overflow_check(b);
+    buffer_overflow_check(b);
 
-       debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
+    debug(D_WEB_BUFFER, "Freeing web buffer of size %zu.", b->size);
 
-       free(b->buffer);
-       free(b);
+    freez(b->buffer);
+    freez(b);
 }
 
 void buffer_increase(BUFFER *b, size_t free_size_required)
 {
-       buffer_overflow_check(b);
-
-       size_t left = b->size - b->len;
+    buffer_overflow_check(b);
 
-       if(left >= free_size_required) return;
+    size_t left = b->size - b->len;
 
-       size_t increase = free_size_required - left;
-       if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
+    if(left >= free_size_required) return;
 
-       debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
+    size_t increase = free_size_required - left;
+    if(increase < WEB_DATA_LENGTH_INCREASE_STEP) increase = WEB_DATA_LENGTH_INCREASE_STEP;
 
-       b->buffer = realloc(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
-       if(!b->buffer)
-               fatal("Failed to increase data buffer from size %zu to %zu.",
-                       b->size + sizeof(BUFFER_OVERFLOW_EOF) + 2,
-                       b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
+    debug(D_WEB_BUFFER, "Increasing data buffer from size %zu to %zu.", b->size, b->size + increase);
 
-       b->size += increase;
+    b->buffer = reallocz(b->buffer, b->size + increase + sizeof(BUFFER_OVERFLOW_EOF) + 2);
+    b->size += increase;
 
-       buffer_overflow_init(b);
-       buffer_overflow_check(b);
+    buffer_overflow_init(b);
+    buffer_overflow_check(b);
 }
index 399eb84a52e189789ed065e208565fa5855fea63..c4cd0563212c0a835baa93b8517081213c68af02 100644 (file)
@@ -1,47 +1,43 @@
-#include <stdarg.h>
-#include <time.h>
-#include "storage_number.h"
-
 #ifndef NETDATA_WEB_BUFFER_H
 #define NETDATA_WEB_BUFFER_H 1
 
-#define WEB_DATA_LENGTH_INCREASE_STEP 16384
+#define WEB_DATA_LENGTH_INCREASE_STEP 1024
 
 typedef struct web_buffer {
-       size_t size;            // allocation size of buffer
-       size_t len;             // current data length in buffer
-       char *buffer;   // the buffer
-       uint8_t contenttype;
-       uint8_t options;
-       time_t date;    // the date this content has been generated
+    size_t size;        // allocation size of buffer
+    size_t len;     // current data length in buffer
+    char *buffer;   // the buffer
+    uint8_t contenttype;
+    uint8_t options;
+    time_t date;    // the date this content has been generated
 } BUFFER;
 
 // options
-#define WB_CONTENT_CACHEABLE                   1
-#define WB_CONTENT_NO_CACHEABLE                        2
+#define WB_CONTENT_CACHEABLE            1
+#define WB_CONTENT_NO_CACHEABLE         2
 
 // content-types
-#define CT_APPLICATION_JSON                            1
-#define CT_TEXT_PLAIN                                  2
-#define CT_TEXT_HTML                                   3
-#define CT_APPLICATION_X_JAVASCRIPT            4
-#define CT_TEXT_CSS                                            5
-#define CT_TEXT_XML                                            6
-#define CT_APPLICATION_XML                             7
-#define CT_TEXT_XSL                                            8
-#define CT_APPLICATION_OCTET_STREAM            9
-#define CT_APPLICATION_X_FONT_TRUETYPE 10
-#define CT_APPLICATION_X_FONT_OPENTYPE 11
-#define CT_APPLICATION_FONT_WOFF               12
-#define CT_APPLICATION_FONT_WOFF2              13
-#define CT_APPLICATION_VND_MS_FONTOBJ  14
-#define CT_IMAGE_SVG_XML                               15
-#define CT_IMAGE_PNG                                   16
-#define CT_IMAGE_JPG                                   17
-#define CT_IMAGE_GIF                                   18
-#define CT_IMAGE_XICON                                 19
-#define CT_IMAGE_ICNS                                  20
-#define CT_IMAGE_BMP                                   21
+#define CT_APPLICATION_JSON             1
+#define CT_TEXT_PLAIN                   2
+#define CT_TEXT_HTML                    3
+#define CT_APPLICATION_X_JAVASCRIPT     4
+#define CT_TEXT_CSS                     5
+#define CT_TEXT_XML                     6
+#define CT_APPLICATION_XML              7
+#define CT_TEXT_XSL                     8
+#define CT_APPLICATION_OCTET_STREAM     9
+#define CT_APPLICATION_X_FONT_TRUETYPE  10
+#define CT_APPLICATION_X_FONT_OPENTYPE  11
+#define CT_APPLICATION_FONT_WOFF        12
+#define CT_APPLICATION_FONT_WOFF2       13
+#define CT_APPLICATION_VND_MS_FONTOBJ   14
+#define CT_IMAGE_SVG_XML                15
+#define CT_IMAGE_PNG                    16
+#define CT_IMAGE_JPG                    17
+#define CT_IMAGE_GIF                    18
+#define CT_IMAGE_XICON                  19
+#define CT_IMAGE_ICNS                   20
+#define CT_IMAGE_BMP                    21
 
 #define buffer_strlen(wb) ((wb)->len)
 extern const char *buffer_tostring(BUFFER *wb);
index 250bbc8985ef8d35a88d8ea73122c633a060960f..daabee3df759e6bc9ac82aa4b8e2d4c523e672b3 100644 (file)
@@ -1,17 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <math.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdlib.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_buffer_svg.h"
 
 #define BADGE_HORIZONTAL_PADDING 4
 #define VERDANA_KERNING 0.5
 */
 
 double verdana11_widths[256] = {
-       [0] = 0.0,
-       [1] = 0.0,
-       [2] = 0.0,
-       [3] = 0.0,
-       [4] = 0.0,
-       [5] = 0.0,
-       [6] = 0.0,
-       [7] = 0.0,
-       [8] = 0.0,
-       [9] = 0.0,
-       [10] = 0.0,
-       [11] = 0.0,
-       [12] = 0.0,
-       [13] = 0.0,
-       [14] = 0.0,
-       [15] = 0.0,
-       [16] = 0.0,
-       [17] = 0.0,
-       [18] = 0.0,
-       [19] = 0.0,
-       [20] = 0.0,
-       [21] = 0.0,
-       [22] = 0.0,
-       [23] = 0.0,
-       [24] = 0.0,
-       [25] = 0.0,
-       [26] = 0.0,
-       [27] = 0.0,
-       [28] = 0.0,
-       [29] = 0.0,
-       [30] = 0.0,
-       [31] = 0.0,
-       [32] = 3.8671874999999996, //
-       [33] = 4.3291015625, // !
-       [34] = 5.048828125, // "
-       [35] = 9.001953125, // #
-       [36] = 6.9931640625, // $
-       [37] = 11.837890625, // %
-       [38] = 7.992187499999999, // &
-       [39] = 2.9541015625, // '
-       [40] = 4.9951171875, // (
-       [41] = 4.9951171875, // )
-       [42] = 6.9931640625, // *
-       [43] = 9.001953125, // +
-       [44] = 4.00146484375, // ,
-       [45] = 4.9951171875, // -
-       [46] = 4.00146484375, // .
-       [47] = 4.9951171875, // /
-       [48] = 6.9931640625, // 0
-       [49] = 6.9931640625, // 1
-       [50] = 6.9931640625, // 2
-       [51] = 6.9931640625, // 3
-       [52] = 6.9931640625, // 4
-       [53] = 6.9931640625, // 5
-       [54] = 6.9931640625, // 6
-       [55] = 6.9931640625, // 7
-       [56] = 6.9931640625, // 8
-       [57] = 6.9931640625, // 9
-       [58] = 4.9951171875, // :
-       [59] = 4.9951171875, // ;
-       [60] = 9.001953125, // <
-       [61] = 9.001953125, // =
-       [62] = 9.001953125, // >
-       [63] = 5.99951171875, // ?
-       [64] = 11.0, // @
-       [65] = 7.51953125, // A
-       [66] = 7.541015625, // B
-       [67] = 7.680664062499999, // C
-       [68] = 8.4755859375, // D
-       [69] = 6.95556640625, // E
-       [70] = 6.32177734375, // F
-       [71] = 8.529296875, // G
-       [72] = 8.26611328125, // H
-       [73] = 4.6298828125, // I
-       [74] = 5.00048828125, // J
-       [75] = 7.62158203125, // K
-       [76] = 6.123046875, // L
-       [77] = 9.2705078125, // M
-       [78] = 8.228515625, // N
-       [79] = 8.658203125, // O
-       [80] = 6.63330078125, // P
-       [81] = 8.658203125, // Q
-       [82] = 7.6484375, // R
-       [83] = 7.51953125, // S
-       [84] = 6.7783203125, // T
-       [85] = 8.05126953125, // U
-       [86] = 7.51953125, // V
-       [87] = 10.87646484375, // W
-       [88] = 7.53564453125, // X
-       [89] = 6.767578125, // Y
-       [90] = 7.53564453125, // Z
-       [91] = 4.9951171875, // [
-       [92] = 4.9951171875, // backslash
-       [93] = 4.9951171875, // ]
-       [94] = 9.001953125, // ^
-       [95] = 6.9931640625, // _
-       [96] = 6.9931640625, // `
-       [97] = 6.6064453125, // a
-       [98] = 6.853515625, // b
-       [99] = 5.73095703125, // c
-       [100] = 6.853515625, // d
-       [101] = 6.552734375, // e
-       [102] = 3.8671874999999996, // f
-       [103] = 6.853515625, // g
-       [104] = 6.9609375, // h
-       [105] = 3.0185546875, // i
-       [106] = 3.78662109375, // j
-       [107] = 6.509765625, // k
-       [108] = 3.0185546875, // l
-       [109] = 10.69921875, // m
-       [110] = 6.9609375, // n
-       [111] = 6.67626953125, // o
-       [112] = 6.853515625, // p
-       [113] = 6.853515625, // q
-       [114] = 4.6943359375, // r
-       [115] = 5.73095703125, // s
-       [116] = 4.33447265625, // t
-       [117] = 6.9609375, // u
-       [118] = 6.509765625, // v
-       [119] = 9.001953125, // w
-       [120] = 6.509765625, // x
-       [121] = 6.509765625, // y
-       [122] = 5.779296875, // z
-       [123] = 6.982421875, // {
-       [124] = 4.9951171875, // |
-       [125] = 6.982421875, // }
-       [126] = 9.001953125, // ~
-       [127] = 0.0,
-       [128] = 0.0,
-       [129] = 0.0,
-       [130] = 0.0,
-       [131] = 0.0,
-       [132] = 0.0,
-       [133] = 0.0,
-       [134] = 0.0,
-       [135] = 0.0,
-       [136] = 0.0,
-       [137] = 0.0,
-       [138] = 0.0,
-       [139] = 0.0,
-       [140] = 0.0,
-       [141] = 0.0,
-       [142] = 0.0,
-       [143] = 0.0,
-       [144] = 0.0,
-       [145] = 0.0,
-       [146] = 0.0,
-       [147] = 0.0,
-       [148] = 0.0,
-       [149] = 0.0,
-       [150] = 0.0,
-       [151] = 0.0,
-       [152] = 0.0,
-       [153] = 0.0,
-       [154] = 0.0,
-       [155] = 0.0,
-       [156] = 0.0,
-       [157] = 0.0,
-       [158] = 0.0,
-       [159] = 0.0,
-       [160] = 0.0,
-       [161] = 0.0,
-       [162] = 0.0,
-       [163] = 0.0,
-       [164] = 0.0,
-       [165] = 0.0,
-       [166] = 0.0,
-       [167] = 0.0,
-       [168] = 0.0,
-       [169] = 0.0,
-       [170] = 0.0,
-       [171] = 0.0,
-       [172] = 0.0,
-       [173] = 0.0,
-       [174] = 0.0,
-       [175] = 0.0,
-       [176] = 0.0,
-       [177] = 0.0,
-       [178] = 0.0,
-       [179] = 0.0,
-       [180] = 0.0,
-       [181] = 0.0,
-       [182] = 0.0,
-       [183] = 0.0,
-       [184] = 0.0,
-       [185] = 0.0,
-       [186] = 0.0,
-       [187] = 0.0,
-       [188] = 0.0,
-       [189] = 0.0,
-       [190] = 0.0,
-       [191] = 0.0,
-       [192] = 0.0,
-       [193] = 0.0,
-       [194] = 0.0,
-       [195] = 0.0,
-       [196] = 0.0,
-       [197] = 0.0,
-       [198] = 0.0,
-       [199] = 0.0,
-       [200] = 0.0,
-       [201] = 0.0,
-       [202] = 0.0,
-       [203] = 0.0,
-       [204] = 0.0,
-       [205] = 0.0,
-       [206] = 0.0,
-       [207] = 0.0,
-       [208] = 0.0,
-       [209] = 0.0,
-       [210] = 0.0,
-       [211] = 0.0,
-       [212] = 0.0,
-       [213] = 0.0,
-       [214] = 0.0,
-       [215] = 0.0,
-       [216] = 0.0,
-       [217] = 0.0,
-       [218] = 0.0,
-       [219] = 0.0,
-       [220] = 0.0,
-       [221] = 0.0,
-       [222] = 0.0,
-       [223] = 0.0,
-       [224] = 0.0,
-       [225] = 0.0,
-       [226] = 0.0,
-       [227] = 0.0,
-       [228] = 0.0,
-       [229] = 0.0,
-       [230] = 0.0,
-       [231] = 0.0,
-       [232] = 0.0,
-       [233] = 0.0,
-       [234] = 0.0,
-       [235] = 0.0,
-       [236] = 0.0,
-       [237] = 0.0,
-       [238] = 0.0,
-       [239] = 0.0,
-       [240] = 0.0,
-       [241] = 0.0,
-       [242] = 0.0,
-       [243] = 0.0,
-       [244] = 0.0,
-       [245] = 0.0,
-       [246] = 0.0,
-       [247] = 0.0,
-       [248] = 0.0,
-       [249] = 0.0,
-       [250] = 0.0,
-       [251] = 0.0,
-       [252] = 0.0,
-       [253] = 0.0,
-       [254] = 0.0,
-       [255] = 0.0
+    [0] = 0.0,
+    [1] = 0.0,
+    [2] = 0.0,
+    [3] = 0.0,
+    [4] = 0.0,
+    [5] = 0.0,
+    [6] = 0.0,
+    [7] = 0.0,
+    [8] = 0.0,
+    [9] = 0.0,
+    [10] = 0.0,
+    [11] = 0.0,
+    [12] = 0.0,
+    [13] = 0.0,
+    [14] = 0.0,
+    [15] = 0.0,
+    [16] = 0.0,
+    [17] = 0.0,
+    [18] = 0.0,
+    [19] = 0.0,
+    [20] = 0.0,
+    [21] = 0.0,
+    [22] = 0.0,
+    [23] = 0.0,
+    [24] = 0.0,
+    [25] = 0.0,
+    [26] = 0.0,
+    [27] = 0.0,
+    [28] = 0.0,
+    [29] = 0.0,
+    [30] = 0.0,
+    [31] = 0.0,
+    [32] = 3.8671874999999996, //
+    [33] = 4.3291015625, // !
+    [34] = 5.048828125, // "
+    [35] = 9.001953125, // #
+    [36] = 6.9931640625, // $
+    [37] = 11.837890625, // %
+    [38] = 7.992187499999999, // &
+    [39] = 2.9541015625, // '
+    [40] = 4.9951171875, // (
+    [41] = 4.9951171875, // )
+    [42] = 6.9931640625, // *
+    [43] = 9.001953125, // +
+    [44] = 4.00146484375, // ,
+    [45] = 4.9951171875, // -
+    [46] = 4.00146484375, // .
+    [47] = 4.9951171875, // /
+    [48] = 6.9931640625, // 0
+    [49] = 6.9931640625, // 1
+    [50] = 6.9931640625, // 2
+    [51] = 6.9931640625, // 3
+    [52] = 6.9931640625, // 4
+    [53] = 6.9931640625, // 5
+    [54] = 6.9931640625, // 6
+    [55] = 6.9931640625, // 7
+    [56] = 6.9931640625, // 8
+    [57] = 6.9931640625, // 9
+    [58] = 4.9951171875, // :
+    [59] = 4.9951171875, // ;
+    [60] = 9.001953125, // <
+    [61] = 9.001953125, // =
+    [62] = 9.001953125, // >
+    [63] = 5.99951171875, // ?
+    [64] = 11.0, // @
+    [65] = 7.51953125, // A
+    [66] = 7.541015625, // B
+    [67] = 7.680664062499999, // C
+    [68] = 8.4755859375, // D
+    [69] = 6.95556640625, // E
+    [70] = 6.32177734375, // F
+    [71] = 8.529296875, // G
+    [72] = 8.26611328125, // H
+    [73] = 4.6298828125, // I
+    [74] = 5.00048828125, // J
+    [75] = 7.62158203125, // K
+    [76] = 6.123046875, // L
+    [77] = 9.2705078125, // M
+    [78] = 8.228515625, // N
+    [79] = 8.658203125, // O
+    [80] = 6.63330078125, // P
+    [81] = 8.658203125, // Q
+    [82] = 7.6484375, // R
+    [83] = 7.51953125, // S
+    [84] = 6.7783203125, // T
+    [85] = 8.05126953125, // U
+    [86] = 7.51953125, // V
+    [87] = 10.87646484375, // W
+    [88] = 7.53564453125, // X
+    [89] = 6.767578125, // Y
+    [90] = 7.53564453125, // Z
+    [91] = 4.9951171875, // [
+    [92] = 4.9951171875, // backslash
+    [93] = 4.9951171875, // ]
+    [94] = 9.001953125, // ^
+    [95] = 6.9931640625, // _
+    [96] = 6.9931640625, // `
+    [97] = 6.6064453125, // a
+    [98] = 6.853515625, // b
+    [99] = 5.73095703125, // c
+    [100] = 6.853515625, // d
+    [101] = 6.552734375, // e
+    [102] = 3.8671874999999996, // f
+    [103] = 6.853515625, // g
+    [104] = 6.9609375, // h
+    [105] = 3.0185546875, // i
+    [106] = 3.78662109375, // j
+    [107] = 6.509765625, // k
+    [108] = 3.0185546875, // l
+    [109] = 10.69921875, // m
+    [110] = 6.9609375, // n
+    [111] = 6.67626953125, // o
+    [112] = 6.853515625, // p
+    [113] = 6.853515625, // q
+    [114] = 4.6943359375, // r
+    [115] = 5.73095703125, // s
+    [116] = 4.33447265625, // t
+    [117] = 6.9609375, // u
+    [118] = 6.509765625, // v
+    [119] = 9.001953125, // w
+    [120] = 6.509765625, // x
+    [121] = 6.509765625, // y
+    [122] = 5.779296875, // z
+    [123] = 6.982421875, // {
+    [124] = 4.9951171875, // |
+    [125] = 6.982421875, // }
+    [126] = 9.001953125, // ~
+    [127] = 0.0,
+    [128] = 0.0,
+    [129] = 0.0,
+    [130] = 0.0,
+    [131] = 0.0,
+    [132] = 0.0,
+    [133] = 0.0,
+    [134] = 0.0,
+    [135] = 0.0,
+    [136] = 0.0,
+    [137] = 0.0,
+    [138] = 0.0,
+    [139] = 0.0,
+    [140] = 0.0,
+    [141] = 0.0,
+    [142] = 0.0,
+    [143] = 0.0,
+    [144] = 0.0,
+    [145] = 0.0,
+    [146] = 0.0,
+    [147] = 0.0,
+    [148] = 0.0,
+    [149] = 0.0,
+    [150] = 0.0,
+    [151] = 0.0,
+    [152] = 0.0,
+    [153] = 0.0,
+    [154] = 0.0,
+    [155] = 0.0,
+    [156] = 0.0,
+    [157] = 0.0,
+    [158] = 0.0,
+    [159] = 0.0,
+    [160] = 0.0,
+    [161] = 0.0,
+    [162] = 0.0,
+    [163] = 0.0,
+    [164] = 0.0,
+    [165] = 0.0,
+    [166] = 0.0,
+    [167] = 0.0,
+    [168] = 0.0,
+    [169] = 0.0,
+    [170] = 0.0,
+    [171] = 0.0,
+    [172] = 0.0,
+    [173] = 0.0,
+    [174] = 0.0,
+    [175] = 0.0,
+    [176] = 0.0,
+    [177] = 0.0,
+    [178] = 0.0,
+    [179] = 0.0,
+    [180] = 0.0,
+    [181] = 0.0,
+    [182] = 0.0,
+    [183] = 0.0,
+    [184] = 0.0,
+    [185] = 0.0,
+    [186] = 0.0,
+    [187] = 0.0,
+    [188] = 0.0,
+    [189] = 0.0,
+    [190] = 0.0,
+    [191] = 0.0,
+    [192] = 0.0,
+    [193] = 0.0,
+    [194] = 0.0,
+    [195] = 0.0,
+    [196] = 0.0,
+    [197] = 0.0,
+    [198] = 0.0,
+    [199] = 0.0,
+    [200] = 0.0,
+    [201] = 0.0,
+    [202] = 0.0,
+    [203] = 0.0,
+    [204] = 0.0,
+    [205] = 0.0,
+    [206] = 0.0,
+    [207] = 0.0,
+    [208] = 0.0,
+    [209] = 0.0,
+    [210] = 0.0,
+    [211] = 0.0,
+    [212] = 0.0,
+    [213] = 0.0,
+    [214] = 0.0,
+    [215] = 0.0,
+    [216] = 0.0,
+    [217] = 0.0,
+    [218] = 0.0,
+    [219] = 0.0,
+    [220] = 0.0,
+    [221] = 0.0,
+    [222] = 0.0,
+    [223] = 0.0,
+    [224] = 0.0,
+    [225] = 0.0,
+    [226] = 0.0,
+    [227] = 0.0,
+    [228] = 0.0,
+    [229] = 0.0,
+    [230] = 0.0,
+    [231] = 0.0,
+    [232] = 0.0,
+    [233] = 0.0,
+    [234] = 0.0,
+    [235] = 0.0,
+    [236] = 0.0,
+    [237] = 0.0,
+    [238] = 0.0,
+    [239] = 0.0,
+    [240] = 0.0,
+    [241] = 0.0,
+    [242] = 0.0,
+    [243] = 0.0,
+    [244] = 0.0,
+    [245] = 0.0,
+    [246] = 0.0,
+    [247] = 0.0,
+    [248] = 0.0,
+    [249] = 0.0,
+    [250] = 0.0,
+    [251] = 0.0,
+    [252] = 0.0,
+    [253] = 0.0,
+    [254] = 0.0,
+    [255] = 0.0
 };
 
 // find the width of the string using the verdana 11points font
 // re-write the string in place, skiping zero-length characters
 static inline int verdana11_width(char *s) {
-       double w = 0.0;
-       char *d = s;
-
-       while(*s) {
-               double t = verdana11_widths[(unsigned char)*s];
-               if(t == 0.0)
-                       s++;
-               else {
-                       w += t + VERDANA_KERNING;
-                       if(d != s)
-                               *d++ = *s++;
-                       else
-                               d = ++s;
-               }
-       }
-
-       *d = '\0';
-       w -= VERDANA_KERNING;
-       return ceil(w);
+    double w = 0.0;
+    char *d = s;
+
+    while(*s) {
+        double t = verdana11_widths[(unsigned char)*s];
+        if(t == 0.0)
+            s++;
+        else {
+            w += t + VERDANA_KERNING;
+            if(d != s)
+                *d++ = *s++;
+            else
+                d = ++s;
+        }
+    }
+
+    *d = '\0';
+    w -= VERDANA_KERNING;
+    return ceil(w);
 }
 
 static inline size_t escape_xmlz(char *dst, const char *src, size_t len) {
-       size_t i = len;
-
-       // required escapes from
-       // https://github.com/badges/shields/blob/master/badge.js
-       while(*src && i) {
-               switch(*src) {
-                       case '\\':
-                               *dst++ = '/';
-                               src++;
-                               i--;
-                               break;
-
-                       case '&':
-                               if(i > 5) {
-                                       strcpy(dst, "&amp;");
-                                       i -= 5;
-                                       dst += 5;
-                                       src++;
-                               }
-                               else goto cleanup;
-                               break;
-
-                       case '<':
-                               if(i > 4) {
-                                       strcpy(dst, "&lt;");
-                                       i -= 4;
-                                       dst += 4;
-                                       src++;
-                               }
-                               else goto cleanup;
-                               break;
-
-                       case '>':
-                               if(i > 4) {
-                                       strcpy(dst, "&gt;");
-                                       i -= 4;
-                                       dst += 4;
-                                       src++;
-                               }
-                               else goto cleanup;
-                               break;
-
-                       case '"':
-                               if(i > 6) {
-                                       strcpy(dst, "&quot;");
-                                       i -= 6;
-                                       dst += 6;
-                                       src++;
-                               }
-                               else goto cleanup;
-                               break;
-
-                       case '\'':
-                               if(i > 6) {
-                                       strcpy(dst, "&apos;");
-                                       i -= 6;
-                                       dst += 6;
-                                       src++;
-                               }
-                               else goto cleanup;
-                               break;
-
-                       default:
-                               i--;
-                               *dst++ = *src++;
-                               break;
-               }
-       }
+    size_t i = len;
+
+    // required escapes from
+    // https://github.com/badges/shields/blob/master/badge.js
+    while(*src && i) {
+        switch(*src) {
+            case '\\':
+                *dst++ = '/';
+                src++;
+                i--;
+                break;
+
+            case '&':
+                if(i > 5) {
+                    strcpy(dst, "&amp;");
+                    i -= 5;
+                    dst += 5;
+                    src++;
+                }
+                else goto cleanup;
+                break;
+
+            case '<':
+                if(i > 4) {
+                    strcpy(dst, "&lt;");
+                    i -= 4;
+                    dst += 4;
+                    src++;
+                }
+                else goto cleanup;
+                break;
+
+            case '>':
+                if(i > 4) {
+                    strcpy(dst, "&gt;");
+                    i -= 4;
+                    dst += 4;
+                    src++;
+                }
+                else goto cleanup;
+                break;
+
+            case '"':
+                if(i > 6) {
+                    strcpy(dst, "&quot;");
+                    i -= 6;
+                    dst += 6;
+                    src++;
+                }
+                else goto cleanup;
+                break;
+
+            case '\'':
+                if(i > 6) {
+                    strcpy(dst, "&apos;");
+                    i -= 6;
+                    dst += 6;
+                    src++;
+                }
+                else goto cleanup;
+                break;
+
+            default:
+                i--;
+                *dst++ = *src++;
+                break;
+        }
+    }
 
 cleanup:
-       *dst = '\0';
-       return len - i;
+    *dst = '\0';
+    return len - i;
 }
 
 static inline const char *fix_units(const char *units) {
-       if(!units || !*units || !strcmp(units, "empty") || !strcmp(units, "null")) return "";
-       if(!strcmp(units, "percentage") || !strcmp(units, "percent") || !strcmp(units, "pcent")) return "%";
-       return units;
+    if(!units || !*units || !strcmp(units, "empty") || !strcmp(units, "null")) return "";
+    if(!strcmp(units, "percentage") || !strcmp(units, "percent") || !strcmp(units, "pcent")) return "%";
+    return units;
 }
 
 static inline const char *color_map(const char *color) {
-       // colors from:
-       // https://github.com/badges/shields/blob/master/colorscheme.json
-            if(!strcmp(color, "brightgreen")) return "#4c1";
-       else if(!strcmp(color, "green"))       return "#97CA00";
-       else if(!strcmp(color, "yellow"))      return "#dfb317";
-       else if(!strcmp(color, "yellowgreen")) return "#a4a61d";
-       else if(!strcmp(color, "orange"))      return "#fe7d37";
-       else if(!strcmp(color, "red"))         return "#e05d44";
-       else if(!strcmp(color, "blue"))        return "#007ec6";
-       else if(!strcmp(color, "grey"))        return "#555";
-       else if(!strcmp(color, "gray"))        return "#555";
-       else if(!strcmp(color, "lightgrey"))   return "#9f9f9f";
-       else if(!strcmp(color, "lightgray"))   return "#9f9f9f";
-       return color;
+    // colors from:
+    // https://github.com/badges/shields/blob/master/colorscheme.json
+         if(!strcmp(color, "brightgreen")) return "#4c1";
+    else if(!strcmp(color, "green"))       return "#97CA00";
+    else if(!strcmp(color, "yellow"))      return "#dfb317";
+    else if(!strcmp(color, "yellowgreen")) return "#a4a61d";
+    else if(!strcmp(color, "orange"))      return "#fe7d37";
+    else if(!strcmp(color, "red"))         return "#e05d44";
+    else if(!strcmp(color, "blue"))        return "#007ec6";
+    else if(!strcmp(color, "grey"))        return "#555";
+    else if(!strcmp(color, "gray"))        return "#555";
+    else if(!strcmp(color, "lightgrey"))   return "#9f9f9f";
+    else if(!strcmp(color, "lightgray"))   return "#9f9f9f";
+    return color;
 }
 
 static inline void calc_colorz(const char *color, char *final, size_t len, calculated_number value, int value_is_null) {
-       char color_buffer[256 + 1] = "";
-       char value_buffer[256 + 1] = "";
-       char comparison = '>';
-
-       // example input:
-       // color<max|color>min|color:null...
-
-       const char *c = color;
-       while(*c) {
-               char *dc = color_buffer, *dv = NULL;
-               size_t ci = 0, vi = 0;
-
-               const char *t = c;
-
-               while(*t && *t != '|') {
-                       switch(*t) {
-                               case ':':
-                                       comparison = '=';
-                                       dv = value_buffer;
-                                       break;
-
-                               case '}':
-                               case ')':
-                               case '>':
-                                       if(t[1] == '=') {
-                                               comparison = ')';
-                                               t++;
-                                       }
-                                       else
-                                               comparison = '>';
-                                       dv = value_buffer;
-                                       break;
-
-                               case '{':
-                               case '(':
-                               case '<':
-                                       if(t[1] == '=') {
-                                               comparison = '(';
-                                               t++;
-                                       }
-                                       else
-                                               comparison = '<';
-                                       dv = value_buffer;
-                                       break;
-
-                               default:
-                                       if(dv) {
-                                               if(vi < 256) {
-                                                       vi++;
-                                                       *dv++ = *t;
-                                               }
-                                       }
-                                       else {
-                                               if(ci < 256) {
-                                                       ci++;
-                                                       *dc++ = *t;
-                                               }
-                                       }
-                                       break;
-                       }
-
-                       t++;
-               }
-
-               // prepare for next iteration
-               if(*t == '|') t++;
-               c = t;
-
-               // do the math
-               *dc = '\0';
-               if(dv) {
-                       *dv = '\0';
-
-                       if(value_is_null) {
-                               if(!*value_buffer || !strcmp(value_buffer, "null"))
-                                       break;
-                       }
-                       else {
-                               calculated_number v = strtold(value_buffer, NULL);
-
-                                    if(comparison == '<' && value < v) break;
-                               else if(comparison == '(' && value <= v) break;
-                               else if(comparison == '>' && value > v) break;
-                               else if(comparison == ')' && value >= v) break;
-                               else if(comparison == '=' && value == v) break;
-                       }
-               }
-               else
-                       break;
-       }
-
-       const char *b;
-       if(color_buffer[0])
-               b = color_buffer;
-       else
-               b = color;
-
-       strncpyz(final, b, len);
+    char color_buffer[256 + 1] = "";
+    char value_buffer[256 + 1] = "";
+    char comparison = '>';
+
+    // example input:
+    // color<max|color>min|color:null...
+
+    const char *c = color;
+    while(*c) {
+        char *dc = color_buffer, *dv = NULL;
+        size_t ci = 0, vi = 0;
+
+        const char *t = c;
+
+        while(*t && *t != '|') {
+            switch(*t) {
+                case ':':
+                    comparison = '=';
+                    dv = value_buffer;
+                    break;
+
+                case '}':
+                case ')':
+                case '>':
+                    if(t[1] == '=') {
+                        comparison = ')';
+                        t++;
+                    }
+                    else
+                        comparison = '>';
+                    dv = value_buffer;
+                    break;
+
+                case '{':
+                case '(':
+                case '<':
+                    if(t[1] == '=') {
+                        comparison = '(';
+                        t++;
+                    }
+                    else
+                        comparison = '<';
+                    dv = value_buffer;
+                    break;
+
+                default:
+                    if(dv) {
+                        if(vi < 256) {
+                            vi++;
+                            *dv++ = *t;
+                        }
+                    }
+                    else {
+                        if(ci < 256) {
+                            ci++;
+                            *dc++ = *t;
+                        }
+                    }
+                    break;
+            }
+
+            t++;
+        }
+
+        // prepare for next iteration
+        if(*t == '|') t++;
+        c = t;
+
+        // do the math
+        *dc = '\0';
+        if(dv) {
+            *dv = '\0';
+
+            if(value_is_null) {
+                if(!*value_buffer || !strcmp(value_buffer, "null"))
+                    break;
+            }
+            else {
+                calculated_number v = strtold(value_buffer, NULL);
+
+                     if(comparison == '<' && value < v) break;
+                else if(comparison == '(' && value <= v) break;
+                else if(comparison == '>' && value > v) break;
+                else if(comparison == ')' && value >= v) break;
+                else if(comparison == '=' && value == v) break;
+            }
+        }
+        else
+            break;
+    }
+
+    const char *b;
+    if(color_buffer[0])
+        b = color_buffer;
+    else
+        b = color;
+
+    strncpyz(final, b, len);
 }
 
 void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int value_is_null, int precision) {
-       char label_buffer[256 + 1], value_string[512 + 1], value_color_buffer[256 + 1];
-       char label_escaped[256 + 1], value_escaped[512 + 1], label_color_escaped[256 + 1], value_color_escaped[256 + 1];
-       int label_width, value_width, total_width;
-
-       if(!label_color || !*label_color) label_color = "#555";
-       if(!value_color || !*value_color) value_color = (value_is_null)?"#999":"#4c1";
-
-       units = fix_units(units);
-       calc_colorz(value_color, value_color_buffer, 256, value, value_is_null);
-
-       char *separator = "";
-       if(isalnum(*units)) separator = " ";
-
-       if(value_is_null)
-               strcpy(value_string, "-");
-       else if(precision < 0) {
-               calculated_number abs = (value < (calculated_number)0)?-value:value;
-               if(abs > (calculated_number)1000.0)      snprintfz(value_string, 512, "%0.0Lf%s%s", (long double)value, separator, units);
-               else if(abs > (calculated_number)100.0)  snprintfz(value_string, 512, "%0.1Lf%s%s", (long double)value, separator, units);
-               else if(abs > (calculated_number)1.0)    snprintfz(value_string, 512, "%0.2Lf%s%s", (long double)value, separator, units);
-               else if(abs > (calculated_number)0.1)    snprintfz(value_string, 512, "%0.3Lf%s%s", (long double)value, separator, units);
-               else                                     snprintfz(value_string, 512, "%0.4Lf%s%s", (long double)value, separator, units);
-       }
-       else {
-               if(precision > 50) precision = 50;
-               snprintfz(value_string, 512, "%0.*Lf%s%s", precision, (long double)value, separator, units);
-       }
-
-       // we need to copy the label, since verdana11_width may write to it
-       strncpyz(label_buffer, label, 256);
-
-       label_width = verdana11_width(label_buffer) + (BADGE_HORIZONTAL_PADDING * 2);
-       value_width = verdana11_width(value_string) + (BADGE_HORIZONTAL_PADDING * 2);
-       total_width = label_width + value_width;
-
-       escape_xmlz(label_escaped, label_buffer, 256);
-       escape_xmlz(value_escaped, value_string, 256);
-       escape_xmlz(label_color_escaped, color_map(label_color), 256);
-       escape_xmlz(value_color_escaped, color_map(value_color_buffer), 256);
-
-       wb->contenttype = CT_IMAGE_SVG_XML;
-
-       // svg template from:
-       // https://raw.githubusercontent.com/badges/shields/master/templates/flat-template.svg
-       buffer_sprintf(wb,
-               "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"%d\" height=\"20\">"
-                       "<linearGradient id=\"smooth\" x2=\"0\" y2=\"100%%\">"
-                               "<stop offset=\"0\" stop-color=\"#bbb\" stop-opacity=\".1\"/>"
-                               "<stop offset=\"1\" stop-opacity=\".1\"/>"
-                       "</linearGradient>"
-                       "<mask id=\"round\">"
-                               "<rect width=\"%d\" height=\"20\" rx=\"3\" fill=\"#fff\"/>"
-                       "</mask>"
-                       "<g mask=\"url(#round)\">"
-                               "<rect width=\"%d\" height=\"20\" fill=\"%s\"/>"
-                               "<rect x=\"%d\" width=\"%d\" height=\"20\" fill=\"%s\"/>"
-                               "<rect width=\"%d\" height=\"20\" fill=\"url(#smooth)\"/>"
-                       "</g>"
-                       "<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"DejaVu Sans,Verdana,Geneva,sans-serif\" font-size=\"11\">"
-                               "<text x=\"%d\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
-                               "<text x=\"%d\" y=\"14\">%s</text>"
-                               "<text x=\"%d\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
-                               "<text x=\"%d\" y=\"14\">%s</text>"
-                       "</g>"
-               "</svg>",
-               total_width, total_width,
-               label_width, label_color_escaped,
-               label_width, value_width, value_color_escaped,
-               total_width,
-               label_width / 2, label_escaped,
-               label_width / 2, label_escaped,
-               label_width + value_width / 2 -1, value_escaped,
-               label_width + value_width / 2 -1, value_escaped);
+    char label_buffer[256 + 1], value_string[512 + 1], value_color_buffer[256 + 1];
+    char label_escaped[256 + 1], value_escaped[512 + 1], label_color_escaped[256 + 1], value_color_escaped[256 + 1];
+    int label_width, value_width, total_width;
+
+    if(!label_color || !*label_color) label_color = "#555";
+    if(!value_color || !*value_color) value_color = (value_is_null)?"#999":"#4c1";
+
+    units = fix_units(units);
+    calc_colorz(value_color, value_color_buffer, 256, value, value_is_null);
+
+    char *separator = "";
+    if(isalnum(*units)) separator = " ";
+
+    if(value_is_null)
+        strcpy(value_string, "-");
+    else if(precision < 0) {
+        calculated_number abs = (value < (calculated_number)0)?-value:value;
+        if(abs > (calculated_number)1000.0)      snprintfz(value_string, 512, "%0.0Lf%s%s", (long double)value, separator, units);
+        else if(abs > (calculated_number)100.0)  snprintfz(value_string, 512, "%0.1Lf%s%s", (long double)value, separator, units);
+        else if(abs > (calculated_number)1.0)    snprintfz(value_string, 512, "%0.2Lf%s%s", (long double)value, separator, units);
+        else if(abs > (calculated_number)0.1)    snprintfz(value_string, 512, "%0.3Lf%s%s", (long double)value, separator, units);
+        else                                     snprintfz(value_string, 512, "%0.4Lf%s%s", (long double)value, separator, units);
+    }
+    else {
+        if(precision > 50) precision = 50;
+        snprintfz(value_string, 512, "%0.*Lf%s%s", precision, (long double)value, separator, units);
+    }
+
+    // we need to copy the label, since verdana11_width may write to it
+    strncpyz(label_buffer, label, 256);
+
+    label_width = verdana11_width(label_buffer) + (BADGE_HORIZONTAL_PADDING * 2);
+    value_width = verdana11_width(value_string) + (BADGE_HORIZONTAL_PADDING * 2);
+    total_width = label_width + value_width;
+
+    escape_xmlz(label_escaped, label_buffer, 256);
+    escape_xmlz(value_escaped, value_string, 256);
+    escape_xmlz(label_color_escaped, color_map(label_color), 256);
+    escape_xmlz(value_color_escaped, color_map(value_color_buffer), 256);
+
+    wb->contenttype = CT_IMAGE_SVG_XML;
+
+    // svg template from:
+    // https://raw.githubusercontent.com/badges/shields/master/templates/flat-template.svg
+    buffer_sprintf(wb,
+        "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"%d\" height=\"20\">"
+            "<linearGradient id=\"smooth\" x2=\"0\" y2=\"100%%\">"
+                "<stop offset=\"0\" stop-color=\"#bbb\" stop-opacity=\".1\"/>"
+                "<stop offset=\"1\" stop-opacity=\".1\"/>"
+            "</linearGradient>"
+            "<mask id=\"round\">"
+                "<rect width=\"%d\" height=\"20\" rx=\"3\" fill=\"#fff\"/>"
+            "</mask>"
+            "<g mask=\"url(#round)\">"
+                "<rect width=\"%d\" height=\"20\" fill=\"%s\"/>"
+                "<rect x=\"%d\" width=\"%d\" height=\"20\" fill=\"%s\"/>"
+                "<rect width=\"%d\" height=\"20\" fill=\"url(#smooth)\"/>"
+            "</g>"
+            "<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"DejaVu Sans,Verdana,Geneva,sans-serif\" font-size=\"11\">"
+                "<text x=\"%d\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
+                "<text x=\"%d\" y=\"14\">%s</text>"
+                "<text x=\"%d\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">%s</text>"
+                "<text x=\"%d\" y=\"14\">%s</text>"
+            "</g>"
+        "</svg>",
+        total_width, total_width,
+        label_width, label_color_escaped,
+        label_width, value_width, value_color_escaped,
+        total_width,
+        label_width / 2, label_escaped,
+        label_width / 2, label_escaped,
+        label_width + value_width / 2 -1, value_escaped,
+        label_width + value_width / 2 -1, value_escaped);
 }
index b853e97dde49d1c3c75ec5f3f7a4c558225bc7ba..1281847eb49c4721c7d00cfe3cd61257f8981215 100644 (file)
@@ -1,6 +1,3 @@
-#include "web_buffer.h"
-#include "dictionary.h"
-
 #ifndef NETDATA_WEB_BUFFER_SVG_H
 #define NETDATA_WEB_BUFFER_SVG_H 1
 
index 4714ef8d979754d036944c53bbf703779aed344b..8976e925cb8bbf74d74f3ebc31509c18668a2b67 100644 (file)
@@ -1,38 +1,4 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <malloc.h>
-#include <pwd.h>
-#include <grp.h>
-#include <ctype.h>
-#include <poll.h>
-
-// TCP_CORK
-#include <netinet/tcp.h>
-
 #include "common.h"
-#include "log.h"
-#include "main.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_server.h"
-#include "global_statistics.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "registry.h"
-#include "web_buffer_svg.h"
-#include "web_client.h"
 
 #define INITIAL_WEB_DATA_LENGTH 16384
 #define WEB_REQUEST_LENGTH 16384
@@ -50,1605 +16,1565 @@ unsigned long long web_clients_count = 0;
 
 inline int web_client_crock_socket(struct web_client *w) {
 #ifdef TCP_CORK
-       if(likely(!w->tcp_cork && w->ofd != -1)) {
-               w->tcp_cork = 1;
-               if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
-                       error("%llu: failed to enable TCP_CORK on socket.", w->id);
-                       w->tcp_cork = 0;
-                       return -1;
-               }
-       }
+    if(likely(!w->tcp_cork && w->ofd != -1)) {
+        w->tcp_cork = 1;
+        if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
+            error("%llu: failed to enable TCP_CORK on socket.", w->id);
+            w->tcp_cork = 0;
+            return -1;
+        }
+    }
 #endif /* TCP_CORK */
 
-       return 0;
+    return 0;
 }
 
 inline int web_client_uncrock_socket(struct web_client *w) {
 #ifdef TCP_CORK
-       if(likely(w->tcp_cork && w->ofd != -1)) {
-               w->tcp_cork = 0;
-               if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
-                       error("%llu: failed to disable TCP_CORK on socket.", w->id);
-                       w->tcp_cork = 1;
-                       return -1;
-               }
-       }
+    if(likely(w->tcp_cork && w->ofd != -1)) {
+        w->tcp_cork = 0;
+        if(unlikely(setsockopt(w->ofd, IPPROTO_TCP, TCP_CORK, (char *) &w->tcp_cork, sizeof(int)) != 0)) {
+            error("%llu: failed to disable TCP_CORK on socket.", w->id);
+            w->tcp_cork = 1;
+            return -1;
+        }
+    }
 #endif /* TCP_CORK */
 
-       return 0;
+    return 0;
 }
 
 struct web_client *web_client_create(int listener)
 {
-       struct web_client *w;
-
-       w = calloc(1, sizeof(struct web_client));
-       if(!w) {
-               error("Cannot allocate new web_client memory.");
-               return NULL;
-       }
-
-       w->id = ++web_clients_count;
-       w->mode = WEB_CLIENT_MODE_NORMAL;
-
-       {
-               struct sockaddr *sadr;
-               socklen_t addrlen;
-
-               sadr = (struct sockaddr*) &w->clientaddr;
-               addrlen = sizeof(w->clientaddr);
-
-               w->ifd = accept4(listener, sadr, &addrlen, SOCK_NONBLOCK);
-               if (w->ifd == -1) {
-                       error("%llu: Cannot accept new incoming connection.", w->id);
-                       free(w);
-                       return NULL;
-               }
-               w->ofd = w->ifd;
-
-               if(getnameinfo(sadr, addrlen, w->client_ip, NI_MAXHOST, w->client_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
-                       error("Cannot getnameinfo() on received client connection.");
-                       strncpyz(w->client_ip,   "UNKNOWN", NI_MAXHOST);
-                       strncpyz(w->client_port, "UNKNOWN", NI_MAXSERV);
-               }
-               w->client_ip[NI_MAXHOST]   = '\0';
-               w->client_port[NI_MAXSERV] = '\0';
-
-               switch(sadr->sa_family) {
-               case AF_INET:
-                       debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
-                       break;
-
-               case AF_INET6:
-                       if(strncmp(w->client_ip, "::ffff:", 7) == 0) {
-                               memmove(w->client_ip, &w->client_ip[7], strlen(&w->client_ip[7]) + 1);
-                               debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
-                       }
-                       else
-                               debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv6 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
-                       break;
-
-               default:
-                       debug(D_WEB_CLIENT_ACCESS, "%llu: New UNKNOWN web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
-                       break;
-               }
-
-               int flag = 1;
-               if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0)
-                       error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
-
-               flag = 1;
-               if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0)
-                       error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
-       }
-
-       w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
-       if(unlikely(!w->response.data)) {
-               // no need for error log - web_buffer_create already logged the error
-               close(w->ifd);
-               free(w);
-               return NULL;
-       }
-
-       w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
-       if(unlikely(!w->response.header)) {
-               // no need for error log - web_buffer_create already logged the error
-               buffer_free(w->response.data);
-               close(w->ifd);
-               free(w);
-               return NULL;
-       }
-
-       w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
-       if(unlikely(!w->response.header_output)) {
-               // no need for error log - web_buffer_create already logged the error
-               buffer_free(w->response.header);
-               buffer_free(w->response.data);
-               close(w->ifd);
-               free(w);
-               return NULL;
-       }
-
-       w->origin[0] = '*';
-       w->wait_receive = 1;
-
-       if(web_clients) web_clients->prev = w;
-       w->next = web_clients;
-       web_clients = w;
-
-       global_statistics.connected_clients++;
-
-       return(w);
+    struct web_client *w;
+
+    w = callocz(1, sizeof(struct web_client));
+    w->id = ++web_clients_count;
+    w->mode = WEB_CLIENT_MODE_NORMAL;
+
+    {
+        struct sockaddr *sadr;
+        socklen_t addrlen;
+
+        sadr = (struct sockaddr*) &w->clientaddr;
+        addrlen = sizeof(w->clientaddr);
+
+        w->ifd = accept4(listener, sadr, &addrlen, SOCK_NONBLOCK);
+        if (w->ifd == -1) {
+            error("%llu: Cannot accept new incoming connection.", w->id);
+            freez(w);
+            return NULL;
+        }
+        w->ofd = w->ifd;
+
+        if(getnameinfo(sadr, addrlen, w->client_ip, NI_MAXHOST, w->client_port, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+            error("Cannot getnameinfo() on received client connection.");
+            strncpyz(w->client_ip,   "UNKNOWN", NI_MAXHOST);
+            strncpyz(w->client_port, "UNKNOWN", NI_MAXSERV);
+        }
+        w->client_ip[NI_MAXHOST]   = '\0';
+        w->client_port[NI_MAXSERV] = '\0';
+
+        switch(sadr->sa_family) {
+        case AF_INET:
+            debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+            break;
+
+        case AF_INET6:
+            if(strncmp(w->client_ip, "::ffff:", 7) == 0) {
+                memmove(w->client_ip, &w->client_ip[7], strlen(&w->client_ip[7]) + 1);
+                debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv4 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+            }
+            else
+                debug(D_WEB_CLIENT_ACCESS, "%llu: New IPv6 web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+            break;
+
+        default:
+            debug(D_WEB_CLIENT_ACCESS, "%llu: New UNKNOWN web client from %s port %s on socket %d.", w->id, w->client_ip, w->client_port, w->ifd);
+            break;
+        }
+
+        int flag = 1;
+        if(setsockopt(w->ofd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) != 0)
+            error("%llu: failed to enable TCP_NODELAY on socket.", w->id);
+
+        flag = 1;
+        if(setsockopt(w->ifd, SOL_SOCKET, SO_KEEPALIVE, (char *) &flag, sizeof(int)) != 0)
+            error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
+    }
+
+    w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
+    w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
+    w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
+    w->origin[0] = '*';
+    w->wait_receive = 1;
+
+    if(web_clients) web_clients->prev = w;
+    w->next = web_clients;
+    web_clients = w;
+
+    web_client_connected();
+
+    return(w);
 }
 
 void web_client_reset(struct web_client *w) {
-       web_client_uncrock_socket(w);
+    web_client_uncrock_socket(w);
 
-       debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
+    debug(D_WEB_CLIENT, "%llu: Reseting client.", w->id);
 
-       if(likely(w->last_url[0])) {
-               struct timeval tv;
-               gettimeofday(&tv, NULL);
+    if(likely(w->last_url[0])) {
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
 
-               size_t size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
-               size_t sent = size;
+        size_t size = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->response.rlen:w->response.data->len;
+        size_t sent = size;
 #ifdef NETDATA_WITH_ZLIB
-               if(likely(w->response.zoutput)) sent = (size_t)w->response.zstream.total_out;
+        if(likely(w->response.zoutput)) sent = (size_t)w->response.zstream.total_out;
 #endif
 
-               // --------------------------------------------------------------------
-               // global statistics
-
-               if(web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
-                       global_statistics_lock();
-
-               global_statistics.web_requests++;
-               global_statistics.web_usec += usecdiff(&tv, &w->tv_in);
-               global_statistics.bytes_received += w->stats_received_bytes;
-               global_statistics.bytes_sent += w->stats_sent_bytes;
-               global_statistics.content_size += size;
-               global_statistics.compressed_content_size += sent;
-
-               if(web_server_mode == WEB_SERVER_MODE_MULTI_THREADED)
-                       global_statistics_unlock();
-
-               w->stats_received_bytes = 0;
-               w->stats_sent_bytes = 0;
-
-
-               // --------------------------------------------------------------------
-               // access log
-
-               log_access("%llu: (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'",
-                                  w->id,
-                                  sent, size, -((size > 0) ? ((size - sent) / (double) size * 100.0) : 0.0),
-                                  usecdiff(&w->tv_ready, &w->tv_in) / 1000.0,
-                                  usecdiff(&tv, &w->tv_ready) / 1000.0,
-                                  usecdiff(&tv, &w->tv_in) / 1000.0,
-                                  (w->mode == WEB_CLIENT_MODE_FILECOPY) ? "filecopy" : ((w->mode == WEB_CLIENT_MODE_OPTIONS)
-                                                                                                                                                ? "options" : "data"),
-                                  w->response.code,
-                                  w->last_url
-               );
-       }
-
-       if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
-               if(w->ifd != w->ofd) {
-                       debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
-                       if(w->ifd != -1) close(w->ifd);
-                       w->ifd = w->ofd;
-               }
-       }
-
-       w->last_url[0] = '\0';
-       w->cookie1[0] = '\0';
-       w->cookie2[0] = '\0';
-       w->origin[0] = '*';
-       w->origin[1] = '\0';
-
-       w->mode = WEB_CLIENT_MODE_NORMAL;
-
-       w->tcp_cork = 0;
-       w->donottrack = 0;
-       w->tracking_required = 0;
-       w->keepalive = 0;
-       w->decoded_url[0] = '\0';
-
-       buffer_reset(w->response.header_output);
-       buffer_reset(w->response.header);
-       buffer_reset(w->response.data);
-       w->response.rlen = 0;
-       w->response.sent = 0;
-       w->response.code = 0;
-
-       w->wait_receive = 1;
-       w->wait_send = 0;
-
-       w->response.zoutput = 0;
-
-       // if we had enabled compression, release it
+        // --------------------------------------------------------------------
+        // global statistics
+
+        finished_web_request_statistics(usec_dt(&tv, &w->tv_in),
+                                        w->stats_received_bytes,
+                                        w->stats_sent_bytes,
+                                        size,
+                                        sent);
+
+        w->stats_received_bytes = 0;
+        w->stats_sent_bytes = 0;
+
+
+        // --------------------------------------------------------------------
+        // access log
+
+        log_access("%llu: (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'",
+                   w->id,
+                   sent, size, -((size > 0) ? ((size - sent) / (double) size * 100.0) : 0.0),
+                   usec_dt(&w->tv_ready, &w->tv_in) / 1000.0,
+                   usec_dt(&tv, &w->tv_ready) / 1000.0,
+                   usec_dt(&tv, &w->tv_in) / 1000.0,
+                   (w->mode == WEB_CLIENT_MODE_FILECOPY) ? "filecopy" : ((w->mode == WEB_CLIENT_MODE_OPTIONS)
+                                                                         ? "options" : "data"),
+                   w->response.code,
+                   w->last_url
+        );
+    }
+
+    if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY)) {
+        if(w->ifd != w->ofd) {
+            debug(D_WEB_CLIENT, "%llu: Closing filecopy input file descriptor %d.", w->id, w->ifd);
+            if(w->ifd != -1) close(w->ifd);
+            w->ifd = w->ofd;
+        }
+    }
+
+    w->last_url[0] = '\0';
+    w->cookie1[0] = '\0';
+    w->cookie2[0] = '\0';
+    w->origin[0] = '*';
+    w->origin[1] = '\0';
+
+    w->mode = WEB_CLIENT_MODE_NORMAL;
+
+    w->tcp_cork = 0;
+    w->donottrack = 0;
+    w->tracking_required = 0;
+    w->keepalive = 0;
+    w->decoded_url[0] = '\0';
+
+    buffer_reset(w->response.header_output);
+    buffer_reset(w->response.header);
+    buffer_reset(w->response.data);
+    w->response.rlen = 0;
+    w->response.sent = 0;
+    w->response.code = 0;
+
+    w->wait_receive = 1;
+    w->wait_send = 0;
+
+    w->response.zoutput = 0;
+
+    // if we had enabled compression, release it
 #ifdef NETDATA_WITH_ZLIB
-       if(w->response.zinitialized) {
-               debug(D_DEFLATE, "%llu: Freeing compression resources.", w->id);
-               deflateEnd(&w->response.zstream);
-               w->response.zsent = 0;
-               w->response.zhave = 0;
-               w->response.zstream.avail_in = 0;
-               w->response.zstream.avail_out = 0;
-               w->response.zstream.total_in = 0;
-               w->response.zstream.total_out = 0;
-               w->response.zinitialized = 0;
-       }
+    if(w->response.zinitialized) {
+        debug(D_DEFLATE, "%llu: Freeing compression resources.", w->id);
+        deflateEnd(&w->response.zstream);
+        w->response.zsent = 0;
+        w->response.zhave = 0;
+        w->response.zstream.avail_in = 0;
+        w->response.zstream.avail_out = 0;
+        w->response.zstream.total_in = 0;
+        w->response.zstream.total_out = 0;
+        w->response.zinitialized = 0;
+    }
 #endif // NETDATA_WITH_ZLIB
 }
 
 struct web_client *web_client_free(struct web_client *w) {
-       web_client_reset(w);
+    web_client_reset(w);
 
-       struct web_client *n = w->next;
-       if(w == web_clients) web_clients = n;
+    struct web_client *n = w->next;
+    if(w == web_clients) web_clients = n;
 
-       debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
+    debug(D_WEB_CLIENT_ACCESS, "%llu: Closing web client from %s port %s.", w->id, w->client_ip, w->client_port);
 
-       if(w->prev)     w->prev->next = w->next;
-       if(w->next) w->next->prev = w->prev;
-       if(w->response.header_output) buffer_free(w->response.header_output);
-       if(w->response.header) buffer_free(w->response.header);
-       if(w->response.data) buffer_free(w->response.data);
-       if(w->ifd != -1) close(w->ifd);
-       if(w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
-       free(w);
+    if(w->prev) w->prev->next = w->next;
+    if(w->next) w->next->prev = w->prev;
+    if(w->response.header_output) buffer_free(w->response.header_output);
+    if(w->response.header) buffer_free(w->response.header);
+    if(w->response.data) buffer_free(w->response.data);
+    if(w->ifd != -1) close(w->ifd);
+    if(w->ofd != -1 && w->ofd != w->ifd) close(w->ofd);
+    freez(w);
 
-       global_statistics.connected_clients--;
+    web_client_disconnected();
 
-       return(n);
+    return(n);
 }
 
 uid_t web_files_uid(void) {
-       static char *web_owner = NULL;
-       static uid_t owner_uid = 0;
-
-       if(unlikely(!web_owner)) {
-               web_owner = config_get("global", "web files owner", config_get("global", "run as user", ""));
-               if(!web_owner || !*web_owner)
-                       owner_uid = geteuid();
-               else {
-                       // getpwnam() is not thread safe,
-                       // but we have called this function once
-                       // while single threaded
-                       struct passwd *pw = getpwnam(web_owner);
-                       if(!pw) {
-                               error("User '%s' is not present. Ignoring option.", web_owner);
-                               owner_uid = geteuid();
-                       }
-                       else {
-                               debug(D_WEB_CLIENT, "Web files owner set to %s.", web_owner);
-                               owner_uid = pw->pw_uid;
-                       }
-               }
-       }
-
-       return(owner_uid);
+    static char *web_owner = NULL;
+    static uid_t owner_uid = 0;
+
+    if(unlikely(!web_owner)) {
+        web_owner = config_get("global", "web files owner", config_get("global", "run as user", ""));
+        if(!web_owner || !*web_owner)
+            owner_uid = geteuid();
+        else {
+            // getpwnam() is not thread safe,
+            // but we have called this function once
+            // while single threaded
+            struct passwd *pw = getpwnam(web_owner);
+            if(!pw) {
+                error("User '%s' is not present. Ignoring option.", web_owner);
+                owner_uid = geteuid();
+            }
+            else {
+                debug(D_WEB_CLIENT, "Web files owner set to %s.", web_owner);
+                owner_uid = pw->pw_uid;
+            }
+        }
+    }
+
+    return(owner_uid);
 }
 
 gid_t web_files_gid(void) {
-       static char *web_group = NULL;
-       static gid_t owner_gid = 0;
-
-       if(unlikely(!web_group)) {
-               web_group = config_get("global", "web files group", config_get("global", "web files owner", ""));
-               if(!web_group || !*web_group)
-                       owner_gid = getegid();
-               else {
-                       // getgrnam() is not thread safe,
-                       // but we have called this function once
-                       // while single threaded
-                       struct group *gr = getgrnam(web_group);
-                       if(!gr) {
-                               error("Group '%s' is not present. Ignoring option.", web_group);
-                               owner_gid = getegid();
-                       }
-                       else {
-                               debug(D_WEB_CLIENT, "Web files group set to %s.", web_group);
-                               owner_gid = gr->gr_gid;
-                       }
-               }
-       }
-
-       return(owner_gid);
+    static char *web_group = NULL;
+    static gid_t owner_gid = 0;
+
+    if(unlikely(!web_group)) {
+        web_group = config_get("global", "web files group", config_get("global", "web files owner", ""));
+        if(!web_group || !*web_group)
+            owner_gid = getegid();
+        else {
+            // getgrnam() is not thread safe,
+            // but we have called this function once
+            // while single threaded
+            struct group *gr = getgrnam(web_group);
+            if(!gr) {
+                error("Group '%s' is not present. Ignoring option.", web_group);
+                owner_gid = getegid();
+            }
+            else {
+                debug(D_WEB_CLIENT, "Web files group set to %s.", web_group);
+                owner_gid = gr->gr_gid;
+            }
+        }
+    }
+
+    return(owner_gid);
 }
 
 int mysendfile(struct web_client *w, char *filename)
 {
-       static char *web_dir = NULL;
-
-       // initialize our static data
-       if(unlikely(!web_dir)) web_dir = config_get("global", "web files directory", WEB_DIR);
-
-       debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, web_dir, filename);
-
-       // skip leading slashes
-       while (*filename == '/') filename++;
-
-       // if the filename contain known paths, skip them
-       if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0)
-               filename = &filename[strlen(WEB_PATH_FILE) + 1];
-
-       char *s;
-       for(s = filename; *s ;s++) {
-               if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
-                       debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
-                       buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", filename, *s);
-                       return 400;
-               }
-       }
-
-       // if the filename contains a .. refuse to serve it
-       if(strstr(filename, "..") != 0) {
-               debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
-               buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename);
-               return 400;
-       }
-
-       // access the file
-       char webfilename[FILENAME_MAX + 1];
-       snprintfz(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename);
-
-       // check if the file exists
-       struct stat stat;
-       if(lstat(webfilename, &stat) != 0) {
-               debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
-               buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", webfilename);
-               return 404;
-       }
-
-       // check if the file is owned by expected user
-       if(stat.st_uid != web_files_uid()) {
-               error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
-               buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
-               return 403;
-       }
-
-       // check if the file is owned by expected group
-       if(stat.st_gid != web_files_gid()) {
-               error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
-               buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
-               return 403;
-       }
-
-       if((stat.st_mode & S_IFMT) == S_IFDIR) {
-               snprintfz(webfilename, FILENAME_MAX, "%s/index.html", filename);
-               return mysendfile(w, webfilename);
-       }
-
-       if((stat.st_mode & S_IFMT) != S_IFREG) {
-               error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
-               buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
-               return 403;
-       }
-
-       // open the file
-       w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
-       if(w->ifd == -1) {
-               w->ifd = w->ofd;
-
-               if(errno == EBUSY || errno == EAGAIN) {
-                       error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
-                       buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
-                       buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", webfilename);
-                       return 307;
-               }
-               else {
-                       error("%llu: Cannot open file '%s'.", w->id, webfilename);
-                       buffer_sprintf(w->response.data, "Cannot open file '%s'.", webfilename);
-                       return 404;
-               }
-       }
-       if(fcntl(w->ifd, F_SETFL, O_NONBLOCK) < 0)
-               error("%llu: Cannot set O_NONBLOCK on file '%s'.", w->id, webfilename);
-
-       // pick a Content-Type for the file
-                if(strstr(filename, ".html") != NULL)  w->response.data->contenttype = CT_TEXT_HTML;
-       else if(strstr(filename, ".js")   != NULL)      w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-       else if(strstr(filename, ".css")  != NULL)      w->response.data->contenttype = CT_TEXT_CSS;
-       else if(strstr(filename, ".xml")  != NULL)      w->response.data->contenttype = CT_TEXT_XML;
-       else if(strstr(filename, ".xsl")  != NULL)      w->response.data->contenttype = CT_TEXT_XSL;
-       else if(strstr(filename, ".txt")  != NULL)  w->response.data->contenttype = CT_TEXT_PLAIN;
-       else if(strstr(filename, ".svg")  != NULL)  w->response.data->contenttype = CT_IMAGE_SVG_XML;
-       else if(strstr(filename, ".ttf")  != NULL)  w->response.data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
-       else if(strstr(filename, ".otf")  != NULL)  w->response.data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
-       else if(strstr(filename, ".woff2")!= NULL)  w->response.data->contenttype = CT_APPLICATION_FONT_WOFF2;
-       else if(strstr(filename, ".woff") != NULL)  w->response.data->contenttype = CT_APPLICATION_FONT_WOFF;
-       else if(strstr(filename, ".eot")  != NULL)  w->response.data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
-       else if(strstr(filename, ".png")  != NULL)  w->response.data->contenttype = CT_IMAGE_PNG;
-       else if(strstr(filename, ".jpg")  != NULL)  w->response.data->contenttype = CT_IMAGE_JPG;
-       else if(strstr(filename, ".jpeg") != NULL)  w->response.data->contenttype = CT_IMAGE_JPG;
-       else if(strstr(filename, ".gif")  != NULL)  w->response.data->contenttype = CT_IMAGE_GIF;
-       else if(strstr(filename, ".bmp")  != NULL)  w->response.data->contenttype = CT_IMAGE_BMP;
-       else if(strstr(filename, ".ico")  != NULL)  w->response.data->contenttype = CT_IMAGE_XICON;
-       else if(strstr(filename, ".icns") != NULL)  w->response.data->contenttype = CT_IMAGE_ICNS;
-       else w->response.data->contenttype = CT_APPLICATION_OCTET_STREAM;
-
-       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, webfilename, stat.st_size, w->ifd, w->ofd);
-
-       w->mode = WEB_CLIENT_MODE_FILECOPY;
-       w->wait_receive = 1;
-       w->wait_send = 0;
-       buffer_flush(w->response.data);
-       w->response.rlen = stat.st_size;
-       w->response.data->date = stat.st_mtim.tv_sec;
-
-       return 200;
+    static char *web_dir = NULL;
+
+    // initialize our static data
+    if(unlikely(!web_dir)) web_dir = config_get("global", "web files directory", WEB_DIR);
+
+    debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, web_dir, filename);
+
+    // skip leading slashes
+    while (*filename == '/') filename++;
+
+    // if the filename contain known paths, skip them
+    if(strncmp(filename, WEB_PATH_FILE "/", strlen(WEB_PATH_FILE) + 1) == 0)
+        filename = &filename[strlen(WEB_PATH_FILE) + 1];
+
+    char *s;
+    for(s = filename; *s ;s++) {
+        if( !isalnum(*s) && *s != '/' && *s != '.' && *s != '-' && *s != '_') {
+            debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+            buffer_sprintf(w->response.data, "File '%s' cannot be served. Filename contains invalid character '%c'", filename, *s);
+            return 400;
+        }
+    }
+
+    // if the filename contains a .. refuse to serve it
+    if(strstr(filename, "..") != 0) {
+        debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not acceptable.", w->id, filename);
+        buffer_sprintf(w->response.data, "File '%s' cannot be served. Relative filenames with '..' in them are not supported.", filename);
+        return 400;
+    }
+
+    // access the file
+    char webfilename[FILENAME_MAX + 1];
+    snprintfz(webfilename, FILENAME_MAX, "%s/%s", web_dir, filename);
+
+    // check if the file exists
+    struct stat stat;
+    if(lstat(webfilename, &stat) != 0) {
+        debug(D_WEB_CLIENT_ACCESS, "%llu: File '%s' is not found.", w->id, webfilename);
+        buffer_sprintf(w->response.data, "File '%s' does not exist, or is not accessible.", webfilename);
+        return 404;
+    }
+
+    // check if the file is owned by expected user
+    if(stat.st_uid != web_files_uid()) {
+        error("%llu: File '%s' is owned by user %u (expected user %u). Access Denied.", w->id, webfilename, stat.st_uid, web_files_uid());
+        buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
+        return 403;
+    }
+
+    // check if the file is owned by expected group
+    if(stat.st_gid != web_files_gid()) {
+        error("%llu: File '%s' is owned by group %u (expected group %u). Access Denied.", w->id, webfilename, stat.st_gid, web_files_gid());
+        buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
+        return 403;
+    }
+
+    if((stat.st_mode & S_IFMT) == S_IFDIR) {
+        snprintfz(webfilename, FILENAME_MAX, "%s/index.html", filename);
+        return mysendfile(w, webfilename);
+    }
+
+    if((stat.st_mode & S_IFMT) != S_IFREG) {
+        error("%llu: File '%s' is not a regular file. Access Denied.", w->id, webfilename);
+        buffer_sprintf(w->response.data, "Access to file '%s' is not permitted.", webfilename);
+        return 403;
+    }
+
+    // open the file
+    w->ifd = open(webfilename, O_NONBLOCK, O_RDONLY);
+    if(w->ifd == -1) {
+        w->ifd = w->ofd;
+
+        if(errno == EBUSY || errno == EAGAIN) {
+            error("%llu: File '%s' is busy, sending 307 Moved Temporarily to force retry.", w->id, webfilename);
+            buffer_sprintf(w->response.header, "Location: /" WEB_PATH_FILE "/%s\r\n", filename);
+            buffer_sprintf(w->response.data, "The file '%s' is currently busy. Please try again later.", webfilename);
+            return 307;
+        }
+        else {
+            error("%llu: Cannot open file '%s'.", w->id, webfilename);
+            buffer_sprintf(w->response.data, "Cannot open file '%s'.", webfilename);
+            return 404;
+        }
+    }
+    if(fcntl(w->ifd, F_SETFL, O_NONBLOCK) < 0)
+        error("%llu: Cannot set O_NONBLOCK on file '%s'.", w->id, webfilename);
+
+    // pick a Content-Type for the file
+         if(strstr(filename, ".html") != NULL)  w->response.data->contenttype = CT_TEXT_HTML;
+    else if(strstr(filename, ".js")   != NULL)  w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+    else if(strstr(filename, ".css")  != NULL)  w->response.data->contenttype = CT_TEXT_CSS;
+    else if(strstr(filename, ".xml")  != NULL)  w->response.data->contenttype = CT_TEXT_XML;
+    else if(strstr(filename, ".xsl")  != NULL)  w->response.data->contenttype = CT_TEXT_XSL;
+    else if(strstr(filename, ".txt")  != NULL)  w->response.data->contenttype = CT_TEXT_PLAIN;
+    else if(strstr(filename, ".svg")  != NULL)  w->response.data->contenttype = CT_IMAGE_SVG_XML;
+    else if(strstr(filename, ".ttf")  != NULL)  w->response.data->contenttype = CT_APPLICATION_X_FONT_TRUETYPE;
+    else if(strstr(filename, ".otf")  != NULL)  w->response.data->contenttype = CT_APPLICATION_X_FONT_OPENTYPE;
+    else if(strstr(filename, ".woff2")!= NULL)  w->response.data->contenttype = CT_APPLICATION_FONT_WOFF2;
+    else if(strstr(filename, ".woff") != NULL)  w->response.data->contenttype = CT_APPLICATION_FONT_WOFF;
+    else if(strstr(filename, ".eot")  != NULL)  w->response.data->contenttype = CT_APPLICATION_VND_MS_FONTOBJ;
+    else if(strstr(filename, ".png")  != NULL)  w->response.data->contenttype = CT_IMAGE_PNG;
+    else if(strstr(filename, ".jpg")  != NULL)  w->response.data->contenttype = CT_IMAGE_JPG;
+    else if(strstr(filename, ".jpeg") != NULL)  w->response.data->contenttype = CT_IMAGE_JPG;
+    else if(strstr(filename, ".gif")  != NULL)  w->response.data->contenttype = CT_IMAGE_GIF;
+    else if(strstr(filename, ".bmp")  != NULL)  w->response.data->contenttype = CT_IMAGE_BMP;
+    else if(strstr(filename, ".ico")  != NULL)  w->response.data->contenttype = CT_IMAGE_XICON;
+    else if(strstr(filename, ".icns") != NULL)  w->response.data->contenttype = CT_IMAGE_ICNS;
+    else w->response.data->contenttype = CT_APPLICATION_OCTET_STREAM;
+
+    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending file '%s' (%ld bytes, ifd %d, ofd %d).", w->id, webfilename, stat.st_size, w->ifd, w->ofd);
+
+    w->mode = WEB_CLIENT_MODE_FILECOPY;
+    w->wait_receive = 1;
+    w->wait_send = 0;
+    buffer_flush(w->response.data);
+    w->response.rlen = stat.st_size;
+    w->response.data->date = stat.st_mtim.tv_sec;
+
+    return 200;
 }
 
 
 #ifdef NETDATA_WITH_ZLIB
 void web_client_enable_deflate(struct web_client *w, int gzip) {
-       if(unlikely(w->response.zinitialized)) {
-               error("%llu: Compression has already be initialized for this client.", w->id);
-               return;
-       }
-
-       if(unlikely(w->response.sent)) {
-               error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
-               return;
-       }
-
-       w->response.zstream.zalloc = Z_NULL;
-       w->response.zstream.zfree = Z_NULL;
-       w->response.zstream.opaque = Z_NULL;
-
-       w->response.zstream.next_in = (Bytef *)w->response.data->buffer;
-       w->response.zstream.avail_in = 0;
-       w->response.zstream.total_in = 0;
-
-       w->response.zstream.next_out = w->response.zbuffer;
-       w->response.zstream.avail_out = 0;
-       w->response.zstream.total_out = 0;
-
-       w->response.zstream.zalloc = Z_NULL;
-       w->response.zstream.zfree = Z_NULL;
-       w->response.zstream.opaque = Z_NULL;
-
-//     if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
-//             error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
-//             return;
-//     }
-
-       // Select GZIP compression: windowbits = 15 + 16 = 31
-       if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
-               error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
-               return;
-       }
-
-       w->response.zsent = 0;
-       w->response.zoutput = 1;
-       w->response.zinitialized = 1;
-
-       debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
+    if(unlikely(w->response.zinitialized)) {
+        error("%llu: Compression has already be initialized for this client.", w->id);
+        return;
+    }
+
+    if(unlikely(w->response.sent)) {
+        error("%llu: Cannot enable compression in the middle of a conversation.", w->id);
+        return;
+    }
+
+    w->response.zstream.zalloc = Z_NULL;
+    w->response.zstream.zfree = Z_NULL;
+    w->response.zstream.opaque = Z_NULL;
+
+    w->response.zstream.next_in = (Bytef *)w->response.data->buffer;
+    w->response.zstream.avail_in = 0;
+    w->response.zstream.total_in = 0;
+
+    w->response.zstream.next_out = w->response.zbuffer;
+    w->response.zstream.avail_out = 0;
+    w->response.zstream.total_out = 0;
+
+    w->response.zstream.zalloc = Z_NULL;
+    w->response.zstream.zfree = Z_NULL;
+    w->response.zstream.opaque = Z_NULL;
+
+//  if(deflateInit(&w->response.zstream, Z_DEFAULT_COMPRESSION) != Z_OK) {
+//      error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+//      return;
+//  }
+
+    // Select GZIP compression: windowbits = 15 + 16 = 31
+    if(deflateInit2(&w->response.zstream, web_gzip_level, Z_DEFLATED, 15 + ((gzip)?16:0), 8, web_gzip_strategy) != Z_OK) {
+        error("%llu: Failed to initialize zlib. Proceeding without compression.", w->id);
+        return;
+    }
+
+    w->response.zsent = 0;
+    w->response.zoutput = 1;
+    w->response.zinitialized = 1;
+
+    debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
 }
 #endif // NETDATA_WITH_ZLIB
 
 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 ret = 0x00000000;
+    char *tok;
+
+    while(o && *o && (tok = mystrsep(&o, ", |"))) {
+        if(!*tok) continue;
+
+        if(!strcmp(tok, "nonzero"))
+            ret |= RRDR_OPTION_NONZERO;
+        else if(!strcmp(tok, "flip") || !strcmp(tok, "reversed") || !strcmp(tok, "reverse"))
+            ret |= RRDR_OPTION_REVERSED;
+        else if(!strcmp(tok, "jsonwrap"))
+            ret |= RRDR_OPTION_JSON_WRAP;
+        else if(!strcmp(tok, "min2max"))
+            ret |= RRDR_OPTION_MIN2MAX;
+        else if(!strcmp(tok, "ms") || !strcmp(tok, "milliseconds"))
+            ret |= RRDR_OPTION_MILLISECONDS;
+        else if(!strcmp(tok, "abs") || !strcmp(tok, "absolute") || !strcmp(tok, "absolute_sum") || !strcmp(tok, "absolute-sum"))
+            ret |= RRDR_OPTION_ABSOLUTE;
+        else if(!strcmp(tok, "seconds"))
+            ret |= RRDR_OPTION_SECONDS;
+        else if(!strcmp(tok, "null2zero"))
+            ret |= RRDR_OPTION_NULL2ZERO;
+        else if(!strcmp(tok, "objectrows"))
+            ret |= RRDR_OPTION_OBJECTSROWS;
+        else if(!strcmp(tok, "google_json"))
+            ret |= RRDR_OPTION_GOOGLE_JSON;
+        else if(!strcmp(tok, "percentage"))
+            ret |= RRDR_OPTION_PERCENTAGE;
+        else if(!strcmp(tok, "unaligned"))
+            ret |= RRDR_OPTION_NOT_ALIGNED;
+    }
+
+    return ret;
 }
 
 uint32_t web_client_api_request_v1_data_format(char *name)
 {
-       if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
-               return DATASOURCE_DATATABLE_JSON;
+    if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSON)) // datatable
+        return DATASOURCE_DATATABLE_JSON;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
-               return DATASOURCE_DATATABLE_JSONP;
+    else if(!strcmp(name, DATASOURCE_FORMAT_DATATABLE_JSONP)) // datasource
+        return DATASOURCE_DATATABLE_JSONP;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
-               return DATASOURCE_JSON;
+    else if(!strcmp(name, DATASOURCE_FORMAT_JSON)) // json
+        return DATASOURCE_JSON;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
-               return DATASOURCE_JSONP;
+    else if(!strcmp(name, DATASOURCE_FORMAT_JSONP)) // jsonp
+        return DATASOURCE_JSONP;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
-               return DATASOURCE_SSV;
+    else if(!strcmp(name, DATASOURCE_FORMAT_SSV)) // ssv
+        return DATASOURCE_SSV;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
-               return DATASOURCE_CSV;
+    else if(!strcmp(name, DATASOURCE_FORMAT_CSV)) // csv
+        return DATASOURCE_CSV;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
-               return DATASOURCE_TSV;
+    else if(!strcmp(name, DATASOURCE_FORMAT_TSV) || !strcmp(name, "tsv-excel")) // tsv
+        return DATASOURCE_TSV;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
-               return DATASOURCE_HTML;
+    else if(!strcmp(name, DATASOURCE_FORMAT_HTML)) // html
+        return DATASOURCE_HTML;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
-               return DATASOURCE_JS_ARRAY;
+    else if(!strcmp(name, DATASOURCE_FORMAT_JS_ARRAY)) // array
+        return DATASOURCE_JS_ARRAY;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
-               return DATASOURCE_SSV_COMMA;
+    else if(!strcmp(name, DATASOURCE_FORMAT_SSV_COMMA)) // ssvcomma
+        return DATASOURCE_SSV_COMMA;
 
-       else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
-               return DATASOURCE_CSV_JSON_ARRAY;
+    else if(!strcmp(name, DATASOURCE_FORMAT_CSV_JSON_ARRAY)) // csvjsonarray
+        return DATASOURCE_CSV_JSON_ARRAY;
 
-       return DATASOURCE_JSON;
+    return DATASOURCE_JSON;
 }
 
 uint32_t web_client_api_request_v1_data_google_format(char *name)
 {
-       if(!strcmp(name, "json"))
-               return DATASOURCE_DATATABLE_JSONP;
+    if(!strcmp(name, "json"))
+        return DATASOURCE_DATATABLE_JSONP;
 
-       else if(!strcmp(name, "html"))
-               return DATASOURCE_HTML;
+    else if(!strcmp(name, "html"))
+        return DATASOURCE_HTML;
 
-       else if(!strcmp(name, "csv"))
-               return DATASOURCE_CSV;
+    else if(!strcmp(name, "csv"))
+        return DATASOURCE_CSV;
 
-       else if(!strcmp(name, "tsv-excel"))
-               return DATASOURCE_TSV;
+    else if(!strcmp(name, "tsv-excel"))
+        return DATASOURCE_TSV;
 
-       return DATASOURCE_JSON;
+    return DATASOURCE_JSON;
 }
 
-int web_client_api_request_v1_data_group(char *name)
+int web_client_api_request_v1_data_group(char *name, int def)
 {
-       if(!strcmp(name, "max"))
-               return GROUP_MAX;
+    if(!strcmp(name, "max"))
+        return GROUP_MAX;
 
-       else if(!strcmp(name, "average"))
-               return GROUP_AVERAGE;
+    else if(!strcmp(name, "average"))
+        return GROUP_AVERAGE;
 
-       else if(!strcmp(name, "sum"))
-               return GROUP_SUM;
+    else if(!strcmp(name, "sum"))
+        return GROUP_SUM;
 
-       else if(!strcmp(name, "incremental-sum"))
-               return GROUP_INCREMENTAL_SUM;
+    else if(!strcmp(name, "incremental-sum"))
+        return GROUP_INCREMENTAL_SUM;
 
-       return GROUP_AVERAGE;
+    return def;
 }
 
 int web_client_api_request_v1_charts(struct web_client *w, char *url)
 {
-       if(url) { ; }
+    if(url) { ; }
 
-       buffer_flush(w->response.data);
-       w->response.data->contenttype = CT_APPLICATION_JSON;
-       rrd_stats_api_v1_charts(w->response.data);
-       return 200;
+    buffer_flush(w->response.data);
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    rrd_stats_api_v1_charts(w->response.data);
+    return 200;
 }
 
 int web_client_api_request_v1_chart(struct web_client *w, char *url)
 {
-       int ret = 400;
-       char *chart = NULL;
-
-       buffer_flush(w->response.data);
-
-       while(url) {
-               char *value = mystrsep(&url, "?&[]");
-               if(!value || !*value) continue;
-
-               char *name = mystrsep(&value, "=");
-               if(!name || !*name) continue;
-               if(!value || !*value) continue;
-
-               // name and value are now the parameters
-               // they are not null and not empty
-
-               if(!strcmp(name, "chart")) chart = value;
-               //else {
-               ///     buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
-               //      goto cleanup;
-               //}
-       }
-
-       if(!chart || !*chart) {
-               buffer_sprintf(w->response.data, "No chart id is given at the request.");
-               goto cleanup;
-       }
-
-       RRDSET *st = rrdset_find(chart);
-       if(!st) st = rrdset_find_byname(chart);
-       if(!st) {
-               buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
-               ret = 404;
-               goto cleanup;
-       }
-
-       w->response.data->contenttype = CT_APPLICATION_JSON;
-       rrd_stats_api_v1_chart(st, w->response.data);
-       return 200;
+    int ret = 400;
+    char *chart = NULL;
+
+    buffer_flush(w->response.data);
+
+    while(url) {
+        char *value = mystrsep(&url, "?&[]");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        //else {
+        /// buffer_sprintf(w->response.data, "Unknown parameter '%s' in request.", name);
+        //  goto cleanup;
+        //}
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(chart);
+    if(!st) st = rrdset_find_byname(chart);
+    if(!st) {
+        buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
+        ret = 404;
+        goto cleanup;
+    }
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    rrd_stats_api_v1_chart(st, w->response.data);
+    return 200;
 
 cleanup:
-       return ret;
+    return ret;
 }
 
 int web_client_api_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;
-
-       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(strlen(value));
-
-                       if(dimensions) {
-                               buffer_strcat(dimensions, "|");
-                               buffer_strcat(dimensions, value);
-                       }
-               }
-               else if(!strcmp(name, "after")) after_str = value;
-               else if(!strcmp(name, "before")) before_str = value;
-               else if(!strcmp(name, "points")) points_str = value;
-               else if(!strcmp(name, "group")) {
-                       group = web_client_api_request_v1_data_group(value);
-               }
-               else if(!strcmp(name, "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;
-       }
-
-       if(!chart || !*chart) {
-               buffer_sprintf(w->response.data, "No chart id is given at the request.");
-               goto cleanup;
-       }
-
-       RRDSET *st = rrdset_find(chart);
-       if(!st) st = rrdset_find_byname(chart);
-       if(!st) {
-               buffer_svg(w->response.data, "chart not found", 0, "", NULL, NULL, 1, -1);
-               ret = 200;
-               goto cleanup;
-       }
-
-       long long multiply  = (multiply_str  && *multiply_str )?atol(multiply_str):1;
-       long long divide    = (divide_str    && *divide_str   )?atol(divide_str):1;
-       long long before    = (before_str    && *before_str   )?atol(before_str):0;
-       long long after     = (after_str     && *after_str    )?atol(after_str):-st->update_every;
-       int       points    = (points_str    && *points_str   )?atoi(points_str):1;
-       int       precision = (precision_str && *precision_str)?atoi(precision_str):-1;
-
-       if(!multiply) multiply = 1;
-       if(!divide) divide = 1;
-
-       int refresh = 0;
-       if(refresh_str && *refresh_str) {
-               if(!strcmp(refresh_str, "auto")) {
-                       if(options & RRDR_OPTION_NOT_ALIGNED)
-                               refresh = st->update_every;
-                       else {
-                               refresh = (before - after);
-                               if(refresh < 0) refresh = -refresh;
-                       }
-               }
-               else {
-                       refresh = atoi(refresh_str);
-                       if(refresh < 0) refresh = -refresh;
-               }
-       }
-
-       if(!label) {
-               if(dimensions) {
-                       const char *dim = buffer_tostring(dimensions);
-                       if(*dim == '|') dim++;
-                       label = dim;
-               }
-               else
-                       label = st->name;
-       }
-       if(!units) {
-               if(options & RRDR_OPTION_PERCENTAGE)
-                       units="%";
-               else
-                       units = st->units;
-       }
-
-       debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
-                       , w->id
-                       , chart
-                       , (dimensions)?buffer_tostring(dimensions):""
-                       , after
-                       , before
-                       , points
-                       , group
-                       , options
-                       );
-
-       time_t latest_timestamp = 0;
-       int value_is_null = 1;
-       calculated_number n = 0;
-       ret = 500;
-
-       // if the collected value is too old, don't calculate its value
-       if(rrdset_last_entry_t(st) >= (time(NULL) - (st->update_every * st->gap_when_lost_iterations_above)))
-               ret = rrd2value(st, w->response.data, &n, dimensions, points, after, before, group, options, &latest_timestamp, &value_is_null);
-
-       // if the value cannot be calculated, show empty badge
-       if(ret != 200) {
-               value_is_null = 1;
-               n = 0;
-               ret = 200;
-       }
-       else if(refresh > 0)
-               buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
-
-       // render the badge
-       buffer_svg(w->response.data, label, n * multiply / divide, units, label_color, value_color, value_is_null, precision);
+    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;
+
+    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;
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(chart);
+    if(!st) st = rrdset_find_byname(chart);
+    if(!st) {
+        buffer_svg(w->response.data, "chart not found", 0, "", NULL, NULL, 1, -1);
+        ret = 200;
+        goto cleanup;
+    }
+
+    long long multiply  = (multiply_str  && *multiply_str )?atol(multiply_str):1;
+    long long divide    = (divide_str    && *divide_str   )?atol(divide_str):1;
+    long long before    = (before_str    && *before_str   )?atol(before_str):0;
+    long long after     = (after_str     && *after_str    )?atol(after_str):-st->update_every;
+    int       points    = (points_str    && *points_str   )?atoi(points_str):1;
+    int       precision = (precision_str && *precision_str)?atoi(precision_str):-1;
+
+    if(!multiply) multiply = 1;
+    if(!divide) divide = 1;
+
+    int refresh = 0;
+    if(refresh_str && *refresh_str) {
+        if(!strcmp(refresh_str, "auto")) {
+            if(options & RRDR_OPTION_NOT_ALIGNED)
+                refresh = st->update_every;
+            else {
+                refresh = (before - after);
+                if(refresh < 0) refresh = -refresh;
+            }
+        }
+        else {
+            refresh = atoi(refresh_str);
+            if(refresh < 0) refresh = -refresh;
+        }
+    }
+
+    if(!label) {
+        if(dimensions) {
+            const char *dim = buffer_tostring(dimensions);
+            if(*dim == '|') dim++;
+            label = dim;
+        }
+        else
+            label = st->name;
+    }
+    if(!units) {
+        if(options & RRDR_OPTION_PERCENTAGE)
+            units="%";
+        else
+            units = st->units;
+    }
+
+    debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
+            , w->id
+            , chart
+            , (dimensions)?buffer_tostring(dimensions):""
+            , after
+            , before
+            , points
+            , group
+            , options
+            );
+
+    time_t latest_timestamp = 0;
+    int value_is_null = 1;
+    calculated_number n = 0;
+    ret = 500;
+
+    // if the collected value is too old, don't calculate its value
+    if(rrdset_last_entry_t(st) >= (time(NULL) - (st->update_every * st->gap_when_lost_iterations_above)))
+        ret = rrd2value(st, w->response.data, &n, (dimensions)?buffer_tostring(dimensions):NULL, points, after, before, group, options, &latest_timestamp, &value_is_null);
+
+    // if the value cannot be calculated, show empty badge
+    if(ret != 200) {
+        value_is_null = 1;
+        n = 0;
+        ret = 200;
+    }
+    else if(refresh > 0)
+        buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
+
+    // render the badge
+    buffer_svg(w->response.data, label, n * multiply / divide, units, label_color, value_color, value_is_null, precision);
 
 cleanup:
-       if(dimensions)
-               buffer_free(dimensions);
-       return ret;
+    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(strlen(value));
-                       if(dimensions) {
-                               buffer_strcat(dimensions, "|");
-                               buffer_strcat(dimensions, value);
-                       }
-               }
-               else if(!strcmp(name, "after")) after_str = value;
-               else if(!strcmp(name, "before")) before_str = value;
-               else if(!strcmp(name, "points")) points_str = value;
-               else if(!strcmp(name, "group")) {
-                       group = web_client_api_request_v1_data_group(value);
-               }
-               else if(!strcmp(name, "format")) {
-                       format = web_client_api_request_v1_data_format(value);
-               }
-               else if(!strcmp(name, "options")) {
-                       options |= web_client_api_request_v1_data_options(value);
-               }
-               else if(!strcmp(name, "callback")) {
-                       responseHandler = value;
-               }
-               else if(!strcmp(name, "filename")) {
-                       outFileName = value;
-               }
-               else if(!strcmp(name, "tqx")) {
-                       // parse Google Visualization API options
-                       // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
-                       char *tqx_name, *tqx_value;
-
-                       while(value) {
-                               tqx_value = mystrsep(&value, ";");
-                               if(!tqx_value || !*tqx_value) continue;
-
-                               tqx_name = mystrsep(&tqx_value, ":");
-                               if(!tqx_name || !*tqx_name) continue;
-                               if(!tqx_value || !*tqx_value) continue;
-
-                               if(!strcmp(tqx_name, "version"))
-                                       google_version = tqx_value;
-                               else if(!strcmp(tqx_name, "reqId"))
-                                       google_reqId = tqx_value;
-                               else if(!strcmp(tqx_name, "sig")) {
-                                       google_sig = tqx_value;
-                                       google_timestamp = strtoul(google_sig, NULL, 0);
-                               }
-                               else if(!strcmp(tqx_name, "out")) {
-                                       google_out = tqx_value;
-                                       format = web_client_api_request_v1_data_google_format(google_out);
-                               }
-                               else if(!strcmp(tqx_name, "responseHandler"))
-                                       responseHandler = tqx_value;
-                               else if(!strcmp(tqx_name, "outFileName"))
-                                       outFileName = tqx_value;
-                       }
-               }
-       }
-
-       if(!chart || !*chart) {
-               buffer_sprintf(w->response.data, "No chart id is given at the request.");
-               goto cleanup;
-       }
-
-       RRDSET *st = rrdset_find(chart);
-       if(!st) st = rrdset_find_byname(chart);
-       if(!st) {
-               buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
-               ret = 404;
-               goto cleanup;
-       }
-
-       long long before = (before_str && *before_str)?atol(before_str):0;
-       long long after  = (after_str  && *after_str) ?atol(after_str):0;
-       int       points = (points_str && *points_str)?atoi(points_str):0;
-
-       debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%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, ");");
+    debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
+
+    int ret = 400;
+    BUFFER *dimensions = NULL;
+
+    buffer_flush(w->response.data);
+
+    char    *google_version = "0.6",
+            *google_reqId = "0",
+            *google_sig = "0",
+            *google_out = "json",
+            *responseHandler = NULL,
+            *outFileName = NULL;
+
+    time_t last_timestamp_in_data = 0, google_timestamp = 0;
+
+    char *chart = NULL
+            , *before_str = NULL
+            , *after_str = NULL
+            , *points_str = NULL;
+
+    int group = GROUP_AVERAGE;
+    uint32_t format = DATASOURCE_JSON;
+    uint32_t options = 0x00000000;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&[]");
+        if(!value || !*value) continue;
+
+        char *name = mystrsep(&value, "=");
+        if(!name || !*name) continue;
+        if(!value || !*value) continue;
+
+        debug(D_WEB_CLIENT, "%llu: API v1 data query param '%s' with value '%s'", w->id, name, value);
+
+        // name and value are now the parameters
+        // they are not null and not empty
+
+        if(!strcmp(name, "chart")) chart = value;
+        else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
+            if(!dimensions) dimensions = buffer_create(100);
+            buffer_strcat(dimensions, "|");
+            buffer_strcat(dimensions, value);
+        }
+        else if(!strcmp(name, "after")) after_str = value;
+        else if(!strcmp(name, "before")) before_str = value;
+        else if(!strcmp(name, "points")) points_str = value;
+        else if(!strcmp(name, "group")) {
+            group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
+        }
+        else if(!strcmp(name, "format")) {
+            format = web_client_api_request_v1_data_format(value);
+        }
+        else if(!strcmp(name, "options")) {
+            options |= web_client_api_request_v1_data_options(value);
+        }
+        else if(!strcmp(name, "callback")) {
+            responseHandler = value;
+        }
+        else if(!strcmp(name, "filename")) {
+            outFileName = value;
+        }
+        else if(!strcmp(name, "tqx")) {
+            // parse Google Visualization API options
+            // https://developers.google.com/chart/interactive/docs/dev/implementing_data_source
+            char *tqx_name, *tqx_value;
+
+            while(value) {
+                tqx_value = mystrsep(&value, ";");
+                if(!tqx_value || !*tqx_value) continue;
+
+                tqx_name = mystrsep(&tqx_value, ":");
+                if(!tqx_name || !*tqx_name) continue;
+                if(!tqx_value || !*tqx_value) continue;
+
+                if(!strcmp(tqx_name, "version"))
+                    google_version = tqx_value;
+                else if(!strcmp(tqx_name, "reqId"))
+                    google_reqId = tqx_value;
+                else if(!strcmp(tqx_name, "sig")) {
+                    google_sig = tqx_value;
+                    google_timestamp = strtoul(google_sig, NULL, 0);
+                }
+                else if(!strcmp(tqx_name, "out")) {
+                    google_out = tqx_value;
+                    format = web_client_api_request_v1_data_google_format(google_out);
+                }
+                else if(!strcmp(tqx_name, "responseHandler"))
+                    responseHandler = tqx_value;
+                else if(!strcmp(tqx_name, "outFileName"))
+                    outFileName = tqx_value;
+            }
+        }
+    }
+
+    if(!chart || !*chart) {
+        buffer_sprintf(w->response.data, "No chart id is given at the request.");
+        goto cleanup;
+    }
+
+    RRDSET *st = rrdset_find(chart);
+    if(!st) st = rrdset_find_byname(chart);
+    if(!st) {
+        buffer_sprintf(w->response.data, "Chart '%s' is not found.", chart);
+        ret = 404;
+        goto cleanup;
+    }
+
+    long long before = (before_str && *before_str)?atol(before_str):0;
+    long long after  = (after_str  && *after_str) ?atol(after_str):0;
+    int       points = (points_str && *points_str)?atoi(points_str):0;
+
+    debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
+            , w->id
+            , chart
+            , (dimensions)?buffer_tostring(dimensions):""
+            , after
+            , before
+            , points
+            , group
+            , format
+            , options
+            );
+
+    if(outFileName && *outFileName) {
+        buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
+        debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName);
+    }
+
+    if(format == DATASOURCE_DATATABLE_JSONP) {
+        if(responseHandler == NULL)
+            responseHandler = "google.visualization.Query.setResponse";
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSON/JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+                w->id, google_version, google_reqId, google_sig, google_out, responseHandler, outFileName
+            );
+
+        buffer_sprintf(w->response.data,
+            "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+            responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+    }
+    else if(format == DATASOURCE_JSONP) {
+        if(responseHandler == NULL)
+            responseHandler = "callback";
+
+        buffer_strcat(w->response.data, responseHandler);
+        buffer_strcat(w->response.data, "(");
+    }
+
+    ret = rrd2format(st, w->response.data, dimensions, format, points, after, before, group, options, &last_timestamp_in_data);
+
+    if(format == DATASOURCE_DATATABLE_JSONP) {
+        if(google_timestamp < last_timestamp_in_data)
+            buffer_strcat(w->response.data, "});");
+
+        else {
+            // the client already has the latest data
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data,
+                "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+                responseHandler, google_version, google_reqId);
+        }
+    }
+    else if(format == DATASOURCE_JSONP)
+        buffer_strcat(w->response.data, ");");
 
 cleanup:
-       if(dimensions) buffer_free(dimensions);
-       return ret;
+    if(dimensions) buffer_free(dimensions);
+    return ret;
 }
 
 int web_client_api_request_v1_registry(struct web_client *w, char *url)
 {
-       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");
+    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");
+        hash_redirects = simple_hash("redirects");
 */
-       }
-
-       char person_guid[36 + 1] = "";
-
-       debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
-
-       // FIXME
-       // The browser may send multiple cookies with our id
-       
-       char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
-       if(cookie)
-               strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
-
-       char action = '\0';
-       char *machine_guid = NULL,
-                       *machine_url = NULL,
-                       *url_name = NULL,
-                       *search_machine_guid = NULL,
-                       *delete_url = NULL,
-                       *to_person_guid = NULL;
+    }
+
+    char person_guid[36 + 1] = "";
+
+    debug(D_WEB_CLIENT, "%llu: API v1 registry with URL '%s'", w->id, url);
+
+    // FIXME
+    // The browser may send multiple cookies with our id
+    
+    char *cookie = strstr(w->response.data->buffer, NETDATA_REGISTRY_COOKIE_NAME "=");
+    if(cookie)
+        strncpyz(person_guid, &cookie[sizeof(NETDATA_REGISTRY_COOKIE_NAME)], 36);
+
+    char action = '\0';
+    char *machine_guid = NULL,
+            *machine_url = NULL,
+            *url_name = NULL,
+            *search_machine_guid = NULL,
+            *delete_url = NULL,
+            *to_person_guid = NULL;
 /*
-       int redirects = 0;
+    int redirects = 0;
 */
 
-       while(url) {
-               char *value = mystrsep(&url, "?&[]");
-               if (!value || !*value) continue;
+    while(url) {
+        char *value = mystrsep(&url, "?&[]");
+        if (!value || !*value) continue;
 
-               char *name = mystrsep(&value, "=");
-               if (!name || !*name) continue;
-               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);
+        debug(D_WEB_CLIENT, "%llu: API v1 registry query param '%s' with value '%s'", w->id, name, value);
 
-               uint32_t hash = simple_hash(name);
+        uint32_t hash = simple_hash(name);
 
-               if(hash == hash_action && !strcmp(name, "action")) {
-                       uint32_t vhash = simple_hash(value);
+        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';
+            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_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;
-               }
+        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);
+        else error("unused registry URL parameter '%s' with value '%s'", name, value);
 #endif /* NETDATA_INTERNAL_CHECKS */
-       }
-
-       if(web_donotrack_comply && w->donottrack) {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
-               return 400;
-       }
-
-       if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
-                                          machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
-               return 400;
-       }
-       else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
-                                          machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
-               return 400;
-       }
-       else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
-                                          machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
-               return 400;
-       }
-       else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
-                                          machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
-               return 400;
-       }
-
-       switch(action) {
-               case 'A':
-                       w->tracking_required = 1;
-                       if(registry_verify_cookies_redirects() > 0 && (!cookie || !person_guid[0])) {
-                               buffer_flush(w->response.data);
-
-                               registry_set_cookie(w, "give-me-back-this-cookie-please");
-                               w->response.data->contenttype = CT_APPLICATION_JSON;
-                               buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry_to_announce());
-                               return 200;
+    }
+
+    if(web_donotrack_comply && w->donottrack) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
+        return 400;
+    }
+
+    if(action == 'A' && (!machine_guid || !machine_url || !url_name)) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Invalid registry request - access requires these parameters: machine ('%s'), url ('%s'), name ('%s')",
+                       machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", url_name?url_name:"UNSET");
+        return 400;
+    }
+    else if(action == 'D' && (!machine_guid || !machine_url || !delete_url)) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Invalid registry request - delete requires these parameters: machine ('%s'), url ('%s'), delete_url ('%s')",
+                       machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", delete_url?delete_url:"UNSET");
+        return 400;
+    }
+    else if(action == 'S' && (!machine_guid || !machine_url || !search_machine_guid)) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')",
+                       machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
+        return 400;
+    }
+    else if(action == 'W' && (!machine_guid || !machine_url || !to_person_guid)) {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Invalid registry request - switching identity requires these parameters: machine ('%s'), url ('%s'), to ('%s')",
+                       machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", to_person_guid?to_person_guid:"UNSET");
+        return 400;
+    }
+
+    switch(action) {
+        case 'A':
+            w->tracking_required = 1;
+            if(registry_verify_cookies_redirects() > 0 && (!cookie || !person_guid[0])) {
+                buffer_flush(w->response.data);
+
+                registry_set_cookie(w, "give-me-back-this-cookie-please");
+                w->response.data->contenttype = CT_APPLICATION_JSON;
+                buffer_sprintf(w->response.data, "{ \"status\": \"redirect\", \"registry\": \"%s\" }", registry_to_announce());
+                return 200;
 
 /*
  * it seems that web browsers are ignoring 307 (Moved Temporarily)
  * under certain conditions, when using CORS
  * so this is commented and we use application level redirects instead
  *
-                               redirects++;
-
-                               if(redirects > registry_verify_cookies_redirects()) {
-                                       buffer_flush(w->response.data);
-                                       buffer_sprintf(w->response.data, "Your browser does not support cookies");
-                                       return 400;
-                               }
-
-                               char *encoded_url = url_encode(machine_url);
-                               if(!encoded_url) {
-                                       error("%llu: Cannot URL encode string '%s'", w->id, machine_url);
-                                       return 500;
-                               }
-
-                               char *encoded_name = url_encode(url_name);
-                               if(!encoded_name) {
-                                       free(encoded_url);
-                                       error("%llu: Cannot URL encode string '%s'", w->id, url_name);
-                                       return 500;
-                               }
-
-                               char *encoded_guid = url_encode(machine_guid);
-                               if(!encoded_guid) {
-                                       free(encoded_url);
-                                       free(encoded_name);
-                                       error("%llu: Cannot URL encode string '%s'", w->id, machine_guid);
-                                       return 500;
-                               }
-
-                               buffer_sprintf(w->response.header, "Location: %s/api/v1/registry?action=access&machine=%s&name=%s&url=%s&redirects=%d\r\n",
-                                                          registry_to_announce(), encoded_guid, encoded_name, encoded_url, redirects);
-
-                               free(encoded_guid);
-                               free(encoded_name);
-                               free(encoded_url);
-                               return 307
+                redirects++;
+
+                if(redirects > registry_verify_cookies_redirects()) {
+                    buffer_flush(w->response.data);
+                    buffer_sprintf(w->response.data, "Your browser does not support cookies");
+                    return 400;
+                }
+
+                char *encoded_url = url_encode(machine_url);
+                if(!encoded_url) {
+                    error("%llu: Cannot URL encode string '%s'", w->id, machine_url);
+                    return 500;
+                }
+
+                char *encoded_name = url_encode(url_name);
+                if(!encoded_name) {
+                    free(encoded_url);
+                    error("%llu: Cannot URL encode string '%s'", w->id, url_name);
+                    return 500;
+                }
+
+                char *encoded_guid = url_encode(machine_guid);
+                if(!encoded_guid) {
+                    free(encoded_url);
+                    free(encoded_name);
+                    error("%llu: Cannot URL encode string '%s'", w->id, machine_guid);
+                    return 500;
+                }
+
+                buffer_sprintf(w->response.header, "Location: %s/api/v1/registry?action=access&machine=%s&name=%s&url=%s&redirects=%d\r\n",
+                               registry_to_announce(), encoded_guid, encoded_name, encoded_url, redirects);
+
+                free(encoded_guid);
+                free(encoded_name);
+                free(encoded_url);
+                return 307
 */
-                       }
-                       return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
+            }
+            return registry_request_access_json(w, person_guid, machine_guid, machine_url, url_name, time(NULL));
 
-               case 'D':
-                       w->tracking_required = 1;
-                       return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, time(NULL));
+        case 'D':
+            w->tracking_required = 1;
+            return registry_request_delete_json(w, person_guid, machine_guid, machine_url, delete_url, time(NULL));
 
-               case 'S':
-                       w->tracking_required = 1;
-                       return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL));
+        case 'S':
+            w->tracking_required = 1;
+            return registry_request_search_json(w, person_guid, machine_guid, machine_url, search_machine_guid, time(NULL));
 
-               case 'W':
-                       w->tracking_required = 1;
-                       return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, time(NULL));
+        case 'W':
+            w->tracking_required = 1;
+            return registry_request_switch_json(w, person_guid, machine_guid, machine_url, to_person_guid, time(NULL));
 
-               case 'H':
-                       return registry_request_hello_json(w);
+        case 'H':
+            return registry_request_hello_json(w);
 
-               default:
-                       buffer_flush(w->response.data);
-                       buffer_sprintf(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
-                       return 400;
-       }
+        default:
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data, "Invalid registry request - you need to set an action: hello, access, delete, search");
+            return 400;
+    }
 
-       buffer_flush(w->response.data);
-       buffer_sprintf(w->response.data, "Invalid or no registry action.");
-       return 400;
+    buffer_flush(w->response.data);
+    buffer_sprintf(w->response.data, "Invalid or no registry action.");
+    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;
-
-       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");
-       }
-
-       // 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_v1_badge(w, url);
-
-               else {
-                       buffer_flush(w->response.data);
-                       buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
-                       return 404;
-               }
-       }
-       else {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "API v1 command?");
-               return 400;
-       }
+    static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 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");
+    }
+
+    // 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_v1_badge(w, url);
+
+        else {
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
+            return 404;
+        }
+    }
+    else {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "API v1 command?");
+        return 400;
+    }
 }
 
 int web_client_api_request(struct web_client *w, char *url)
 {
-       // get the api version
-       char *tok = mystrsep(&url, "/?&");
-       if(tok && *tok) {
-               debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
-               if(strcmp(tok, "v1") == 0)
-                       return web_client_api_request_v1(w, url);
-               else {
-                       buffer_flush(w->response.data);
-                       buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
-                       return 404;
-               }
-       }
-       else {
-               buffer_flush(w->response.data);
-               buffer_sprintf(w->response.data, "Which API version?");
-               return 400;
-       }
+    // get the api version
+    char *tok = mystrsep(&url, "/?&");
+    if(tok && *tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+        if(strcmp(tok, "v1") == 0)
+            return web_client_api_request_v1(w, url);
+        else {
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
+            return 404;
+        }
+    }
+    else {
+        buffer_flush(w->response.data);
+        buffer_sprintf(w->response.data, "Which API version?");
+        return 400;
+    }
 }
 
 int web_client_api_old_data_request(struct web_client *w, char *url, int datasource_type)
 {
-       RRDSET *st = NULL;
-
-       char *args = strchr(url, '?');
-       if(args) {
-               *args='\0';
-               args = &args[1];
-       }
-
-       // get the name of the data to show
-       char *tok = mystrsep(&url, "/");
-       if(!tok) tok = "";
-
-       // do we have such a data set?
-       if(*tok) {
-               debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-               st = rrdset_find_byname(tok);
-               if(!st) st = rrdset_find(tok);
-       }
-
-       if(!st) {
-               // we don't have it
-               // try to send a file with that name
-               buffer_flush(w->response.data);
-               return(mysendfile(w, tok));
-       }
-
-       // we have it
-       debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
-
-       // how many entries does the client want?
-       int lines = rrd_default_history_entries;
-       int group_count = 1;
-       time_t after = 0, before = 0;
-       int group_method = GROUP_AVERAGE;
-       int nonzero = 0;
-
-       if(url) {
-               // parse the lines required
-               tok = mystrsep(&url, "/");
-               if(tok) lines = atoi(tok);
-               if(lines < 1) lines = 1;
-       }
-       if(url) {
-               // parse the group count required
-               tok = mystrsep(&url, "/");
-               if(tok && *tok) group_count = atoi(tok);
-               if(group_count < 1) group_count = 1;
-               //if(group_count > save_history / 20) group_count = save_history / 20;
-       }
-       if(url) {
-               // parse the grouping method required
-               tok = mystrsep(&url, "/");
-               if(tok && *tok) {
-                       if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
-                       else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
-                       else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
-                       else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
-               }
-       }
-       if(url) {
-               // parse after time
-               tok = mystrsep(&url, "/");
-               if(tok && *tok) after = strtoul(tok, NULL, 10);
-               if(after < 0) after = 0;
-       }
-       if(url) {
-               // parse before time
-               tok = mystrsep(&url, "/");
-               if(tok && *tok) before = strtoul(tok, NULL, 10);
-               if(before < 0) before = 0;
-       }
-       if(url) {
-               // parse nonzero
-               tok = mystrsep(&url, "/");
-               if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
-       }
-
-       w->response.data->contenttype = CT_APPLICATION_JSON;
-       buffer_flush(w->response.data);
-
-       char *google_version = "0.6";
-       char *google_reqId = "0";
-       char *google_sig = "0";
-       char *google_out = "json";
-       char *google_responseHandler = "google.visualization.Query.setResponse";
-       char *google_outFileName = NULL;
-       time_t last_timestamp_in_data = 0;
-       if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
-
-               w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
-
-               while(args) {
-                       tok = mystrsep(&args, "&");
-                       if(tok && *tok) {
-                               char *name = mystrsep(&tok, "=");
-                               if(name && *name && strcmp(name, "tqx") == 0) {
-                                       char *key = mystrsep(&tok, ":");
-                                       char *value = mystrsep(&tok, ";");
-                                       if(key && value && *key && *value) {
-                                               if(strcmp(key, "version") == 0)
-                                                       google_version = value;
-
-                                               else if(strcmp(key, "reqId") == 0)
-                                                       google_reqId = value;
-
-                                               else if(strcmp(key, "sig") == 0)
-                                                       google_sig = value;
-
-                                               else if(strcmp(key, "out") == 0)
-                                                       google_out = value;
-
-                                               else if(strcmp(key, "responseHandler") == 0)
-                                                       google_responseHandler = value;
-
-                                               else if(strcmp(key, "outFileName") == 0)
-                                                       google_outFileName = value;
-                                       }
-                               }
-                       }
-               }
-
-               debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
-                       w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
-                       );
-
-               if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-                       last_timestamp_in_data = strtoul(google_sig, NULL, 0);
-
-                       // check the client wants json
-                       if(strcmp(google_out, "json") != 0) {
-                               buffer_sprintf(w->response.data,
-                                       "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
-                                       google_responseHandler, google_version, google_reqId, google_out);
-                                       return 200;
-                       }
-               }
-       }
-
-       if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-               buffer_sprintf(w->response.data,
-                       "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
-                       google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
-       }
-
-       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
-               w->id, st->name, st->id, lines, group_count, group_method, after, before);
-
-       time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
-
-       if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
-               if(timestamp_in_data > last_timestamp_in_data)
-                       buffer_strcat(w->response.data, "});");
-
-               else {
-                       // the client already has the latest data
-                       buffer_flush(w->response.data);
-                       buffer_sprintf(w->response.data,
-                               "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
-                               google_responseHandler, google_version, google_reqId);
-               }
-       }
-
-       return 200;
+    RRDSET *st = NULL;
+
+    char *args = strchr(url, '?');
+    if(args) {
+        *args='\0';
+        args = &args[1];
+    }
+
+    // get the name of the data to show
+    char *tok = mystrsep(&url, "/");
+    if(!tok) tok = "";
+
+    // do we have such a data set?
+    if(*tok) {
+        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+        st = rrdset_find_byname(tok);
+        if(!st) st = rrdset_find(tok);
+    }
+
+    if(!st) {
+        // we don't have it
+        // try to send a file with that name
+        buffer_flush(w->response.data);
+        return(mysendfile(w, tok));
+    }
+
+    // we have it
+    debug(D_WEB_CLIENT, "%llu: Found RRD data with name '%s'.", w->id, tok);
+
+    // how many entries does the client want?
+    int lines = rrd_default_history_entries;
+    int group_count = 1;
+    time_t after = 0, before = 0;
+    int group_method = GROUP_AVERAGE;
+    int nonzero = 0;
+
+    if(url) {
+        // parse the lines required
+        tok = mystrsep(&url, "/");
+        if(tok) lines = atoi(tok);
+        if(lines < 1) lines = 1;
+    }
+    if(url) {
+        // parse the group count required
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) group_count = atoi(tok);
+        if(group_count < 1) group_count = 1;
+        //if(group_count > save_history / 20) group_count = save_history / 20;
+    }
+    if(url) {
+        // parse the grouping method required
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) {
+            if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
+            else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
+            else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
+            else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
+        }
+    }
+    if(url) {
+        // parse after time
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) after = strtoul(tok, NULL, 10);
+        if(after < 0) after = 0;
+    }
+    if(url) {
+        // parse before time
+        tok = mystrsep(&url, "/");
+        if(tok && *tok) before = strtoul(tok, NULL, 10);
+        if(before < 0) before = 0;
+    }
+    if(url) {
+        // parse nonzero
+        tok = mystrsep(&url, "/");
+        if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
+    }
+
+    w->response.data->contenttype = CT_APPLICATION_JSON;
+    buffer_flush(w->response.data);
+
+    char *google_version = "0.6";
+    char *google_reqId = "0";
+    char *google_sig = "0";
+    char *google_out = "json";
+    char *google_responseHandler = "google.visualization.Query.setResponse";
+    char *google_outFileName = NULL;
+    time_t last_timestamp_in_data = 0;
+    if(datasource_type == DATASOURCE_DATATABLE_JSON || datasource_type == DATASOURCE_DATATABLE_JSONP) {
+
+        w->response.data->contenttype = CT_APPLICATION_X_JAVASCRIPT;
+
+        while(args) {
+            tok = mystrsep(&args, "&");
+            if(tok && *tok) {
+                char *name = mystrsep(&tok, "=");
+                if(name && *name && strcmp(name, "tqx") == 0) {
+                    char *key = mystrsep(&tok, ":");
+                    char *value = mystrsep(&tok, ";");
+                    if(key && value && *key && *value) {
+                        if(strcmp(key, "version") == 0)
+                            google_version = value;
+
+                        else if(strcmp(key, "reqId") == 0)
+                            google_reqId = value;
+
+                        else if(strcmp(key, "sig") == 0)
+                            google_sig = value;
+
+                        else if(strcmp(key, "out") == 0)
+                            google_out = value;
+
+                        else if(strcmp(key, "responseHandler") == 0)
+                            google_responseHandler = value;
+
+                        else if(strcmp(key, "outFileName") == 0)
+                            google_outFileName = value;
+                    }
+                }
+            }
+        }
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: GOOGLE JSONP: version = '%s', reqId = '%s', sig = '%s', out = '%s', responseHandler = '%s', outFileName = '%s'",
+            w->id, google_version, google_reqId, google_sig, google_out, google_responseHandler, google_outFileName
+            );
+
+        if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+            last_timestamp_in_data = strtoul(google_sig, NULL, 0);
+
+            // check the client wants json
+            if(strcmp(google_out, "json") != 0) {
+                buffer_sprintf(w->response.data,
+                    "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'invalid_query',message:'output format is not supported',detailed_message:'the format %s requested is not supported by netdata.'}]});",
+                    google_responseHandler, google_version, google_reqId, google_out);
+                    return 200;
+            }
+        }
+    }
+
+    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+        buffer_sprintf(w->response.data,
+            "%s({version:'%s',reqId:'%s',status:'ok',sig:'%ld',table:",
+            google_responseHandler, google_version, google_reqId, st->last_updated.tv_sec);
+    }
+
+    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending RRD data '%s' (id %s, %d lines, %d group, %d group_method, %ld after, %ld before).",
+        w->id, st->name, st->id, lines, group_count, group_method, after, before);
+
+    time_t timestamp_in_data = rrd_stats_json(datasource_type, st, w->response.data, lines, group_count, group_method, (unsigned long)after, (unsigned long)before, nonzero);
+
+    if(datasource_type == DATASOURCE_DATATABLE_JSONP) {
+        if(timestamp_in_data > last_timestamp_in_data)
+            buffer_strcat(w->response.data, "});");
+
+        else {
+            // the client already has the latest data
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data,
+                "%s({version:'%s',reqId:'%s',status:'error',errors:[{reason:'not_modified',message:'Data not modified'}]});",
+                google_responseHandler, google_version, google_reqId);
+        }
+    }
+
+    return 200;
 }
 
 const char *web_content_type_to_string(uint8_t contenttype) {
-       switch(contenttype) {
-               case CT_TEXT_HTML:
-                       return "text/html; charset=utf-8";
+    switch(contenttype) {
+        case CT_TEXT_HTML:
+            return "text/html; charset=utf-8";
 
-               case CT_APPLICATION_XML:
-                       return "application/xml; charset=utf-8";
+        case CT_APPLICATION_XML:
+            return "application/xml; charset=utf-8";
 
-               case CT_APPLICATION_JSON:
-                       return "application/json; charset=utf-8";
+        case CT_APPLICATION_JSON:
+            return "application/json; charset=utf-8";
 
-               case CT_APPLICATION_X_JAVASCRIPT:
-                       return "application/x-javascript; charset=utf-8";
+        case CT_APPLICATION_X_JAVASCRIPT:
+            return "application/x-javascript; charset=utf-8";
 
-               case CT_TEXT_CSS:
-                       return "text/css; charset=utf-8";
+        case CT_TEXT_CSS:
+            return "text/css; charset=utf-8";
 
-               case CT_TEXT_XML:
-                       return "text/xml; charset=utf-8";
+        case CT_TEXT_XML:
+            return "text/xml; charset=utf-8";
 
-               case CT_TEXT_XSL:
-                       return "text/xsl; charset=utf-8";
+        case CT_TEXT_XSL:
+            return "text/xsl; charset=utf-8";
 
-               case CT_APPLICATION_OCTET_STREAM:
-                       return "application/octet-stream";
+        case CT_APPLICATION_OCTET_STREAM:
+            return "application/octet-stream";
 
-               case CT_IMAGE_SVG_XML:
-                       return "image/svg+xml";
+        case CT_IMAGE_SVG_XML:
+            return "image/svg+xml";
 
-               case CT_APPLICATION_X_FONT_TRUETYPE:
-                       return "application/x-font-truetype";
+        case CT_APPLICATION_X_FONT_TRUETYPE:
+            return "application/x-font-truetype";
 
-               case CT_APPLICATION_X_FONT_OPENTYPE:
-                       return "application/x-font-opentype";
+        case CT_APPLICATION_X_FONT_OPENTYPE:
+            return "application/x-font-opentype";
 
-               case CT_APPLICATION_FONT_WOFF:
-                       return "application/font-woff";
+        case CT_APPLICATION_FONT_WOFF:
+            return "application/font-woff";
 
-               case CT_APPLICATION_FONT_WOFF2:
-                       return "application/font-woff2";
+        case CT_APPLICATION_FONT_WOFF2:
+            return "application/font-woff2";
 
-               case CT_APPLICATION_VND_MS_FONTOBJ:
-                       return "application/vnd.ms-fontobject";
+        case CT_APPLICATION_VND_MS_FONTOBJ:
+            return "application/vnd.ms-fontobject";
 
-               case CT_IMAGE_PNG:
-                       return "image/png";
+        case CT_IMAGE_PNG:
+            return "image/png";
 
-               case CT_IMAGE_JPG:
-                       return "image/jpeg";
+        case CT_IMAGE_JPG:
+            return "image/jpeg";
 
-               case CT_IMAGE_GIF:
-                       return "image/gif";
+        case CT_IMAGE_GIF:
+            return "image/gif";
 
-               case CT_IMAGE_XICON:
-                       return "image/x-icon";
+        case CT_IMAGE_XICON:
+            return "image/x-icon";
 
-               case CT_IMAGE_BMP:
-                       return "image/bmp";
+        case CT_IMAGE_BMP:
+            return "image/bmp";
 
-               case CT_IMAGE_ICNS:
-                       return "image/icns";
+        case CT_IMAGE_ICNS:
+            return "image/icns";
 
-               default:
-               case CT_TEXT_PLAIN:
-                       return "text/plain; charset=utf-8";
-       }
+        default:
+        case CT_TEXT_PLAIN:
+            return "text/plain; charset=utf-8";
+    }
 }
 
 
 const char *web_response_code_to_string(int code) {
-       switch(code) {
-               case 200:
-                       return "OK";
+    switch(code) {
+        case 200:
+            return "OK";
 
-               case 307:
-                       return "Temporary Redirect";
+        case 307:
+            return "Temporary Redirect";
 
-               case 400:
-                       return "Bad Request";
+        case 400:
+            return "Bad Request";
 
-               case 403:
-                       return "Forbidden";
+        case 403:
+            return "Forbidden";
 
-               case 404:
-                       return "Not Found";
+        case 404:
+            return "Not Found";
 
-               case 412:
-                       return "Preconditions Failed";
+        case 412:
+            return "Preconditions Failed";
 
-               default:
-                       if(code >= 100 && code < 200)
-                               return "Informational";
+        default:
+            if(code >= 100 && code < 200)
+                return "Informational";
 
-                       if(code >= 200 && code < 300)
-                               return "Successful";
+            if(code >= 200 && code < 300)
+                return "Successful";
 
-                       if(code >= 300 && code < 400)
-                               return "Redirection";
+            if(code >= 300 && code < 400)
+                return "Redirection";
 
-                       if(code >= 400 && code < 500)
-                               return "Bad Request";
+            if(code >= 400 && code < 500)
+                return "Bad Request";
 
-                       if(code >= 500 && code < 600)
-                               return "Server Error";
+            if(code >= 500 && code < 600)
+                return "Server Error";
 
-                       return "Undefined Error";
-       }
+            return "Undefined Error";
+    }
 }
 
 static inline char *http_header_parse(struct web_client *w, char *s) {
-       static uint32_t hash_origin = 0, hash_connection = 0, hash_accept_encoding = 0, hash_donottrack = 0;
-
-       if(unlikely(!hash_origin)) {
-               hash_origin = simple_uhash("Origin");
-               hash_connection = simple_uhash("Connection");
-               hash_accept_encoding = simple_uhash("Accept-Encoding");
-               hash_donottrack = simple_uhash("DNT");
-       }
-
-       char *e = s;
-
-       // find the :
-       while(*e && *e != ':') e++;
-       if(!*e) return e;
-
-       // get the name
-       *e = '\0';
-
-       // find the value
-       char *v = e + 1, *ve;
-
-       // skip leading spaces from value
-       while(*v == ' ') v++;
-       ve = v;
-
-       // find the \r
-       while(*ve && *ve != '\r') ve++;
-       if(!*ve || ve[1] != '\n') {
-               *e = ':';
-               return ve;
-       }
-
-       // terminate the value
-       *ve = '\0';
-
-       // fprintf(stderr, "HEADER: '%s' = '%s'\n", s, v);
-       uint32_t hash = simple_uhash(s);
-
-       if(hash == hash_origin && !strcasecmp(s, "Origin"))
-               strncpyz(w->origin, v, ORIGIN_MAX);
-
-       else if(hash == hash_connection && !strcasecmp(s, "Connection")) {
-               if(strcasestr(v, "keep-alive"))
-                       w->keepalive = 1;
-       }
-       else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
-               if(*v == '0') w->donottrack = 0;
-               else if(*v == '1') w->donottrack = 1;
-       }
+    static uint32_t hash_origin = 0, hash_connection = 0, hash_accept_encoding = 0, hash_donottrack = 0;
+
+    if(unlikely(!hash_origin)) {
+        hash_origin = simple_uhash("Origin");
+        hash_connection = simple_uhash("Connection");
+        hash_accept_encoding = simple_uhash("Accept-Encoding");
+        hash_donottrack = simple_uhash("DNT");
+    }
+
+    char *e = s;
+
+    // find the :
+    while(*e && *e != ':') e++;
+    if(!*e) return e;
+
+    // get the name
+    *e = '\0';
+
+    // find the value
+    char *v = e + 1, *ve;
+
+    // skip leading spaces from value
+    while(*v == ' ') v++;
+    ve = v;
+
+    // find the \r
+    while(*ve && *ve != '\r') ve++;
+    if(!*ve || ve[1] != '\n') {
+        *e = ':';
+        return ve;
+    }
+
+    // terminate the value
+    *ve = '\0';
+
+    // fprintf(stderr, "HEADER: '%s' = '%s'\n", s, v);
+    uint32_t hash = simple_uhash(s);
+
+    if(hash == hash_origin && !strcasecmp(s, "Origin"))
+        strncpyz(w->origin, v, ORIGIN_MAX);
+
+    else if(hash == hash_connection && !strcasecmp(s, "Connection")) {
+        if(strcasestr(v, "keep-alive"))
+            w->keepalive = 1;
+    }
+    else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
+        if(*v == '0') w->donottrack = 0;
+        else if(*v == '1') w->donottrack = 1;
+    }
 #ifdef NETDATA_WITH_ZLIB
-       else if(hash == hash_accept_encoding && !strcasecmp(s, "Accept-Encoding")) {
-               if(web_enable_gzip) {
-                       if(strcasestr(v, "gzip"))
-                               web_client_enable_deflate(w, 1);
-                       //
-                       // does not seem to work
-                       // else if(strcasestr(v, "deflate"))
-                       //      web_client_enable_deflate(w, 0);
-               }
-       }
+    else if(hash == hash_accept_encoding && !strcasecmp(s, "Accept-Encoding")) {
+        if(web_enable_gzip) {
+            if(strcasestr(v, "gzip"))
+                web_client_enable_deflate(w, 1);
+            //
+            // does not seem to work
+            // else if(strcasestr(v, "deflate"))
+            //  web_client_enable_deflate(w, 0);
+        }
+    }
 #endif /* NETDATA_WITH_ZLIB */
 
-       *e = ':';
-       *ve = '\r';
-       return ve;
+    *e = ':';
+    *ve = '\r';
+    return ve;
 }
 
 // http_request_validate()
@@ -1658,758 +1584,760 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
 // < 0 : request is incomplete - wait for more data
 
 static inline int http_request_validate(struct web_client *w) {
-       char *s = w->response.data->buffer, *encoded_url = NULL;
-
-       // is is a valid request?
-       if(!strncmp(s, "GET ", 4)) {
-               encoded_url = s = &s[4];
-               w->mode = WEB_CLIENT_MODE_NORMAL;
-       }
-       else if(!strncmp(s, "OPTIONS ", 8)) {
-               encoded_url = s = &s[8];
-               w->mode = WEB_CLIENT_MODE_OPTIONS;
-       }
-       else {
-               w->wait_receive = 0;
-               return 1;
-       }
-
-       // find the SPACE + "HTTP/"
-       while(*s) {
-               // find the next space
-               while (*s && *s != ' ') s++;
-
-               // is it SPACE + "HTTP/" ?
-               if(*s && !strncmp(s, " HTTP/", 6)) break;
-               else s++;
-       }
-
-       // incomplete requests
-       if(unlikely(!*s)) {
-               w->wait_receive = 1;
-               return -2;
-       }
-
-       // we have the end of encoded_url - remember it
-       char *ue = s;
-
-       // make sure we have complete request
-       // complete requests contain: \r\n\r\n
-       while(*s) {
-               // find a line feed
-               while(*s && *s++ != '\r');
-
-               // did we reach the end?
-               if(unlikely(!*s)) break;
-
-               // is it \r\n ?
-               if(likely(*s++ == '\n')) {
-
-                       // is it again \r\n ? (header end)
-                       if(unlikely(*s == '\r' && s[1] == '\n')) {
-                               // a valid complete HTTP request found
-
-                               *ue = '\0';
-                               url_decode_r(w->decoded_url, encoded_url, URL_MAX + 1);
-                               *ue = ' ';
-                               
-                               // copy the URL - we are going to overwrite parts of it
-                               // FIXME -- we should avoid it
-                               strncpyz(w->last_url, w->decoded_url, URL_MAX);
-
-                               w->wait_receive = 0;
-                               return 0;
-                       }
-
-                       // another header line
-                       s = http_header_parse(w, s);
-               }
-       }
-
-       // incomplete request
-       w->wait_receive = 1;
-       return -3;
+    char *s = w->response.data->buffer, *encoded_url = NULL;
+
+    // is is a valid request?
+    if(!strncmp(s, "GET ", 4)) {
+        encoded_url = s = &s[4];
+        w->mode = WEB_CLIENT_MODE_NORMAL;
+    }
+    else if(!strncmp(s, "OPTIONS ", 8)) {
+        encoded_url = s = &s[8];
+        w->mode = WEB_CLIENT_MODE_OPTIONS;
+    }
+    else {
+        w->wait_receive = 0;
+        return 1;
+    }
+
+    // find the SPACE + "HTTP/"
+    while(*s) {
+        // find the next space
+        while (*s && *s != ' ') s++;
+
+        // is it SPACE + "HTTP/" ?
+        if(*s && !strncmp(s, " HTTP/", 6)) break;
+        else s++;
+    }
+
+    // incomplete requests
+    if(unlikely(!*s)) {
+        w->wait_receive = 1;
+        return -2;
+    }
+
+    // we have the end of encoded_url - remember it
+    char *ue = s;
+
+    // make sure we have complete request
+    // complete requests contain: \r\n\r\n
+    while(*s) {
+        // find a line feed
+        while(*s && *s++ != '\r');
+
+        // did we reach the end?
+        if(unlikely(!*s)) break;
+
+        // is it \r\n ?
+        if(likely(*s++ == '\n')) {
+
+            // is it again \r\n ? (header end)
+            if(unlikely(*s == '\r' && s[1] == '\n')) {
+                // a valid complete HTTP request found
+
+                *ue = '\0';
+                url_decode_r(w->decoded_url, encoded_url, URL_MAX + 1);
+                *ue = ' ';
+                
+                // copy the URL - we are going to overwrite parts of it
+                // FIXME -- we should avoid it
+                strncpyz(w->last_url, w->decoded_url, URL_MAX);
+
+                w->wait_receive = 0;
+                return 0;
+            }
+
+            // another header line
+            s = http_header_parse(w, s);
+        }
+    }
+
+    // incomplete request
+    w->wait_receive = 1;
+    return -3;
 }
 
 void web_client_process(struct web_client *w) {
-       static uint32_t hash_api = 0, hash_netdata_conf = 0, hash_data = 0, hash_datasource = 0, hash_graph = 0,
-                       hash_list = 0, hash_all_json = 0, hash_exit = 0, hash_debug = 0, hash_mirror = 0;
-
-       if(unlikely(!hash_api)) {
-               hash_api = simple_hash("api");
-               hash_netdata_conf = simple_hash("netdata.conf");
-               hash_data = simple_hash(WEB_PATH_DATA);
-               hash_datasource = simple_hash(WEB_PATH_DATASOURCE);
-               hash_graph = simple_hash(WEB_PATH_GRAPH);
-               hash_list = simple_hash("list");
-               hash_all_json = simple_hash("all.json");
-               hash_exit = simple_hash("exit");
-               hash_debug = simple_hash("debug");
-               hash_mirror = simple_hash("mirror");
-       }
-
-       int code = 500;
-       ssize_t bytes;
-
-       int what_to_do = http_request_validate(w);
-
-       // wait for more data
-       if(what_to_do < 0) {
-               if(w->response.data->len > TOO_BIG_REQUEST) {
-                       strcpy(w->last_url, "too big request");
-
-                       debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
-
-                       code = 400;
-                       buffer_flush(w->response.data);
-                       buffer_sprintf(w->response.data, "Received request is too big  (%zu bytes).\r\n", w->response.data->len);
-               }
-               else {
-                       // wait for more data
-                       return;
-               }
-       }
-       else if(what_to_do > 0) {
-               strcpy(w->last_url, "not a valid request");
-
-               debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
-
-               code = 500;
-               buffer_flush(w->response.data);
-               buffer_strcat(w->response.data, "I don't understand you...\r\n");
-       }
-       else { // what_to_do == 0
-               gettimeofday(&w->tv_in, NULL);
-
-               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 = 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);
-                               }
+    static uint32_t hash_api = 0, hash_netdata_conf = 0, hash_data = 0, hash_datasource = 0, hash_graph = 0,
+            hash_list = 0, hash_all_json = 0, hash_exit = 0, hash_debug = 0, hash_mirror = 0;
+
+    // start timing us
+    gettimeofday(&w->tv_in, NULL);
+
+    if(unlikely(!hash_api)) {
+        hash_api = simple_hash("api");
+        hash_netdata_conf = simple_hash("netdata.conf");
+        hash_data = simple_hash(WEB_PATH_DATA);
+        hash_datasource = simple_hash(WEB_PATH_DATASOURCE);
+        hash_graph = simple_hash(WEB_PATH_GRAPH);
+        hash_list = simple_hash("list");
+        hash_all_json = simple_hash("all.json");
+        hash_exit = simple_hash("exit");
+        hash_debug = simple_hash("debug");
+        hash_mirror = simple_hash("mirror");
+    }
+
+    int code = 500;
+    ssize_t bytes;
+
+    int what_to_do = http_request_validate(w);
+
+    // wait for more data
+    if(what_to_do < 0) {
+        if(w->response.data->len > TOO_BIG_REQUEST) {
+            strcpy(w->last_url, "too big request");
+
+            debug(D_WEB_CLIENT_ACCESS, "%llu: Received request is too big (%zu bytes).", w->id, w->response.data->len);
+
+            code = 400;
+            buffer_flush(w->response.data);
+            buffer_sprintf(w->response.data, "Received request is too big  (%zu bytes).\r\n", w->response.data->len);
+        }
+        else {
+            // wait for more data
+            return;
+        }
+    }
+    else if(what_to_do > 0) {
+        // strcpy(w->last_url, "not a valid request");
+
+        debug(D_WEB_CLIENT_ACCESS, "%llu: Cannot understand '%s'.", w->id, w->response.data->buffer);
+
+        code = 500;
+        buffer_flush(w->response.data);
+        buffer_strcat(w->response.data, "I don't understand you...\r\n");
+    }
+    else { // what_to_do == 0
+        if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
+            code = 200;
+            w->response.data->contenttype = CT_TEXT_PLAIN;
+            buffer_flush(w->response.data);
+            buffer_strcat(w->response.data, "OK");
+        }
+        else {
+            char *url = w->decoded_url;
+            char *tok = mystrsep(&url, "/?");
+            if(tok && *tok) {
+                uint32_t hash = simple_hash(tok);
+                debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
+
+                if(hash == hash_api && strcmp(tok, "api") == 0) {
+                    // the client is requesting api access
+                    code = web_client_api_request(w, url);
+                }
+                else if(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0) {
+                    code = 200;
+                    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
+
+                    w->response.data->contenttype = CT_TEXT_PLAIN;
+                    buffer_flush(w->response.data);
+                    generate_config(w->response.data, 0);
+                }
+                else if(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
+                    // the client is requesting rrd data -- OLD API
+                    code = web_client_api_old_data_request(w, url, DATASOURCE_JSON);
+                }
+                else if(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
+                    // the client is requesting google datasource -- OLD API
+                    code = web_client_api_old_data_request(w, url, DATASOURCE_DATATABLE_JSONP);
+                }
+                else if(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
+                    // the client is requesting an rrd graph -- OLD API
+
+                    // get the name of the data to show
+                    tok = mystrsep(&url, "/?&");
+                    if(tok && *tok) {
+                        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+                        // do we have such a data set?
+                        RRDSET *st = rrdset_find_byname(tok);
+                        if(!st) st = rrdset_find(tok);
+                        if(!st) {
+                            // we don't have it
+                            // try to send a file with that name
+                            buffer_flush(w->response.data);
+                            code = mysendfile(w, tok);
+                        }
+                        else {
+                            code = 200;
+                            debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
+                            w->response.data->contenttype = CT_APPLICATION_JSON;
+                            buffer_flush(w->response.data);
+                            rrd_stats_graph_json(st, url, w->response.data);
+                        }
+                    }
+                    else {
+                        code = 400;
+                        buffer_flush(w->response.data);
+                        buffer_strcat(w->response.data, "Graph name?\r\n");
+                    }
+                }
+                else if(hash == hash_list && strcmp(tok, "list") == 0) {
+                    // OLD API
+                    code = 200;
+
+                    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
+
+                    buffer_flush(w->response.data);
+                    RRDSET *st = localhost.rrdset_root;
+
+                    for ( ; st ; st = st->next )
+                        buffer_sprintf(w->response.data, "%s\n", st->name);
+                }
+                else if(hash == hash_all_json && strcmp(tok, "all.json") == 0) {
+                    // OLD API
+                    code = 200;
+                    debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
+
+                    w->response.data->contenttype = CT_APPLICATION_JSON;
+                    buffer_flush(w->response.data);
+                    rrd_stats_all_json(w->response.data);
+                }
 #ifdef NETDATA_INTERNAL_CHECKS
-                               else if(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_exit = 1;
-                               }
-                               else if(hash == hash_debug && strcmp(tok, "debug") == 0) {
-                                       buffer_flush(w->response.data);
-
-                                       // get the name of the data to show
-                                       tok = mystrsep(&url, "/?&");
-                                       if(tok && *tok) {
-                                               debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
-                                               // do we have such a data set?
-                                               RRDSET *st = rrdset_find_byname(tok);
-                                               if(!st) st = rrdset_find(tok);
-                                               if(!st) {
-                                                       code = 404;
-                                                       buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
-                                                       debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
-                                               }
-                                               else {
-                                                       code = 200;
-                                                       debug_flags |= D_RRD_STATS;
-                                                       st->debug = !st->debug;
-                                                       buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
-                                                       debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
-                                               }
-                                       }
-                                       else {
-                                               code = 500;
-                                               buffer_flush(w->response.data);
-                                               buffer_strcat(w->response.data, "debug which chart?\r\n");
-                                       }
-                               }
-                               else if(hash == hash_mirror && strcmp(tok, "mirror") == 0) {
-                                       code = 200;
-
-                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
-
-                                       // replace the zero bytes with spaces
-                                       buffer_char_replace(w->response.data, '\0', ' ');
-
-                                       // just leave the buffer as is
-                                       // it will be copied back to the client
-                               }
-#endif /* NETDATA_INTERNAL_CHECKS */
-                               else {
-                                       char filename[FILENAME_MAX+1];
-                                       url = filename;
-                                       strncpyz(filename, w->last_url, FILENAME_MAX);
-                                       tok = mystrsep(&url, "?");
-                                       buffer_flush(w->response.data);
-                                       code = mysendfile(w, (tok && *tok)?tok:"/");
-                               }
-                       }
-                       else {
-                               char filename[FILENAME_MAX+1];
-                               url = filename;
-                               strncpyz(filename, w->last_url, FILENAME_MAX);
-                               tok = mystrsep(&url, "?");
-                               buffer_flush(w->response.data);
-                               code = mysendfile(w, (tok && *tok)?tok:"/");
-                       }
-               }
-       }
-
-       gettimeofday(&w->tv_ready, NULL);
-       w->response.data->date = time(NULL);
-       w->response.sent = 0;
-       w->response.code = code;
-
-       // prepare the HTTP response header
-       debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
-
-       const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
-       const char *code_msg = web_response_code_to_string(code);
-
-       char date[32];
-       struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
-       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
-
-       buffer_sprintf(w->response.header_output,
-               "HTTP/1.1 %d %s\r\n"
-               "Connection: %s\r\n"
-               "Server: NetData Embedded HTTP Server\r\n"
-               "Access-Control-Allow-Origin: %s\r\n"
-               "Access-Control-Allow-Credentials: true\r\n"
-               "Content-Type: %s\r\n"
-               "Date: %s\r\n"
-               , code, code_msg
-               , w->keepalive?"keep-alive":"close"
-               , w->origin
-               , content_type_string
-               , date
-               );
-
-       if(w->cookie1[0] || w->cookie2[0]) {
-               if(w->cookie1[0]) {
-                       buffer_sprintf(w->response.header_output,
-                          "Set-Cookie: %s\r\n",
-                          w->cookie1);
-               }
-
-               if(w->cookie2[0]) {
-                       buffer_sprintf(w->response.header_output,
-                          "Set-Cookie: %s\r\n",
-                          w->cookie2);
-               }
-
-               if(web_donotrack_comply)
-                       buffer_sprintf(w->response.header_output,
-                          "Tk: T;cookies\r\n");
-       }
-       else {
-               if(web_donotrack_comply) {
-                       if(w->tracking_required)
-                               buffer_sprintf(w->response.header_output,
-                                  "Tk: T;cookies\r\n");
-                       else
-                               buffer_sprintf(w->response.header_output,
-                                  "Tk: N\r\n");
-               }
-       }
-
-       if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
-               buffer_strcat(w->response.header_output,
-                       "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
-                       "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie\r\n"
-                       "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
-                       );
-       }
-
-       if(buffer_strlen(w->response.header))
-               buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
-
-       if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
-               buffer_sprintf(w->response.header_output,
-                       "Expires: %s\r\n"
-                       "Cache-Control: no-cache\r\n"
-                       , date);
-       }
-       else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
-               char edate[32];
-               time_t et = w->response.data->date + (86400 * 14);
-               struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
-               strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
-
-               buffer_sprintf(w->response.header_output,
-                       "Expires: %s\r\n"
-                       "Cache-Control: public\r\n"
-                       , edate);
-       }
-
-       // if we know the content length, put it
-       if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
-               buffer_sprintf(w->response.header_output,
-                       "Content-Length: %zu\r\n"
-                       , w->response.data->len? w->response.data->len: w->response.rlen
-                       );
-       else if(!w->response.zoutput)
-               w->keepalive = 0;       // content-length is required for keep-alive
-
-       if(w->response.zoutput) {
-               buffer_strcat(w->response.header_output,
-                       "Content-Encoding: gzip\r\n"
-                       "Transfer-Encoding: chunked\r\n"
-                       );
-       }
-
-       buffer_strcat(w->response.header_output, "\r\n");
-
-       // sent the HTTP header
-       debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
-                       , w->id
-                       , buffer_strlen(w->response.header_output)
-                       , buffer_tostring(w->response.header_output)
-                       );
-
-       web_client_crock_socket(w);
-
-       bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
-       if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
-               if(bytes > 0)
-                       w->stats_sent_bytes += bytes;
-
-               debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
-                       , w->id
-                       , buffer_strlen(w->response.header_output)
-                       , bytes);
-
-               WEB_CLIENT_IS_DEAD(w);
-               return;
-       }
-       else 
-               w->stats_sent_bytes += bytes;
-
-       // enable sending immediately if we have data
-       if(w->response.data->len) w->wait_send = 1;
-       else w->wait_send = 0;
-
-       // pretty logging
-       switch(w->mode) {
-               case WEB_CLIENT_MODE_OPTIONS:
-                       debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
-                       break;
-
-               case WEB_CLIENT_MODE_NORMAL:
-                       debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
-                       break;
-
-               case WEB_CLIENT_MODE_FILECOPY:
-                       if(w->response.rlen) {
-                               debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
-                               w->wait_receive = 1;
-
-                               /*
-                               // utilize the kernel sendfile() for copying the file to the socket.
-                               // this block of code can be commented, without anything missing.
-                               // when it is commented, the program will copy the data using async I/O.
-                               {
-                                       long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
-                                       if(len != w->response.data->rbytes)
-                                               error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
-                                       else
-                                               web_client_reset(w);
-                               }
-                               */
-                       }
-                       else
-                               debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
-                       break;
-
-               default:
-                       fatal("%llu: Unknown client mode %d.", w->id, w->mode);
-                       break;
-       }
+                else if(hash == hash_exit && strcmp(tok, "exit") == 0) {
+                    code = 200;
+                    w->response.data->contenttype = CT_TEXT_PLAIN;
+                    buffer_flush(w->response.data);
+
+                    if(!netdata_exit)
+                        buffer_strcat(w->response.data, "ok, will do...");
+                    else
+                        buffer_strcat(w->response.data, "I am doing it already");
+
+                    error("web request to exit received.");
+                    netdata_cleanup_and_exit(0);
+                    netdata_exit = 1;
+                }
+                else if(hash == hash_debug && strcmp(tok, "debug") == 0) {
+                    buffer_flush(w->response.data);
+
+                    // get the name of the data to show
+                    tok = mystrsep(&url, "/?&");
+                    if(tok && *tok) {
+                        debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+                        // do we have such a data set?
+                        RRDSET *st = rrdset_find_byname(tok);
+                        if(!st) st = rrdset_find(tok);
+                        if(!st) {
+                            code = 404;
+                            buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
+                            debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+                        }
+                        else {
+                            code = 200;
+                            debug_flags |= D_RRD_STATS;
+                            st->debug = !st->debug;
+                            buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
+                            debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
+                        }
+                    }
+                    else {
+                        code = 500;
+                        buffer_flush(w->response.data);
+                        buffer_strcat(w->response.data, "debug which chart?\r\n");
+                    }
+                }
+                else if(hash == hash_mirror && strcmp(tok, "mirror") == 0) {
+                    code = 200;
+
+                    debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+
+                    // replace the zero bytes with spaces
+                    buffer_char_replace(w->response.data, '\0', ' ');
+
+                    // just leave the buffer as is
+                    // it will be copied back to the client
+                }
+#endif  /* NETDATA_INTERNAL_CHECKS */
+                else {
+                    char filename[FILENAME_MAX+1];
+                    url = filename;
+                    strncpyz(filename, w->last_url, FILENAME_MAX);
+                    tok = mystrsep(&url, "?");
+                    buffer_flush(w->response.data);
+                    code = mysendfile(w, (tok && *tok)?tok:"/");
+                }
+            }
+            else {
+                char filename[FILENAME_MAX+1];
+                url = filename;
+                strncpyz(filename, w->last_url, FILENAME_MAX);
+                tok = mystrsep(&url, "?");
+                buffer_flush(w->response.data);
+                code = mysendfile(w, (tok && *tok)?tok:"/");
+            }
+        }
+    }
+
+    gettimeofday(&w->tv_ready, NULL);
+    w->response.data->date = time(NULL);
+    w->response.sent = 0;
+    w->response.code = code;
+
+    // prepare the HTTP response header
+    debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
+
+    const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
+    const char *code_msg = web_response_code_to_string(code);
+
+    char date[32];
+    struct tm tmbuf, *tm = gmtime_r(&w->response.data->date, &tmbuf);
+    strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
+
+    buffer_sprintf(w->response.header_output,
+        "HTTP/1.1 %d %s\r\n"
+        "Connection: %s\r\n"
+        "Server: NetData Embedded HTTP Server\r\n"
+        "Access-Control-Allow-Origin: %s\r\n"
+        "Access-Control-Allow-Credentials: true\r\n"
+        "Content-Type: %s\r\n"
+        "Date: %s\r\n"
+        , code, code_msg
+        , w->keepalive?"keep-alive":"close"
+        , w->origin
+        , content_type_string
+        , date
+        );
+
+    if(w->cookie1[0] || w->cookie2[0]) {
+        if(w->cookie1[0]) {
+            buffer_sprintf(w->response.header_output,
+               "Set-Cookie: %s\r\n",
+               w->cookie1);
+        }
+
+        if(w->cookie2[0]) {
+            buffer_sprintf(w->response.header_output,
+               "Set-Cookie: %s\r\n",
+               w->cookie2);
+        }
+
+        if(web_donotrack_comply)
+            buffer_sprintf(w->response.header_output,
+               "Tk: T;cookies\r\n");
+    }
+    else {
+        if(web_donotrack_comply) {
+            if(w->tracking_required)
+                buffer_sprintf(w->response.header_output,
+                   "Tk: T;cookies\r\n");
+            else
+                buffer_sprintf(w->response.header_output,
+                   "Tk: N\r\n");
+        }
+    }
+
+    if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
+        buffer_strcat(w->response.header_output,
+            "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
+            "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie\r\n"
+            "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
+            );
+    }
+
+    if(buffer_strlen(w->response.header))
+        buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
+
+    if(w->mode == WEB_CLIENT_MODE_NORMAL && (w->response.data->options & WB_CONTENT_NO_CACHEABLE)) {
+        buffer_sprintf(w->response.header_output,
+            "Expires: %s\r\n"
+            "Cache-Control: no-cache\r\n"
+            , date);
+    }
+    else if(w->mode != WEB_CLIENT_MODE_OPTIONS) {
+        char edate[32];
+        time_t et = w->response.data->date + (86400 * 14);
+        struct tm etmbuf, *etm = gmtime_r(&et, &etmbuf);
+        strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", etm);
+
+        buffer_sprintf(w->response.header_output,
+            "Expires: %s\r\n"
+            "Cache-Control: public\r\n"
+            , edate);
+    }
+
+    // if we know the content length, put it
+    if(!w->response.zoutput && (w->response.data->len || w->response.rlen))
+        buffer_sprintf(w->response.header_output,
+            "Content-Length: %zu\r\n"
+            , w->response.data->len? w->response.data->len: w->response.rlen
+            );
+    else if(!w->response.zoutput)
+        w->keepalive = 0;   // content-length is required for keep-alive
+
+    if(w->response.zoutput) {
+        buffer_strcat(w->response.header_output,
+            "Content-Encoding: gzip\r\n"
+            "Transfer-Encoding: chunked\r\n"
+            );
+    }
+
+    buffer_strcat(w->response.header_output, "\r\n");
+
+    // sent the HTTP header
+    debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
+            , w->id
+            , buffer_strlen(w->response.header_output)
+            , buffer_tostring(w->response.header_output)
+            );
+
+    web_client_crock_socket(w);
+
+    bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
+    if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
+        if(bytes > 0)
+            w->stats_sent_bytes += bytes;
+
+        debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
+            , w->id
+            , buffer_strlen(w->response.header_output)
+            , bytes);
+
+        WEB_CLIENT_IS_DEAD(w);
+        return;
+    }
+    else 
+        w->stats_sent_bytes += bytes;
+
+    // enable sending immediately if we have data
+    if(w->response.data->len) w->wait_send = 1;
+    else w->wait_send = 0;
+
+    // pretty logging
+    switch(w->mode) {
+        case WEB_CLIENT_MODE_OPTIONS:
+            debug(D_WEB_CLIENT, "%llu: Done preparing the OPTIONS response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+            break;
+
+        case WEB_CLIENT_MODE_NORMAL:
+            debug(D_WEB_CLIENT, "%llu: Done preparing the response. Sending data (%zu bytes) to client.", w->id, w->response.data->len);
+            break;
+
+        case WEB_CLIENT_MODE_FILECOPY:
+            if(w->response.rlen) {
+                debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending data file of %zu bytes to client.", w->id, w->response.rlen);
+                w->wait_receive = 1;
+
+                /*
+                // utilize the kernel sendfile() for copying the file to the socket.
+                // this block of code can be commented, without anything missing.
+                // when it is commented, the program will copy the data using async I/O.
+                {
+                    long len = sendfile(w->ofd, w->ifd, NULL, w->response.data->rbytes);
+                    if(len != w->response.data->rbytes)
+                        error("%llu: sendfile() should copy %ld bytes, but copied %ld. Falling back to manual copy.", w->id, w->response.data->rbytes, len);
+                    else
+                        web_client_reset(w);
+                }
+                */
+            }
+            else
+                debug(D_WEB_CLIENT, "%llu: Done preparing the response. Will be sending an unknown amount of bytes to client.", w->id);
+            break;
+
+        default:
+            fatal("%llu: Unknown client mode %d.", w->id, w->mode);
+            break;
+    }
 }
 
 ssize_t web_client_send_chunk_header(struct web_client *w, size_t len)
 {
-       debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
-       char buf[24];
-       sprintf(buf, "%zX\r\n", len);
-       
-       ssize_t bytes = send(w->ofd, buf, strlen(buf), 0);
-       if(bytes > 0) {
-               debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
-               w->stats_sent_bytes += bytes;
-       }
-
-       else if(bytes == 0) {
-               debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-       else {
-               debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-
-       return bytes;
+    debug(D_DEFLATE, "%llu: OPEN CHUNK of %zu bytes (hex: %zx).", w->id, len, len);
+    char buf[24];
+    sprintf(buf, "%zX\r\n", len);
+    
+    ssize_t bytes = send(w->ofd, buf, strlen(buf), 0);
+    if(bytes > 0) {
+        debug(D_DEFLATE, "%llu: Sent chunk header %zd bytes.", w->id, bytes);
+        w->stats_sent_bytes += bytes;
+    }
+
+    else if(bytes == 0) {
+        debug(D_WEB_CLIENT, "%llu: Did not send chunk header to the client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+    else {
+        debug(D_WEB_CLIENT, "%llu: Failed to send chunk header to client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+
+    return bytes;
 }
 
 ssize_t web_client_send_chunk_close(struct web_client *w)
 {
-       //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
-
-       ssize_t bytes = send(w->ofd, "\r\n", 2, 0);
-       if(bytes > 0) {
-               debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
-               w->stats_sent_bytes += bytes;
-       }
-
-       else if(bytes == 0) {
-               debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-       else {
-               debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-
-       return bytes;
+    //debug(D_DEFLATE, "%llu: CLOSE CHUNK.", w->id);
+
+    ssize_t bytes = send(w->ofd, "\r\n", 2, 0);
+    if(bytes > 0) {
+        debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+        w->stats_sent_bytes += bytes;
+    }
+
+    else if(bytes == 0) {
+        debug(D_WEB_CLIENT, "%llu: Did not send chunk suffix to the client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+    else {
+        debug(D_WEB_CLIENT, "%llu: Failed to send chunk suffix to client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+
+    return bytes;
 }
 
 ssize_t web_client_send_chunk_finalize(struct web_client *w)
 {
-       //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
-
-       ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, 0);
-       if(bytes > 0) {
-               debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
-               w->stats_sent_bytes += bytes;
-       }
-
-       else if(bytes == 0) {
-               debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-       else {
-               debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-
-       return bytes;
+    //debug(D_DEFLATE, "%llu: FINALIZE CHUNK.", w->id);
+
+    ssize_t bytes = send(w->ofd, "\r\n0\r\n\r\n", 7, 0);
+    if(bytes > 0) {
+        debug(D_DEFLATE, "%llu: Sent chunk suffix %zd bytes.", w->id, bytes);
+        w->stats_sent_bytes += bytes;
+    }
+
+    else if(bytes == 0) {
+        debug(D_WEB_CLIENT, "%llu: Did not send chunk finalize suffix to the client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+    else {
+        debug(D_WEB_CLIENT, "%llu: Failed to send chunk finalize suffix to client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+
+    return bytes;
 }
 
 #ifdef NETDATA_WITH_ZLIB
 ssize_t web_client_send_deflate(struct web_client *w)
 {
-       ssize_t len = 0, t = 0;
-
-       // when using compression,
-       // w->response.sent is the amount of bytes passed through compression
-
-       debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
-               w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
-
-       if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
-               // there is nothing to send
-
-               debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
-
-               // finalize the chunk
-               if(w->response.sent != 0) {
-                       t = web_client_send_chunk_finalize(w);
-                       if(t < 0) return t;
-               }
-
-               if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->response.rlen && w->response.rlen > w->response.data->len) {
-                       // we have to wait, more data will come
-                       debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
-                       w->wait_send = 0;
-                       return t;
-               }
-
-               if(unlikely(!w->keepalive)) {
-                       debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
-                       WEB_CLIENT_IS_DEAD(w);
-                       return t;
-               }
-
-               // reset the client
-               web_client_reset(w);
-               debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
-               return t;
-       }
-
-       if(w->response.zhave == w->response.zsent) {
-               // compress more input data
-
-               // close the previous open chunk
-               if(w->response.sent != 0) {
-                       t = web_client_send_chunk_close(w);
-                       if(t < 0) return t;
-               }
-
-               debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
-
-               // give the compressor all the data not passed through the compressor yet
-               if(w->response.data->len > w->response.sent) {
-                       w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent - w->response.zstream.avail_in];
-                       w->response.zstream.avail_in += (uInt) (w->response.data->len - w->response.sent);
-               }
-
-               // reset the compressor output buffer
-               w->response.zstream.next_out = w->response.zbuffer;
-               w->response.zstream.avail_out = ZLIB_CHUNK;
-
-               // ask for FINISH if we have all the input
-               int flush = Z_SYNC_FLUSH;
-               if(w->mode == WEB_CLIENT_MODE_NORMAL
-                       || (w->mode == WEB_CLIENT_MODE_FILECOPY && !w->wait_receive && w->response.data->len == w->response.rlen)) {
-                       flush = Z_FINISH;
-                       debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
-               }
-               else {
-                       debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
-               }
-
-               // compress
-               if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
-                       error("%llu: Compression failed. Closing down client.", w->id);
-                       web_client_reset(w);
-                       return(-1);
-               }
-
-               w->response.zhave = ZLIB_CHUNK - w->response.zstream.avail_out;
-               w->response.zsent = 0;
-
-               // keep track of the bytes passed through the compressor
-               w->response.sent = w->response.data->len;
-
-               debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
-
-               // open a new chunk
-               ssize_t t2 = web_client_send_chunk_header(w, w->response.zhave);
-               if(t2 < 0) return t2;
-               t += t2;
-       }
-       
-       debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
-
-       len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
-       if(len > 0) {
-               w->stats_sent_bytes += len;
-               w->response.zsent += len;
-               len += t;
-               debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
-       }
-       else if(len == 0) {
-               debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
-                       w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
-
-               WEB_CLIENT_IS_DEAD(w);
-       }
-       else {
-               debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-
-       return(len);
+    ssize_t len = 0, t = 0;
+
+    // when using compression,
+    // w->response.sent is the amount of bytes passed through compression
+
+    debug(D_DEFLATE, "%llu: web_client_send_deflate(): w->response.data->len = %zu, w->response.sent = %zu, w->response.zhave = %zu, w->response.zsent = %zu, w->response.zstream.avail_in = %u, w->response.zstream.avail_out = %u, w->response.zstream.total_in = %lu, w->response.zstream.total_out = %lu.",
+        w->id, w->response.data->len, w->response.sent, w->response.zhave, w->response.zsent, w->response.zstream.avail_in, w->response.zstream.avail_out, w->response.zstream.total_in, w->response.zstream.total_out);
+
+    if(w->response.data->len - w->response.sent == 0 && w->response.zstream.avail_in == 0 && w->response.zhave == w->response.zsent && w->response.zstream.avail_out != 0) {
+        // there is nothing to send
+
+        debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+
+        // finalize the chunk
+        if(w->response.sent != 0) {
+            t = web_client_send_chunk_finalize(w);
+            if(t < 0) return t;
+        }
+
+        if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->response.rlen && w->response.rlen > w->response.data->len) {
+            // we have to wait, more data will come
+            debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+            w->wait_send = 0;
+            return t;
+        }
+
+        if(unlikely(!w->keepalive)) {
+            debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+            WEB_CLIENT_IS_DEAD(w);
+            return t;
+        }
+
+        // reset the client
+        web_client_reset(w);
+        debug(D_WEB_CLIENT, "%llu: Done sending all data on socket.", w->id);
+        return t;
+    }
+
+    if(w->response.zhave == w->response.zsent) {
+        // compress more input data
+
+        // close the previous open chunk
+        if(w->response.sent != 0) {
+            t = web_client_send_chunk_close(w);
+            if(t < 0) return t;
+        }
+
+        debug(D_DEFLATE, "%llu: Compressing %zu new bytes starting from %zu (and %u left behind).", w->id, (w->response.data->len - w->response.sent), w->response.sent, w->response.zstream.avail_in);
+
+        // give the compressor all the data not passed through the compressor yet
+        if(w->response.data->len > w->response.sent) {
+            w->response.zstream.next_in = (Bytef *)&w->response.data->buffer[w->response.sent - w->response.zstream.avail_in];
+            w->response.zstream.avail_in += (uInt) (w->response.data->len - w->response.sent);
+        }
+
+        // reset the compressor output buffer
+        w->response.zstream.next_out = w->response.zbuffer;
+        w->response.zstream.avail_out = ZLIB_CHUNK;
+
+        // ask for FINISH if we have all the input
+        int flush = Z_SYNC_FLUSH;
+        if(w->mode == WEB_CLIENT_MODE_NORMAL
+            || (w->mode == WEB_CLIENT_MODE_FILECOPY && !w->wait_receive && w->response.data->len == w->response.rlen)) {
+            flush = Z_FINISH;
+            debug(D_DEFLATE, "%llu: Requesting Z_FINISH, if possible.", w->id);
+        }
+        else {
+            debug(D_DEFLATE, "%llu: Requesting Z_SYNC_FLUSH.", w->id);
+        }
+
+        // compress
+        if(deflate(&w->response.zstream, flush) == Z_STREAM_ERROR) {
+            error("%llu: Compression failed. Closing down client.", w->id);
+            web_client_reset(w);
+            return(-1);
+        }
+
+        w->response.zhave = ZLIB_CHUNK - w->response.zstream.avail_out;
+        w->response.zsent = 0;
+
+        // keep track of the bytes passed through the compressor
+        w->response.sent = w->response.data->len;
+
+        debug(D_DEFLATE, "%llu: Compression produced %zu bytes.", w->id, w->response.zhave);
+
+        // open a new chunk
+        ssize_t t2 = web_client_send_chunk_header(w, w->response.zhave);
+        if(t2 < 0) return t2;
+        t += t2;
+    }
+    
+    debug(D_WEB_CLIENT, "%llu: Sending %zu bytes of data (+%zd of chunk header).", w->id, w->response.zhave - w->response.zsent, t);
+
+    len = send(w->ofd, &w->response.zbuffer[w->response.zsent], (size_t) (w->response.zhave - w->response.zsent), MSG_DONTWAIT);
+    if(len > 0) {
+        w->stats_sent_bytes += len;
+        w->response.zsent += len;
+        len += t;
+        debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, len);
+    }
+    else if(len == 0) {
+        debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client (zhave = %zu, zsent = %zu, need to send = %zu).",
+            w->id, w->response.zhave, w->response.zsent, w->response.zhave - w->response.zsent);
+
+        WEB_CLIENT_IS_DEAD(w);
+    }
+    else {
+        debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+
+    return(len);
 }
 #endif // NETDATA_WITH_ZLIB
 
 ssize_t web_client_send(struct web_client *w) {
 #ifdef NETDATA_WITH_ZLIB
-       if(likely(w->response.zoutput)) return web_client_send_deflate(w);
+    if(likely(w->response.zoutput)) return web_client_send_deflate(w);
 #endif // NETDATA_WITH_ZLIB
 
-       ssize_t bytes;
-
-       if(unlikely(w->response.data->len - w->response.sent == 0)) {
-               // there is nothing to send
-
-               debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
-
-               // there can be two cases for this
-               // A. we have done everything
-               // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
-
-               if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->response.rlen && w->response.rlen > w->response.data->len) {
-                       // we have to wait, more data will come
-                       debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
-                       w->wait_send = 0;
-                       return 0;
-               }
-
-               if(unlikely(!w->keepalive)) {
-                       debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
-                       WEB_CLIENT_IS_DEAD(w);
-                       return 0;
-               }
-
-               web_client_reset(w);
-               debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
-               return 0;
-       }
-
-       bytes = send(w->ofd, &w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
-       if(likely(bytes > 0)) {
-               w->stats_sent_bytes += bytes;
-               w->response.sent += bytes;
-               debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
-       }
-       else if(likely(bytes == 0)) {
-               debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-       else {
-               debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-
-       return(bytes);
+    ssize_t bytes;
+
+    if(unlikely(w->response.data->len - w->response.sent == 0)) {
+        // there is nothing to send
+
+        debug(D_WEB_CLIENT, "%llu: Out of output data.", w->id);
+
+        // there can be two cases for this
+        // A. we have done everything
+        // B. we temporarily have nothing to send, waiting for the buffer to be filled by ifd
+
+        if(w->mode == WEB_CLIENT_MODE_FILECOPY && w->wait_receive && w->response.rlen && w->response.rlen > w->response.data->len) {
+            // we have to wait, more data will come
+            debug(D_WEB_CLIENT, "%llu: Waiting for more data to become available.", w->id);
+            w->wait_send = 0;
+            return 0;
+        }
+
+        if(unlikely(!w->keepalive)) {
+            debug(D_WEB_CLIENT, "%llu: Closing (keep-alive is not enabled). %zu bytes sent.", w->id, w->response.sent);
+            WEB_CLIENT_IS_DEAD(w);
+            return 0;
+        }
+
+        web_client_reset(w);
+        debug(D_WEB_CLIENT, "%llu: Done sending all data on socket. Waiting for next request on the same socket.", w->id);
+        return 0;
+    }
+
+    bytes = send(w->ofd, &w->response.data->buffer[w->response.sent], w->response.data->len - w->response.sent, MSG_DONTWAIT);
+    if(likely(bytes > 0)) {
+        w->stats_sent_bytes += bytes;
+        w->response.sent += bytes;
+        debug(D_WEB_CLIENT, "%llu: Sent %zd bytes.", w->id, bytes);
+    }
+    else if(likely(bytes == 0)) {
+        debug(D_WEB_CLIENT, "%llu: Did not send any bytes to the client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+    else {
+        debug(D_WEB_CLIENT, "%llu: Failed to send data to client.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+
+    return(bytes);
 }
 
 ssize_t web_client_receive(struct web_client *w)
 {
-       // do we have any space for more data?
-       buffer_need_bytes(w->response.data, WEB_REQUEST_LENGTH);
-
-       ssize_t left = w->response.data->size - w->response.data->len;
-       ssize_t bytes;
-
-       if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY))
-               bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
-       else
-               bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
-
-       if(likely(bytes > 0)) {
-               if(w->mode != WEB_CLIENT_MODE_FILECOPY)
-                       w->stats_received_bytes += bytes;
-
-               size_t old = w->response.data->len;
-               w->response.data->len += bytes;
-               w->response.data->buffer[w->response.data->len] = '\0';
-
-               debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
-               debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
-
-               if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
-                       w->wait_send = 1;
-
-                       if(w->response.rlen && w->response.data->len >= w->response.rlen)
-                               w->wait_receive = 0;
-               }
-       }
-       else if(likely(bytes == 0)) {
-               debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
-
-               // if we cannot read, it means we have an error on input.
-               // if however, we are copying a file from ifd to ofd, we should not return an error.
-               // in this case, the error should be generated when the file has been sent to the client.
-
-               if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
-                       // we are copying data from ifd to ofd
-                       // let it finish copying...
-                       w->wait_receive = 0;
-
-                       debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
-                       if(w->ifd != w->ofd) close(w->ifd);
-                       w->ifd = w->ofd;
-               }
-               else {
-                       debug(D_WEB_CLIENT, "%llu: failed to receive data.", w->id);
-                       WEB_CLIENT_IS_DEAD(w);
-               }
-       }
-       else {
-               debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
-               WEB_CLIENT_IS_DEAD(w);
-       }
-
-       return(bytes);
+    // do we have any space for more data?
+    buffer_need_bytes(w->response.data, WEB_REQUEST_LENGTH);
+
+    ssize_t left = w->response.data->size - w->response.data->len;
+    ssize_t bytes;
+
+    if(unlikely(w->mode == WEB_CLIENT_MODE_FILECOPY))
+        bytes = read(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1));
+    else
+        bytes = recv(w->ifd, &w->response.data->buffer[w->response.data->len], (size_t) (left - 1), MSG_DONTWAIT);
+
+    if(likely(bytes > 0)) {
+        if(w->mode != WEB_CLIENT_MODE_FILECOPY)
+            w->stats_received_bytes += bytes;
+
+        size_t old = w->response.data->len;
+        w->response.data->len += bytes;
+        w->response.data->buffer[w->response.data->len] = '\0';
+
+        debug(D_WEB_CLIENT, "%llu: Received %zd bytes.", w->id, bytes);
+        debug(D_WEB_DATA, "%llu: Received data: '%s'.", w->id, &w->response.data->buffer[old]);
+
+        if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
+            w->wait_send = 1;
+
+            if(w->response.rlen && w->response.data->len >= w->response.rlen)
+                w->wait_receive = 0;
+        }
+    }
+    else if(likely(bytes == 0)) {
+        debug(D_WEB_CLIENT, "%llu: Out of input data.", w->id);
+
+        // if we cannot read, it means we have an error on input.
+        // if however, we are copying a file from ifd to ofd, we should not return an error.
+        // in this case, the error should be generated when the file has been sent to the client.
+
+        if(w->mode == WEB_CLIENT_MODE_FILECOPY) {
+            // we are copying data from ifd to ofd
+            // let it finish copying...
+            w->wait_receive = 0;
+
+            debug(D_WEB_CLIENT, "%llu: Read the whole file.", w->id);
+            if(w->ifd != w->ofd) close(w->ifd);
+            w->ifd = w->ofd;
+        }
+        else {
+            debug(D_WEB_CLIENT, "%llu: failed to receive data.", w->id);
+            WEB_CLIENT_IS_DEAD(w);
+        }
+    }
+    else {
+        debug(D_WEB_CLIENT, "%llu: receive data failed.", w->id);
+        WEB_CLIENT_IS_DEAD(w);
+    }
+
+    return(bytes);
 }
 
 
@@ -2423,131 +2351,131 @@ ssize_t web_client_receive(struct web_client *w)
 
 void *web_client_main(void *ptr)
 {
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
-
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
-
-       struct web_client *w = ptr;
-       struct pollfd fds[2], *ifd, *ofd;
-       int retval, fdmax = 0, timeout;
-
-       log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid());
-
-       for(;;) {
-               if(unlikely(w->dead)) {
-                       debug(D_WEB_CLIENT, "%llu: client is dead.", w->id);
-                       break;
-               }
-               else if(unlikely(!w->wait_receive && !w->wait_send)) {
-                       debug(D_WEB_CLIENT, "%llu: client is not set for neither receiving nor sending data.", w->id);
-                       break;
-               }
-
-               if(unlikely(w->ifd < 0 || w->ofd < 0)) {
-                       error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd", w->id, w->ifd, w->ofd);
-                       break;
-               }
-
-               if(w->ifd == w->ofd) {
-                       fds[0].fd = w->ifd;
-                       fds[0].events = 0;
-                       fds[0].revents = 0;
-
-                       if(w->wait_receive) fds[0].events |= POLLIN;
-                       if(w->wait_send)    fds[0].events |= POLLOUT;
-
-                       fds[1].fd = -1;
-                       fds[1].events = 0;
-                       fds[1].revents = 0;
-
-                       ifd = ofd = &fds[0];
-
-                       fdmax = 1;
-               }
-               else {
-                       fds[0].fd = w->ifd;
-                       fds[0].events = 0;
-                       fds[0].revents = 0;
-                       if(w->wait_receive) fds[0].events |= POLLIN;
-                       ifd = &fds[0];
-
-                       fds[1].fd = w->ofd;
-                       fds[1].events = 0;
-                       fds[1].revents = 0;
-                       if(w->wait_send)    fds[1].events |= POLLOUT;
-                       ofd = &fds[1];
-
-                       fdmax = 2;
-               }
-
-               debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
-               errno = 0;
-               timeout = web_client_timeout * 1000;
-               retval = poll(fds, fdmax, timeout);
-
-               if(unlikely(retval == -1)) {
-                       if(errno == EAGAIN || errno == EINTR) {
-                               debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id);
-                               continue;
-                       }
-
-                       debug(D_WEB_CLIENT, "%llu: LISTENER: poll() failed (input fd = %d, output fd = %d). Closing client.", w->id, w->ifd, w->ofd);
-                       break;
-               }
-               else if(unlikely(!retval)) {
-                       debug(D_WEB_CLIENT, "%llu: Timeout while waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
-                       break;
-               }
-
-               int used = 0;
-               if(w->wait_send && ofd->revents & POLLOUT) {
-                       used++;
-                       if(web_client_send(w) < 0) {
-                               debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
-                               break;
-                       }
-               }
-
-               if(w->wait_receive && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) {
-                       used++;
-                       if(web_client_receive(w) < 0) {
-                               debug(D_WEB_CLIENT, "%llu: Cannot receive data from client. Closing client.", w->id);
-                               break;
-                       }
-
-                       if(w->mode == WEB_CLIENT_MODE_NORMAL) {
-                               debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id);
-                               web_client_process(w);
-                       }
-               }
-
-               if(unlikely(!used)) {
-                       debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on socket.", w->id);
-                       break;
-               }
-       }
-
-       web_client_reset(w);
-
-       log_access("%llu: %s port %s disconnected from thread task id %d", w->id, w->client_ip, w->client_port, gettid());
-       debug(D_WEB_CLIENT, "%llu: done...", w->id);
-
-       // close the sockets/files now
-       // to free file descriptors
-       if(w->ifd == w->ofd) {
-               if(w->ifd != -1) close(w->ifd);
-       }
-       else {
-               if(w->ifd != -1) close(w->ifd);
-               if(w->ofd != -1) close(w->ofd);
-       }
-       w->ifd = -1;
-       w->ofd = -1;
-
-       w->obsolete = 1;
-
-       pthread_exit(NULL);
-       return NULL;
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
+
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
+
+    struct web_client *w = ptr;
+    struct pollfd fds[2], *ifd, *ofd;
+    int retval, fdmax = 0, timeout;
+
+    log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid());
+
+    for(;;) {
+        if(unlikely(w->dead)) {
+            debug(D_WEB_CLIENT, "%llu: client is dead.", w->id);
+            break;
+        }
+        else if(unlikely(!w->wait_receive && !w->wait_send)) {
+            debug(D_WEB_CLIENT, "%llu: client is not set for neither receiving nor sending data.", w->id);
+            break;
+        }
+
+        if(unlikely(w->ifd < 0 || w->ofd < 0)) {
+            error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd", w->id, w->ifd, w->ofd);
+            break;
+        }
+
+        if(w->ifd == w->ofd) {
+            fds[0].fd = w->ifd;
+            fds[0].events = 0;
+            fds[0].revents = 0;
+
+            if(w->wait_receive) fds[0].events |= POLLIN;
+            if(w->wait_send)    fds[0].events |= POLLOUT;
+
+            fds[1].fd = -1;
+            fds[1].events = 0;
+            fds[1].revents = 0;
+
+            ifd = ofd = &fds[0];
+
+            fdmax = 1;
+        }
+        else {
+            fds[0].fd = w->ifd;
+            fds[0].events = 0;
+            fds[0].revents = 0;
+            if(w->wait_receive) fds[0].events |= POLLIN;
+            ifd = &fds[0];
+
+            fds[1].fd = w->ofd;
+            fds[1].events = 0;
+            fds[1].revents = 0;
+            if(w->wait_send)    fds[1].events |= POLLOUT;
+            ofd = &fds[1];
+
+            fdmax = 2;
+        }
+
+        debug(D_WEB_CLIENT, "%llu: Waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
+        errno = 0;
+        timeout = web_client_timeout * 1000;
+        retval = poll(fds, fdmax, timeout);
+
+        if(unlikely(retval == -1)) {
+            if(errno == EAGAIN || errno == EINTR) {
+                debug(D_WEB_CLIENT, "%llu: EAGAIN received.", w->id);
+                continue;
+            }
+
+            debug(D_WEB_CLIENT, "%llu: LISTENER: poll() failed (input fd = %d, output fd = %d). Closing client.", w->id, w->ifd, w->ofd);
+            break;
+        }
+        else if(unlikely(!retval)) {
+            debug(D_WEB_CLIENT, "%llu: Timeout while waiting socket async I/O for %s %s", w->id, w->wait_receive?"INPUT":"", w->wait_send?"OUTPUT":"");
+            break;
+        }
+
+        int used = 0;
+        if(w->wait_send && ofd->revents & POLLOUT) {
+            used++;
+            if(web_client_send(w) < 0) {
+                debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
+                break;
+            }
+        }
+
+        if(w->wait_receive && (ifd->revents & POLLIN || ifd->revents & POLLPRI)) {
+            used++;
+            if(web_client_receive(w) < 0) {
+                debug(D_WEB_CLIENT, "%llu: Cannot receive data from client. Closing client.", w->id);
+                break;
+            }
+
+            if(w->mode == WEB_CLIENT_MODE_NORMAL) {
+                debug(D_WEB_CLIENT, "%llu: Attempting to process received data.", w->id);
+                web_client_process(w);
+            }
+        }
+
+        if(unlikely(!used)) {
+            debug(D_WEB_CLIENT_ACCESS, "%llu: Received error on socket.", w->id);
+            break;
+        }
+    }
+
+    web_client_reset(w);
+
+    log_access("%llu: %s port %s disconnected from thread task id %d", w->id, w->client_ip, w->client_port, gettid());
+    debug(D_WEB_CLIENT, "%llu: done...", w->id);
+
+    // close the sockets/files now
+    // to free file descriptors
+    if(w->ifd == w->ofd) {
+        if(w->ifd != -1) close(w->ifd);
+    }
+    else {
+        if(w->ifd != -1) close(w->ifd);
+        if(w->ofd != -1) close(w->ofd);
+    }
+    w->ifd = -1;
+    w->ofd = -1;
+
+    w->obsolete = 1;
+
+    pthread_exit(NULL);
+    return NULL;
 }
index a3198857672d0ba3f13b78544340953670915bbe..862acf031a816a8ee77db6890d6a4b21f0ebe3ce 100644 (file)
@@ -1,19 +1,3 @@
-
-#ifdef NETDATA_WITH_ZLIB
-#include <zlib.h>
-#endif
-
-#include <sys/time.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#include "web_buffer.h"
-#include "dictionary.h"
-
 #ifndef NETDATA_WEB_CLIENT_H
 #define NETDATA_WEB_CLIENT_H 1
 
@@ -24,83 +8,83 @@ extern int web_client_timeout;
 extern int web_enable_gzip, web_gzip_level, web_gzip_strategy, web_donotrack_comply;
 #endif /* NETDATA_WITH_ZLIB */
 
-#define WEB_CLIENT_MODE_NORMAL         0
-#define WEB_CLIENT_MODE_FILECOPY       1
-#define WEB_CLIENT_MODE_OPTIONS                2
+#define WEB_CLIENT_MODE_NORMAL      0
+#define WEB_CLIENT_MODE_FILECOPY    1
+#define WEB_CLIENT_MODE_OPTIONS     2
 
 #define URL_MAX 8192
-#define ZLIB_CHUNK     16384
+#define ZLIB_CHUNK  16384
 #define HTTP_RESPONSE_HEADER_SIZE 4096
 #define COOKIE_MAX 1024
 #define ORIGIN_MAX 1024
 
 struct response {
-       BUFFER *header;                                 // our response header
-       BUFFER *header_output;                  // internal use
-       BUFFER *data;                                   // our response data buffer
+    BUFFER *header;                 // our response header
+    BUFFER *header_output;          // internal use
+    BUFFER *data;                   // our response data buffer
 
-       int code;                                               // the HTTP response code
+    int code;                       // the HTTP response code
 
-       size_t rlen;                                    // if non-zero, the excepted size of ifd (input of firecopy)
-       size_t sent;                                    // current data length sent to output
+    size_t rlen;                    // if non-zero, the excepted size of ifd (input of firecopy)
+    size_t sent;                    // current data length sent to output
 
-       int zoutput;                                    // if set to 1, web_client_send() will send compressed data
+    int zoutput;                    // if set to 1, web_client_send() will send compressed data
 #ifdef NETDATA_WITH_ZLIB
-       z_stream zstream;                               // zlib stream for sending compressed output to client
-       Bytef zbuffer[ZLIB_CHUNK];              // temporary buffer for storing compressed output
-       size_t zsent;                                   // the compressed bytes we have sent to the client
-       size_t zhave;                                   // the compressed bytes that we have received from zlib
-       int zinitialized:1;
+    z_stream zstream;               // zlib stream for sending compressed output to client
+    Bytef zbuffer[ZLIB_CHUNK];      // temporary buffer for storing compressed output
+    size_t zsent;                   // the compressed bytes we have sent to the client
+    size_t zhave;                   // the compressed bytes that we have received from zlib
+    int zinitialized:1;
 #endif /* NETDATA_WITH_ZLIB */
 
 };
 
 struct web_client {
-       unsigned long long id;
+    unsigned long long id;
 
-       uint8_t obsolete:1;                                     // if set to 1, the listener will remove this client
-                                                                               // after setting this to 1, you should not touch
-                                                                               // this web_client
+    uint8_t obsolete:1;                 // if set to 1, the listener will remove this client
+                                        // after setting this to 1, you should not touch
+                                        // this web_client
 
-       uint8_t dead:1;                                         // if set to 1, this client is dead
+    uint8_t dead:1;                     // if set to 1, this client is dead
 
-       uint8_t keepalive:1;                            // if set to 1, the web client will be re-used
+    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 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 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
+    uint8_t donottrack:1;               // 1 = we should not set cookies on this client
+    uint8_t tracking_required:1;        // 1 = if the request requires cookies
 
-       int tcp_cork;                                           // 1 = we have a cork on the socket
+    int tcp_cork;                       // 1 = we have a cork on the socket
 
-       int ifd;
-       int ofd;
+    int ifd;
+    int ofd;
 
-       char client_ip[NI_MAXHOST+1];
-       char client_port[NI_MAXSERV+1];
+    char client_ip[NI_MAXHOST+1];
+    char client_port[NI_MAXSERV+1];
 
-       char decoded_url[URL_MAX + 1];  // we decode the URL in this buffer
-       char last_url[URL_MAX+1];               // we keep a copy of the decoded URL here
+    char decoded_url[URL_MAX + 1];  // we decode the URL in this buffer
+    char last_url[URL_MAX+1];       // we keep a copy of the decoded URL here
 
-       struct timeval tv_in, tv_ready;
+    struct timeval tv_in, tv_ready;
 
-       char cookie1[COOKIE_MAX+1];
-       char cookie2[COOKIE_MAX+1];
-       char origin[ORIGIN_MAX+1];
+    char cookie1[COOKIE_MAX+1];
+    char cookie2[COOKIE_MAX+1];
+    char origin[ORIGIN_MAX+1];
 
-       struct sockaddr_storage clientaddr;
-       struct response response;
+    struct sockaddr_storage clientaddr;
+    struct response response;
 
-       size_t stats_received_bytes;
-       size_t stats_sent_bytes;
+    size_t stats_received_bytes;
+    size_t stats_sent_bytes;
 
-       pthread_t thread;                               // the thread servicing this client
+    pthread_t thread;               // the thread servicing this client
 
-       struct web_client *prev;
-       struct web_client *next;
+    struct web_client *prev;
+    struct web_client *next;
 };
 
 #define WEB_CLIENT_IS_DEAD(w) (w)->dead=1
@@ -119,4 +103,5 @@ extern void web_client_reset(struct web_client *w);
 
 extern void *web_client_main(void *ptr);
 
+extern int web_client_api_request_v1_data_group(char *name, int def);
 #endif
index 14ac610fdb325884d4e5be48ead158ccfd51aeb1..cf3687f3e6031779d8b17769730df8ffa739b594 100644 (file)
@@ -1,35 +1,7 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <pthread.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <netinet/tcp.h>
-#include <malloc.h>
-#include <poll.h>
-#include <ctype.h>
-
 #include "common.h"
-#include "log.h"
-#include "appconfig.h"
-#include "url.h"
-#include "web_buffer.h"
-#include "web_client.h"
-#include "web_server.h"
-#include "global_statistics.h"
-#include "rrd.h"
-#include "rrd2json.h"
-#include "../config.h"
 
 int listen_backlog = LISTEN_BACKLOG;
-int listen_fds_count = 0;
+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;
@@ -38,75 +10,75 @@ int web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
 #ifdef NETDATA_INTERNAL_CHECKS
 static void log_allocations(void)
 {
-       static int mem = 0;
+    static int mem = 0;
 
-       struct mallinfo mi;
+    struct mallinfo mi;
 
-       mi = mallinfo();
-       if(mi.uordblks > mem) {
-               int clients = 0;
-               struct web_client *w;
-               for(w = web_clients; w ; w = w->next) clients++;
+    mi = mallinfo();
+    if(mi.uordblks > mem) {
+        int clients = 0;
+        struct web_client *w;
+        for(w = web_clients; w ; w = w->next) clients++;
 
-               info("Allocated memory increased from %d to %d (increased by %d bytes). There are %d web clients connected.", mem, mi.uordblks, mi.uordblks - mem, clients);
-               mem = mi.uordblks;
-       }
+        info("Allocated memory increased from %d to %d (increased by %d bytes). There are %d web clients connected.", mem, mi.uordblks, mi.uordblks - mem, clients);
+        mem = mi.uordblks;
+    }
 }
 #endif
 
 #ifndef HAVE_ACCEPT4
 int accept4(int sock, struct sockaddr *addr, socklen_t *addrlen, int flags) {
-       int fd = accept(sock, addr, addrlen);
-       int newflags = 0;
-
-       if (fd < 0) return fd;
-
-       if (flags & SOCK_NONBLOCK) {
-               newflags |= O_NONBLOCK;
-               flags &= ~SOCK_NONBLOCK;
-       }
-
-       if (flags & SOCK_CLOEXEC) {
-               newflags |= O_CLOEXEC;
-               flags &= ~SOCK_CLOEXEC;
-       }
-
-       if (flags) {
-               errno = -EINVAL;
-               return -1;
-       }
-
-       if (fcntl(fd, F_SETFL, newflags) < 0) {
-               int saved_errno = errno;
-               close(fd);
-               errno = saved_errno;
-               return -1;
-       }
-
-       return fd;
+    int fd = accept(sock, addr, addrlen);
+    int newflags = 0;
+
+    if (fd < 0) return fd;
+
+    if (flags & SOCK_NONBLOCK) {
+        newflags |= O_NONBLOCK;
+        flags &= ~SOCK_NONBLOCK;
+    }
+
+    if (flags & SOCK_CLOEXEC) {
+        newflags |= O_CLOEXEC;
+        flags &= ~SOCK_CLOEXEC;
+    }
+
+    if (flags) {
+        errno = -EINVAL;
+        return -1;
+    }
+
+    if (fcntl(fd, F_SETFL, newflags) < 0) {
+        int saved_errno = errno;
+        close(fd);
+        errno = saved_errno;
+        return -1;
+    }
+
+    return fd;
 }
 #endif
 
 int create_listen_socket4(const char *ip, int port, int listen_backlog) {
-       int sock;
-       int sockopt = 1;
+    int sock;
+    int sockopt = 1;
 
-       debug(D_LISTENER, "IPv4 creating new listening socket on ip '%s' port %d", ip, port);
+    debug(D_LISTENER, "IPv4 creating new listening socket on ip '%s' port %d", ip, port);
 
-       sock = socket(AF_INET, SOCK_STREAM, 0);
-       if(sock < 0) {
-               error("IPv4 socket() on ip '%s' port %d failed.", ip, port);
-               return -1;
-       }
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    if(sock < 0) {
+        error("IPv4 socket() on ip '%s' port %d failed.", ip, port);
+        return -1;
+    }
 
-       /* avoid "address already in use" */
-       if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) != 0)
-               error("Cannot set SO_REUSEADDR on ip '%s' port's %d.", ip, port);
+    /* 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);
+    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) {
@@ -115,34 +87,34 @@ int create_listen_socket4(const char *ip, int port, int listen_backlog) {
         return -1;
     }
 
-       if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
-               close(sock);
-               error("IPv4 bind() on ip '%s' port %d failed.", ip, port);
-               return -1;
-       }
+    if(bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
+        close(sock);
+        error("IPv4 bind() on ip '%s' port %d failed.", ip, port);
+        return -1;
+    }
 
-       if(listen(sock, listen_backlog) < 0) {
-               close(sock);
-               fatal("IPv4 listen() on ip '%s' port %d failed.", ip, port);
-               return -1;
-       }
+    if(listen(sock, listen_backlog) < 0) {
+        close(sock);
+        fatal("IPv4 listen() on ip '%s' port %d failed.", ip, port);
+        return -1;
+    }
 
-       debug(D_LISTENER, "Listening on IPv4 ip '%s' port %d", ip, port);
-       return sock;
+    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 sock = -1;
+    int sockopt = 1;
     int ipv6only = 1;
 
-       debug(D_LISTENER, "IPv6 creating new listening socket on ip '%s' port %d", ip, port);
+    debug(D_LISTENER, "IPv6 creating new listening socket on ip '%s' port %d", ip, port);
 
-       sock = socket(AF_INET6, SOCK_STREAM, 0);
-       if (sock < 0) {
-               error("IPv6 socket() on ip '%s' port %d failed.", ip, port);
-               return -1;
-       }
+    sock = socket(AF_INET6, SOCK_STREAM, 0);
+    if (sock < 0) {
+        error("IPv6 socket() on ip '%s' port %d failed.", ip, port);
+        return -1;
+    }
 
     /* avoid "address already in use" */
     if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sockopt, sizeof(sockopt)) != 0)
@@ -153,9 +125,9 @@ int create_listen_socket6(const char *ip, int port, int listen_backlog) {
         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);
+    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) {
@@ -164,22 +136,22 @@ int create_listen_socket6(const char *ip, int port, int listen_backlog) {
         return -1;
     }
 
-       name.sin6_scope_id = 0;
+    name.sin6_scope_id = 0;
 
-       if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
-               close(sock);
-               error("IPv6 bind() on ip '%s' port %d failed.", ip, port);
-               return -1;
-       }
+    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
+        close(sock);
+        error("IPv6 bind() on ip '%s' port %d failed.", ip, port);
+        return -1;
+    }
 
-       if (listen(sock, listen_backlog) < 0) {
-               close(sock);
-               error("IPv6 listen() on ip '%s' port %d failed.", ip, port);
-               return -1;
-       }
+    if (listen(sock, listen_backlog) < 0) {
+        close(sock);
+        error("IPv6 listen() on ip '%s' port %d failed.", ip, port);
+        return -1;
+    }
 
-       debug(D_LISTENER, "Listening on IPv6 ip '%s' port %d", ip, port);
-       return sock;
+    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) {
@@ -193,14 +165,14 @@ static inline int add_listen_socket(int fd, const char *ip, int port) {
 
     char buffer[100 + 1];
     snprintfz(buffer, 100, "[%s]:%d", ip, port);
-    listen_fds_names[listen_fds_count] = strdup(buffer);
+    listen_fds_names[listen_fds_count] = strdupz(buffer);
 
     listen_fds_count++;
     return 0;
 }
 
 int is_listen_socket(int fd) {
-    int i;
+    size_t i;
     for(i = 0; i < listen_fds_count ;i++)
         if(listen_fds[i] == fd) return 1;
 
@@ -208,12 +180,12 @@ int is_listen_socket(int fd) {
 }
 
 static inline void close_listen_sockets(void) {
-    int i;
+    size_t i;
     for(i = 0; i < listen_fds_count ;i++) {
         close(listen_fds[i]);
         listen_fds[i] = -1;
 
-        if(listen_fds_names[i]) free(listen_fds_names[i]);
+        freez(listen_fds_names[i]);
         listen_fds_names[i] = NULL;
     }
 
@@ -223,7 +195,7 @@ static inline void close_listen_sockets(void) {
 static inline int bind_to_one(const char *definition, int default_port, int listen_backlog) {
     int added = 0;
     struct addrinfo hints;
-    struct addrinfo *result, *rp;
+    struct addrinfo *result = NULL, *rp = NULL;
 
     char buffer[strlen(definition) + 1];
     strcpy(buffer, definition);
@@ -303,11 +275,13 @@ static inline int bind_to_one(const char *definition, int default_port, int list
         }
     }
 
+    freeaddrinfo(result);
+
     return added;
 }
 
 int create_listen_sockets(void) {
-       listen_backlog = (int) config_get_number("global", "http port listen backlog", LISTEN_BACKLOG);
+    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");
@@ -316,11 +290,11 @@ int create_listen_sockets(void) {
         config_rename("global", "port", "default port");
 
     listen_port = (int) config_get_number("global", "default port", LISTEN_PORT);
-       if(listen_port < 1 || listen_port > 65535) {
-               error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
+    if(listen_port < 1 || listen_port > 65535) {
+        error("Invalid listen port %d given. Defaulting to %d.", listen_port, LISTEN_PORT);
         listen_port = (int) config_set_number("global", "default port", LISTEN_PORT);
-       }
-       debug(D_OPTIONS, "Default listen port set to %d.", listen_port);
+    }
+    debug(D_OPTIONS, "Default listen port set to %d.", listen_port);
 
     char *s = config_get("global", "bind to", "*");
     while(*s) {
@@ -345,27 +319,27 @@ int create_listen_sockets(void) {
     if(!listen_fds_count)
         fatal("Cannot listen on any socket. Exiting...");
 
-       return listen_fds_count;
+    return (int)listen_fds_count;
 }
 
 // --------------------------------------------------------------------------------------
 // the main socket listener
 
 static inline void cleanup_web_clients(void) {
-       struct web_client *w;
-
-       for (w = web_clients; w;) {
-               if (w->obsolete) {
-                       debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
-                       // pthread_cancel(w->thread);
-                       // pthread_join(w->thread, NULL);
-                       w = web_client_free(w);
+    struct web_client *w;
+
+    for (w = web_clients; w;) {
+        if (w->obsolete) {
+            debug(D_WEB_CLIENT, "%llu: Removing client.", w->id);
+            // pthread_cancel(w->thread);
+            // pthread_join(w->thread, NULL);
+            w = web_client_free(w);
 #ifdef NETDATA_INTERNAL_CHECKS
-                       log_allocations();
+            log_allocations();
 #endif
-               }
-               else w = w->next;
-       }
+        }
+        else w = w->next;
+    }
 }
 
 // 1. it accepts new incoming requests on our port
@@ -376,257 +350,252 @@ static inline void cleanup_web_clients(void) {
 #define CLEANUP_EVERY_EVENTS 100
 
 void *socket_listen_main_multi_threaded(void *ptr) {
-       (void)ptr;
+    (void)ptr;
 
-       web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
-       info("Multi-threaded WEB SERVER thread created with task id %d", gettid());
+    web_server_mode = WEB_SERVER_MODE_MULTI_THREADED;
+    info("Multi-threaded WEB SERVER thread created with task id %d", gettid());
 
-       struct web_client *w;
-       int retval, counter = 0;
+    struct web_client *w;
+    int retval, counter = 0;
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
 
-       if(!listen_fds_count)
-               fatal("LISTENER: No sockets to listen to.");
+    if(!listen_fds_count)
+        fatal("LISTENER: No sockets to listen to.");
 
-       struct pollfd *fds = calloc(sizeof(struct pollfd), listen_fds_count);
-       if(!fds)
-               fatal("LISTENER: Cannot allocate memory for poll fds.");
+    struct pollfd *fds = callocz(sizeof(struct pollfd), listen_fds_count);
 
-       int i;
-       for(i = 0; i < listen_fds_count ;i++) {
-               fds[i].fd = listen_fds[i];
-               fds[i].events = POLLIN;
-               fds[i].revents = 0;
+    size_t i;
+    for(i = 0; i < listen_fds_count ;i++) {
+        fds[i].fd = listen_fds[i];
+        fds[i].events = POLLIN;
+        fds[i].revents = 0;
 
         info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN");
     }
 
-       int timeout = 10 * 1000;
-
-       for(;;) {
-               // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
-               retval = poll(fds, listen_fds_count, timeout);
-
-               if(unlikely(retval == -1)) {
-                       error("LISTENER: poll() failed.");
-                       continue;
-               }
-               else if(unlikely(!retval)) {
-                       debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
-                       counter = 0;
-                       cleanup_web_clients();
-                       continue;
-               }
-
-               for(i = 0 ; i < listen_fds_count ; i++) {
-                       short int revents = fds[i].revents;
-
-                       // check for new incoming connections
-                       if(revents & POLLIN || revents & POLLPRI) {
-                               fds[i].revents = 0;
-
-                               w = web_client_create(fds[i].fd);
-                               if(unlikely(!w)) {
-                                       // no need for error log - web_client_create already logged the error
-                                       continue;
-                               }
-
-                               if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
-                                       error("%llu: failed to create new thread for web client.", w->id);
-                                       w->obsolete = 1;
-                               }
-                               else if(pthread_detach(w->thread) != 0) {
-                                       error("%llu: Cannot request detach of newly created web client thread.", w->id);
-                                       w->obsolete = 1;
-                               }
-                       }
-               }
-
-               // cleanup unused clients
-               counter++;
-               if(counter >= CLEANUP_EVERY_EVENTS) {
-                       counter = 0;
-                       cleanup_web_clients();
-               }
-       }
-
-       debug(D_WEB_CLIENT, "LISTENER: exit!");
+    int timeout = 10 * 1000;
+
+    for(;;) {
+        // debug(D_WEB_CLIENT, "LISTENER: Waiting...");
+        retval = poll(fds, listen_fds_count, timeout);
+
+        if(unlikely(retval == -1)) {
+            error("LISTENER: poll() failed.");
+            continue;
+        }
+        else if(unlikely(!retval)) {
+            debug(D_WEB_CLIENT, "LISTENER: select() timeout.");
+            counter = 0;
+            cleanup_web_clients();
+            continue;
+        }
+
+        for(i = 0 ; i < listen_fds_count ; i++) {
+            short int revents = fds[i].revents;
+
+            // check for new incoming connections
+            if(revents & POLLIN || revents & POLLPRI) {
+                fds[i].revents = 0;
+
+                w = web_client_create(fds[i].fd);
+                if(unlikely(!w)) {
+                    // no need for error log - web_client_create already logged the error
+                    continue;
+                }
+
+                if(pthread_create(&w->thread, NULL, web_client_main, w) != 0) {
+                    error("%llu: failed to create new thread for web client.", w->id);
+                    w->obsolete = 1;
+                }
+                else if(pthread_detach(w->thread) != 0) {
+                    error("%llu: Cannot request detach of newly created web client thread.", w->id);
+                    w->obsolete = 1;
+                }
+            }
+        }
+
+        // cleanup unused clients
+        counter++;
+        if(counter >= CLEANUP_EVERY_EVENTS) {
+            counter = 0;
+            cleanup_web_clients();
+        }
+    }
+
+    debug(D_WEB_CLIENT, "LISTENER: exit!");
     close_listen_sockets();
 
-       return NULL;
+    return NULL;
 }
 
 struct web_client *single_threaded_clients[FD_SETSIZE];
 
 static inline int single_threaded_link_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds, int *max) {
-       if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
-               return 1;
+    if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
+        return 1;
 
-       if(unlikely(w->ifd < 0 || w->ifd >= FD_SETSIZE || w->ofd < 0 || w->ofd >= FD_SETSIZE)) {
-               error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd < FD_SETSIZE (%d)", w->id, w->ifd, w->ofd, FD_SETSIZE);
-               return 1;
-       }
+    if(unlikely(w->ifd < 0 || w->ifd >= FD_SETSIZE || w->ofd < 0 || w->ofd >= FD_SETSIZE)) {
+        error("%llu: invalid file descriptor, ifd = %d, ofd = %d (required 0 <= fd < FD_SETSIZE (%d)", w->id, w->ifd, w->ofd, FD_SETSIZE);
+        return 1;
+    }
 
-       FD_SET(w->ifd, efds);
-       if(unlikely(*max < w->ifd)) *max = w->ifd;
+    FD_SET(w->ifd, efds);
+    if(unlikely(*max < w->ifd)) *max = w->ifd;
 
-       if(unlikely(w->ifd != w->ofd)) {
-               if(*max < w->ofd) *max = w->ofd;
-               FD_SET(w->ofd, efds);
-       }
+    if(unlikely(w->ifd != w->ofd)) {
+        if(*max < w->ofd) *max = w->ofd;
+        FD_SET(w->ofd, efds);
+    }
 
-       if(w->wait_receive) FD_SET(w->ifd, ifds);
-       if(w->wait_send)    FD_SET(w->ofd, ofds);
+    if(w->wait_receive) FD_SET(w->ifd, ifds);
+    if(w->wait_send)    FD_SET(w->ofd, ofds);
 
-       single_threaded_clients[w->ifd] = w;
-       single_threaded_clients[w->ofd] = w;
+    single_threaded_clients[w->ifd] = w;
+    single_threaded_clients[w->ofd] = w;
 
-       return 0;
+    return 0;
 }
 
 static inline int single_threaded_unlink_client(struct web_client *w, fd_set *ifds, fd_set *ofds, fd_set *efds) {
-       FD_CLR(w->ifd, efds);
-       if(unlikely(w->ifd != w->ofd)) FD_CLR(w->ofd, efds);
+    FD_CLR(w->ifd, efds);
+    if(unlikely(w->ifd != w->ofd)) FD_CLR(w->ofd, efds);
 
-       if(w->wait_receive) FD_CLR(w->ifd, ifds);
-       if(w->wait_send)    FD_CLR(w->ofd, ofds);
+    if(w->wait_receive) FD_CLR(w->ifd, ifds);
+    if(w->wait_send)    FD_CLR(w->ofd, ofds);
 
-       single_threaded_clients[w->ifd] = NULL;
-       single_threaded_clients[w->ofd] = NULL;
+    single_threaded_clients[w->ifd] = NULL;
+    single_threaded_clients[w->ofd] = NULL;
 
-       if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
-               return 1;
+    if(unlikely(w->obsolete || w->dead || (!w->wait_receive && !w->wait_send)))
+        return 1;
 
-       return 0;
+    return 0;
 }
 
 void *socket_listen_main_single_threaded(void *ptr) {
-       (void)ptr;
-
-       web_server_mode = WEB_SERVER_MODE_SINGLE_THREADED;
+    (void)ptr;
 
-       info("Single-threaded WEB SERVER thread created with task id %d", gettid());
+    web_server_mode = WEB_SERVER_MODE_SINGLE_THREADED;
 
-       struct web_client *w;
-       int retval;
+    info("Single-threaded WEB SERVER thread created with task id %d", gettid());
 
-       if(ptr) { ; }
+    struct web_client *w;
+    int retval;
 
-       if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
-               error("Cannot set pthread cancel type to DEFERRED.");
+    if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
+        error("Cannot set pthread cancel type to DEFERRED.");
 
-       if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
-               error("Cannot set pthread cancel state to ENABLE.");
+    if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
+        error("Cannot set pthread cancel state to ENABLE.");
 
-       int i;
-
-       if(!listen_fds_count)
-               fatal("LISTENER: no listen sockets available.");
+    if(!listen_fds_count)
+        fatal("LISTENER: no listen sockets available.");
 
-       for(i = 0; i < FD_SETSIZE ; i++)
-               single_threaded_clients[i] = NULL;
+    size_t i;
+    for(i = 0; i < FD_SETSIZE ; i++)
+        single_threaded_clients[i] = NULL;
 
-       fd_set ifds, ofds, efds, rifds, rofds, refds;
-       FD_ZERO (&ifds);
-       FD_ZERO (&ofds);
-       FD_ZERO (&efds);
-       int fdmax = 0;
+    fd_set ifds, ofds, efds, rifds, rofds, refds;
+    FD_ZERO (&ifds);
+    FD_ZERO (&ofds);
+    FD_ZERO (&efds);
+    int fdmax = 0;
 
-       for(i = 0; i < listen_fds_count ; i++) {
-               if (listen_fds[i] < 0 || listen_fds[i] >= FD_SETSIZE)
-                       fatal("LISTENER: Listen socket %d is not ready, or invalid.", listen_fds[i]);
+    for(i = 0; i < listen_fds_count ; i++) {
+        if (listen_fds[i] < 0 || listen_fds[i] >= FD_SETSIZE)
+            fatal("LISTENER: Listen socket %d is not ready, or invalid.", listen_fds[i]);
 
         info("Listening on '%s'", (listen_fds_names[i])?listen_fds_names[i]:"UNKNOWN");
 
-               FD_SET(listen_fds[i], &ifds);
-               FD_SET(listen_fds[i], &efds);
-               if(fdmax < listen_fds[i])
+        FD_SET(listen_fds[i], &ifds);
+        FD_SET(listen_fds[i], &efds);
+        if(fdmax < listen_fds[i])
             fdmax = listen_fds[i];
-       }
-
-       for(;;) {
-               debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server waiting (fdmax = %d)...", fdmax);
-
-               struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
-               rifds = ifds;
-               rofds = ofds;
-               refds = efds;
-               retval = select(fdmax+1, &rifds, &rofds, &refds, &tv);
-
-               if(unlikely(retval == -1)) {
-                       error("LISTENER: select() failed.");
-                       continue;
-               }
-               else if(likely(retval)) {
-                       debug(D_WEB_CLIENT_ACCESS, "LISTENER: got something.");
-
-                       for(i = 0; i < listen_fds_count ; i++) {
-                               if (FD_ISSET(listen_fds[i], &rifds)) {
-                                       debug(D_WEB_CLIENT_ACCESS, "LISTENER: new connection.");
-                                       w = web_client_create(listen_fds[i]);
-                                       if (single_threaded_link_client(w, &ifds, &ofds, &ifds, &fdmax) != 0) {
-                                               web_client_free(w);
-                                       }
-                               }
-                       }
-
-                       for(i = 0 ; i <= fdmax ; i++) {
-                               if(likely(!FD_ISSET(i, &rifds) && !FD_ISSET(i, &rofds) && !FD_ISSET(i, &refds)))
-                                       continue;
-
-                               w = single_threaded_clients[i];
-                               if(unlikely(!w))
-                                       continue;
-
-                               if(unlikely(single_threaded_unlink_client(w, &ifds, &ofds, &efds) != 0)) {
-                                       web_client_free(w);
-                                       continue;
-                               }
-
-                               if (unlikely(FD_ISSET(w->ifd, &refds) || FD_ISSET(w->ofd, &refds))) {
-                                       web_client_free(w);
-                                       continue;
-                               }
-
-                               if (unlikely(w->wait_receive && FD_ISSET(w->ifd, &rifds))) {
-                                       if (unlikely(web_client_receive(w) < 0)) {
-                                               web_client_free(w);
-                                               continue;
-                                       }
-
-                                       if (w->mode != WEB_CLIENT_MODE_FILECOPY) {
-                                               debug(D_WEB_CLIENT, "%llu: Processing received data.", w->id);
-                                               web_client_process(w);
-                                       }
-                               }
-
-                               if (unlikely(w->wait_send && FD_ISSET(w->ofd, &rofds))) {
-                                       if (unlikely(web_client_send(w) < 0)) {
-                                               debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
-                                               web_client_free(w);
-                                               continue;
-                                       }
-                               }
-
-                               if(unlikely(single_threaded_link_client(w, &ifds, &ofds, &efds, &fdmax) != 0)) {
-                                       web_client_free(w);
-                               }
-                       }
-               }
-               else {
-                       debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server timeout.");
+    }
+
+    for(;;) {
+        debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server waiting (fdmax = %d)...", fdmax);
+
+        struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+        rifds = ifds;
+        rofds = ofds;
+        refds = efds;
+        retval = select(fdmax+1, &rifds, &rofds, &refds, &tv);
+
+        if(unlikely(retval == -1)) {
+            error("LISTENER: select() failed.");
+            continue;
+        }
+        else if(likely(retval)) {
+            debug(D_WEB_CLIENT_ACCESS, "LISTENER: got something.");
+
+            for(i = 0; i < listen_fds_count ; i++) {
+                if (FD_ISSET(listen_fds[i], &rifds)) {
+                    debug(D_WEB_CLIENT_ACCESS, "LISTENER: new connection.");
+                    w = web_client_create(listen_fds[i]);
+                    if (single_threaded_link_client(w, &ifds, &ofds, &ifds, &fdmax) != 0) {
+                        web_client_free(w);
+                    }
+                }
+            }
+
+            for(i = 0 ; i <= (size_t)fdmax ; i++) {
+                if(likely(!FD_ISSET(i, &rifds) && !FD_ISSET(i, &rofds) && !FD_ISSET(i, &refds)))
+                    continue;
+
+                w = single_threaded_clients[i];
+                if(unlikely(!w))
+                    continue;
+
+                if(unlikely(single_threaded_unlink_client(w, &ifds, &ofds, &efds) != 0)) {
+                    web_client_free(w);
+                    continue;
+                }
+
+                if (unlikely(FD_ISSET(w->ifd, &refds) || FD_ISSET(w->ofd, &refds))) {
+                    web_client_free(w);
+                    continue;
+                }
+
+                if (unlikely(w->wait_receive && FD_ISSET(w->ifd, &rifds))) {
+                    if (unlikely(web_client_receive(w) < 0)) {
+                        web_client_free(w);
+                        continue;
+                    }
+
+                    if (w->mode != WEB_CLIENT_MODE_FILECOPY) {
+                        debug(D_WEB_CLIENT, "%llu: Processing received data.", w->id);
+                        web_client_process(w);
+                    }
+                }
+
+                if (unlikely(w->wait_send && FD_ISSET(w->ofd, &rofds))) {
+                    if (unlikely(web_client_send(w) < 0)) {
+                        debug(D_WEB_CLIENT, "%llu: Cannot send data to client. Closing client.", w->id);
+                        web_client_free(w);
+                        continue;
+                    }
+                }
+
+                if(unlikely(single_threaded_link_client(w, &ifds, &ofds, &efds, &fdmax) != 0)) {
+                    web_client_free(w);
+                }
+            }
+        }
+        else {
+            debug(D_WEB_CLIENT_ACCESS, "LISTENER: single threaded web server timeout.");
 #ifdef NETDATA_INTERNAL_CHECKS
-                       log_allocations();
+            log_allocations();
 #endif
-               }
-       }
+        }
+    }
 
-       debug(D_WEB_CLIENT, "LISTENER: exit!");
+    debug(D_WEB_CLIENT, "LISTENER: exit!");
     close_listen_sockets();
-       return NULL;
+    return NULL;
 }
index cea97761d2ffade9998483e1814c5430c5a99e7c..93adc5b28a847f0f5c961f3a56cea86aaae17b70 100644 (file)
@@ -1,10 +1,10 @@
 #ifndef NETDATA_WEB_SERVER_H
 #define NETDATA_WEB_SERVER_H 1
 
-#define WEB_PATH_FILE                          "file"
-#define WEB_PATH_DATA                          "data"
-#define WEB_PATH_DATASOURCE                    "datasource"
-#define WEB_PATH_GRAPH                         "graph"
+#define WEB_PATH_FILE               "file"
+#define WEB_PATH_DATA               "data"
+#define WEB_PATH_DATASOURCE         "datasource"
+#define WEB_PATH_GRAPH              "graph"
 
 #define LISTEN_PORT 19999
 #define LISTEN_BACKLOG 100
index d06944610adc312974f5927b033bedd6cdc1fc64..7858ef0dc5055ad71fd94d0c6e1dab0257098955 100644 (file)
@@ -6,14 +6,8 @@
        delaycompress
        notifempty
        sharedscripts
-       #
-       # if you add netdata to your init.d/system.d
-       # comment su & copytruncate and uncomment postrotate
-       # to have netdata restart when logs are rotated
-       create 0640 netdata netdata
-       copytruncate
-       #
-       #postrotate
-       #       /sbin/service netdata try-restart >/dev/null
-       #endscript
+       create 0664 netdata netdata
+       postrotate
+               /sbin/killall -HUP netdata
+       endscript
 }
index 0dd6eba38d9f18aa4f3d8ee922a3b00390c2e495..2f71735661f22ee95b2289e184cd85aa7ecb57e5 100644 (file)
@@ -1,27 +1,47 @@
 [Unit]
-Description=Linux real time system monitoring, done right
+Description=Real time performance monitoring
 After=network.target httpd.service squid.service nfs-server.service mysqld.service named.service postfix.service
 
 [Service]
-Type=forking
+Type=simple
 WorkingDirectory=/tmp
 User=netdata
 Group=netdata
 RuntimeDirectory=netdata
-PIDFile=@localstatedir_POST@/run/netdata/netdata.pid
-ExecStart=@sbindir_POST@/netdata -P @localstatedir_POST@/run/netdata/netdata.pid
+ExecStartPre=/bin/mkdir -p @localstatedir_POST@/run/netdata
+ExecStartPre=/bin/chown -R netdata:netdata @localstatedir_POST@/run/netdata
+ExecStartPre=/bin/chmod 0775 @localstatedir_POST@/run/netdata
+ExecStart=@sbindir_POST@/netdata -D -P @localstatedir_POST@/run/netdata/netdata.pid
+
+# -----------------------------------------------------------------------------
+# Stopping netdata
+
 KillMode=mixed
 KillSignal=SIGTERM
-TimeoutStopSec=30
 
-#Hardening
-AmbientCapabilities=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
-CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
+# saving a big db on slow disks may need some time
+TimeoutStopSec=60
+
+# and disable SIGKILL - if sent during save, we will loose the db
+SendSIGKILL=no
+
+# -----------------------------------------------------------------------------
+# Hardening netdata
+
+# These will apply these capabilities to the entire netdata process tree
+# We don't want this - only apps.plugin needs them
+# AmbientCapabilities=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
+# CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_SYS_PTRACE
+
 PrivateTmp=true
 ProtectSystem=full
 ProtectHome=read-only
-#NoNewPrivileges=true is implicitly set by the MemoryDenyWriteExecute=true
-MemoryDenyWriteExecute=true
+
+# is implicitly set by the MemoryDenyWriteExecute=true
+# NoNewPrivileges=true
+
+# Do not enable - it makes node.js plugins to crash
+# MemoryDenyWriteExecute=true
 
 [Install]
 WantedBy=multi-user.target
index a64eb9018ce5d120d70cf67a7d7fe2af42a68db4..709af9e6145a222b558c0235d8070b4f4f3af249 100644 (file)
@@ -1,24 +1,24 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>NetData Dashboard</title>
-       <meta name="application-name" content="netdata">
-
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-       <meta name="author" content="costa@tsaousis.gr">
-
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+    <title>NetData Dashboard</title>
+    <meta name="application-name" content="netdata">
+
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+    <meta name="author" content="costa@tsaousis.gr">
+
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
 </head>
 <body>
 
@@ -32,33 +32,33 @@ This is a template for building custom dashboards. To build a dashboard you just
 &lt;!DOCTYPE html>
 &lt;html lang="en">
 &lt;head>
-       &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-       &lt;meta charset="utf-8">
-       &lt;meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       &lt;meta name="viewport" content="width=device-width, initial-scale=1">
-       &lt;meta name="apple-mobile-web-app-capable" content="yes">
-       &lt;meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+    &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    &lt;meta charset="utf-8">
+    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    &lt;meta name="viewport" content="width=device-width, initial-scale=1">
+    &lt;meta name="apple-mobile-web-app-capable" content="yes">
+    &lt;meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
 &lt;/head>
 &lt;body>
-       &lt;div data-netdata="system.processes"
-               data-chart-library="dygraph"
-               data-width="600"
-               data-height="200"
-               data-after="-600"
-               >&lt;/div>
+    &lt;div data-netdata="system.processes"
+        data-chart-library="dygraph"
+        data-width="600"
+        data-height="200"
+        data-after="-600"
+        >&lt;/div>
 &lt;/body>
 &lt;script type="text/javascript" src="http://netdata.server:19999/dashboard.js">&lt;/script>
 &lt;/html>
 </pre>
 
 <ul>
-       <li>You can host your dashboard anywhere.</li>
-       <li>You can add as many charts as you like.</li>
-       <li>You can have charts from many different netdata servers (add <pre>data-host="http://another.netdata.server:19999/"</pre> to each chart).</li>
-       <li>You can use different chart libraries on the same page: <b>peity</b>, <b>sparkline</b>, <b>dygraph</b>, <b>google</b>, <b>morris</b></li>
-       <li>You can customize each chart to your preferences. For each chart library most of their attributes can be given in <b>data-</b> attributes.</li>
-       <li>Each chart can have each own duration - it is controlled with the <b>data-after</b> attribute to give that many seconds of data.</li>
-       <li>Depending on the width of the chart and <b>data-after</b> attribute, netdata will automatically refresh the chart when it needs to be updated. For example giving 600 pixels for width for -600 seconds of data, using a chart library that needs 3 pixels per point, will yeld in a chart updated once every 3 seconds.</li>
+    <li>You can host your dashboard anywhere.</li>
+    <li>You can add as many charts as you like.</li>
+    <li>You can have charts from many different netdata servers (add <pre>data-host="http://another.netdata.server:19999/"</pre> to each chart).</li>
+    <li>You can use different chart libraries on the same page: <b>peity</b>, <b>sparkline</b>, <b>dygraph</b>, <b>google</b>, <b>morris</b></li>
+    <li>You can customize each chart to your preferences. For each chart library most of their attributes can be given in <b>data-</b> attributes.</li>
+    <li>Each chart can have each own duration - it is controlled with the <b>data-after</b> attribute to give that many seconds of data.</li>
+    <li>Depending on the width of the chart and <b>data-after</b> attribute, netdata will automatically refresh the chart when it needs to be updated. For example giving 600 pixels for width for -600 seconds of data, using a chart library that needs 3 pixels per point, will yeld in a chart updated once every 3 seconds.</li>
 </ul>
 
 
@@ -69,61 +69,61 @@ Sparkline charts stretch the values to show the variations between values in mor
 They also have mouse-hover support.
 <br/>
 <b>Sparklines are fantastic</b>. You can inline charts in text. For example this
-       <div    data-netdata="system.cpu"
-                       data-chart-library="sparkline"
-                       data-width="5%"
-                       data-height="20"
-                       data-after="-30"
-                       ></div> is my current cpu usage (last 30 seconds),
-       while this
-       <div    data-netdata="netdata.net"
-                       data-dimensions="out"
-                       data-chart-library="sparkline"
-                       data-width="10%"
-                       data-height="20"
-                       data-after="-60"
-                       ></div> is the bandwidth my netdata server is currently transmitting (last minute)
-       and this
-       <div    data-netdata="netdata.requests"
-                       data-chart-library="sparkline"
-                       data-width="20%"
-                       data-height="20"
-                       data-after="-180"
-                       ></div> is the requests/sec it serves (last 3 minutes).
+    <div    data-netdata="system.cpu"
+            data-chart-library="sparkline"
+            data-width="5%"
+            data-height="20"
+            data-after="-30"
+            ></div> is my current cpu usage (last 30 seconds),
+    while this
+    <div    data-netdata="netdata.net"
+            data-dimensions="out"
+            data-chart-library="sparkline"
+            data-width="10%"
+            data-height="20"
+            data-after="-60"
+            ></div> is the bandwidth my netdata server is currently transmitting (last minute)
+    and this
+    <div    data-netdata="netdata.requests"
+            data-chart-library="sparkline"
+            data-width="20%"
+            data-height="20"
+            data-after="-180"
+            ></div> is the requests/sec it serves (last 3 minutes).
 
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="sparkline"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time101"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time101">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="sparkline"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time101"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time101">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="sparkline"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time102"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time102">X</span> ms</small>
+    <div data-netdata="system.ipv4"
+        data-chart-library="sparkline"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time102"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time102">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="sparkline"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time103"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time103">X</span> ms</small>
+    <div data-netdata="system.cpu"
+        data-chart-library="sparkline"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time103"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time103">X</span> ms</small>
 </div>
 
 
@@ -135,37 +135,37 @@ Peity charts cannot have multiple dimensions on the charts - so netdata will use
 the total of all dimensions.
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="peity"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time001"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time001">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="peity"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time001"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time001">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="peity"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time002"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time002">X</span> ms</small>
+    <div data-netdata="system.ipv4"
+        data-chart-library="peity"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time002"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time002">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="peity"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time003"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time003">X</span> ms</small>
+    <div data-netdata="system.cpu"
+        data-chart-library="peity"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time003"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time003">X</span> ms</small>
 </div>
 
 
@@ -178,173 +178,173 @@ The charts are zoomable (drag their contents to pan, shift with mouse wheel to z
 <b>Netdata magic!</b> Realtime charts on your web page!
 <br/>
 Sparklines using dygraphs
-       <div data-netdata="system.processes"
-               data-chart-library="dygraph"
-               data-dygraph-theme="sparkline"
-               data-legend="no"
-               data-width="15%"
-               data-height="20"
-               data-after="-300"
-               ></div>
-       are also possible! This
-       <div data-netdata="system.ipv4"
-               data-chart-library="dygraph"
-               data-dygraph-theme="sparkline"
-               data-legend="no"
-               data-width="15%"
-               data-height="20"
-               data-after="-300"
-               ></div>
-       is an area chart, while this
-       <div data-netdata="system.cpu"
-               data-chart-library="dygraph"
-               data-dygraph-theme="sparkline"
-               data-legend="no"
-               data-width="15%"
-               data-height="20"
-               data-after="-300"
-               ></div> is a stacked area chart!
+    <div data-netdata="system.processes"
+        data-chart-library="dygraph"
+        data-dygraph-theme="sparkline"
+        data-legend="no"
+        data-width="15%"
+        data-height="20"
+        data-after="-300"
+        ></div>
+    are also possible! This
+    <div data-netdata="system.ipv4"
+        data-chart-library="dygraph"
+        data-dygraph-theme="sparkline"
+        data-legend="no"
+        data-width="15%"
+        data-height="20"
+        data-after="-300"
+        ></div>
+    is an area chart, while this
+    <div data-netdata="system.cpu"
+        data-chart-library="dygraph"
+        data-dygraph-theme="sparkline"
+        data-legend="no"
+        data-width="15%"
+        data-height="20"
+        data-after="-300"
+        ></div> is a stacked area chart!
 
 
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="dygraph"
-               data-dygraph-theme="sparkline"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time501"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time501">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="dygraph"
+        data-dygraph-theme="sparkline"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time501"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time501">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="dygraph"
-               data-dygraph-theme="sparkline"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time502"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time502">X</span> ms</small>
+    <div data-netdata="system.ipv4"
+        data-chart-library="dygraph"
+        data-dygraph-theme="sparkline"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time502"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time502">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="dygraph"
-               data-dygraph-theme="sparkline"
-               data-width="100%"
-               data-height="30"
-               data-after="-300"
-               data-dt-element-name="time503"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time503">X</span> ms</small>
+    <div data-netdata="system.cpu"
+        data-chart-library="dygraph"
+        data-dygraph-theme="sparkline"
+        data-width="100%"
+        data-height="30"
+        data-after="-300"
+        data-dt-element-name="time503"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time503">X</span> ms</small>
 </div>
 
 
 
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time201"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time201">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time202"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time202">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time203"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time203">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.io"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time204"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time204">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="apps.cpu"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time205"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time205">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="netdata.net"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time206"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time206">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="netdata.server_cpu"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time207"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time207">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="netdata.requests"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time208"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time208">X</span> ms</small>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div data-netdata="net.gstag0"
-               data-chart-library="dygraph"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time209"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time209">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time201"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time201">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="system.ipv4"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time202"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time202">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="system.cpu"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time203"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time203">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="system.io"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time204"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time204">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="apps.cpu"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time205"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time205">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="netdata.net"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time206"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time206">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="netdata.server_cpu"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time207"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time207">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="netdata.requests"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time208"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time208">X</span> ms</small>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div data-netdata="net.gstag0"
+        data-chart-library="dygraph"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time209"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time209">X</span> ms</small>
 </div>
 
 
@@ -353,82 +353,82 @@ Sparklines using dygraphs
 <h1>EasyPieChart</h1>
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.processes"
-                       data-chart-library="easypiechart"
-                       data-width="200"
-                       data-height="200"
-                       data-after="-300"
-                       data-points="300"
-                       data-dt-element-name="time601"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time601">X</span> ms</small>
-       </div>
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.processes"
-                       data-chart-library="easypiechart"
-                       data-width="150"
-                       data-height="150"
-                       data-after="-300"
-                       data-points="150"
-                       data-dt-element-name="time601a"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time601a">X</span> ms</small>
-       </div>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.ipv4"
-                       data-chart-library="easypiechart"
-                       data-width="200"
-                       data-height="200"
-                       data-after="-300"
-                       data-points="300"
-                       data-dt-element-name="time602"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time602">X</span> ms</small>
-       </div>
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.ipv4"
-                       data-chart-library="easypiechart"
-                       data-width="100"
-                       data-height="100"
-                       data-after="-300"
-                       data-points="150"
-                       data-dt-element-name="time602a"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time602a">X</span> ms</small>
-       </div>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.cpu"
-                       data-chart-library="easypiechart"
-                       data-width="200"
-                       data-height="200"
-                       data-after="-300"
-                       data-points="300"
-                       data-dt-element-name="time603"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time603">X</span> ms</small>
-       </div>
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.cpu"
-                       data-chart-library="easypiechart"
-                       data-width="75"
-                       data-height="75"
-                       data-after="-300"
-                       data-points="150"
-                       data-dt-element-name="time603a"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time603a">X</span> ms</small>
-       </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.processes"
+            data-chart-library="easypiechart"
+            data-width="200"
+            data-height="200"
+            data-after="-300"
+            data-points="300"
+            data-dt-element-name="time601"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time601">X</span> ms</small>
+    </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.processes"
+            data-chart-library="easypiechart"
+            data-width="150"
+            data-height="150"
+            data-after="-300"
+            data-points="150"
+            data-dt-element-name="time601a"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time601a">X</span> ms</small>
+    </div>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.ipv4"
+            data-chart-library="easypiechart"
+            data-width="200"
+            data-height="200"
+            data-after="-300"
+            data-points="300"
+            data-dt-element-name="time602"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time602">X</span> ms</small>
+    </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.ipv4"
+            data-chart-library="easypiechart"
+            data-width="100"
+            data-height="100"
+            data-after="-300"
+            data-points="150"
+            data-dt-element-name="time602a"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time602a">X</span> ms</small>
+    </div>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.cpu"
+            data-chart-library="easypiechart"
+            data-width="200"
+            data-height="200"
+            data-after="-300"
+            data-points="300"
+            data-dt-element-name="time603"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time603">X</span> ms</small>
+    </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.cpu"
+            data-chart-library="easypiechart"
+            data-width="75"
+            data-height="75"
+            data-after="-300"
+            data-points="150"
+            data-dt-element-name="time603a"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time603a">X</span> ms</small>
+    </div>
 </div>
 
 
@@ -436,82 +436,82 @@ Sparklines using dygraphs
 <h1>Gauge.js</h1>
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.processes"
-                       data-chart-library="gauge"
-                       data-width="250"
-                       data-height="200"
-                       data-after="-300"
-                       data-points="300"
-                       data-dt-element-name="time701"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time701">X</span> ms</small>
-       </div>
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.processes"
-                       data-chart-library="gauge"
-                       data-width="125"
-                       data-height="100"
-                       data-after="-300"
-                       data-points="150"
-                       data-dt-element-name="time701a"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time701a">X</span> ms</small>
-       </div>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.ipv4"
-                       data-chart-library="gauge"
-                       data-width="250"
-                       data-height="200"
-                       data-after="-300"
-                       data-points="300"
-                       data-dt-element-name="time702"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time702">X</span> ms</small>
-       </div>
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.ipv4"
-                       data-chart-library="gauge"
-                       data-width="125"
-                       data-height="100"
-                       data-after="-300"
-                       data-points="150"
-                       data-dt-element-name="time702a"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time702a">X</span> ms</small>
-       </div>
-</div>
-<div style="width: 33%; display: inline-block;">
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.cpu"
-                       data-chart-library="gauge"
-                       data-width="250"
-                       data-height="200"
-                       data-after="-300"
-                       data-points="300"
-                       data-dt-element-name="time703"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time703">X</span> ms</small>
-       </div>
-       <div style="display: inline-block; position: relative;">
-               <div data-netdata="system.cpu"
-                       data-chart-library="gauge"
-                       data-width="125"
-                       data-height="100"
-                       data-after="-300"
-                       data-points="150"
-                       data-dt-element-name="time703a"
-                       ></div>
-               <br/>
-               <small>rendered in <span id="time703a">X</span> ms</small>
-       </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.processes"
+            data-chart-library="gauge"
+            data-width="250"
+            data-height="200"
+            data-after="-300"
+            data-points="300"
+            data-dt-element-name="time701"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time701">X</span> ms</small>
+    </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.processes"
+            data-chart-library="gauge"
+            data-width="125"
+            data-height="100"
+            data-after="-300"
+            data-points="150"
+            data-dt-element-name="time701a"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time701a">X</span> ms</small>
+    </div>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.ipv4"
+            data-chart-library="gauge"
+            data-width="250"
+            data-height="200"
+            data-after="-300"
+            data-points="300"
+            data-dt-element-name="time702"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time702">X</span> ms</small>
+    </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.ipv4"
+            data-chart-library="gauge"
+            data-width="125"
+            data-height="100"
+            data-after="-300"
+            data-points="150"
+            data-dt-element-name="time702a"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time702a">X</span> ms</small>
+    </div>
+</div>
+<div style="width: 33%; display: inline-block;">
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.cpu"
+            data-chart-library="gauge"
+            data-width="250"
+            data-height="200"
+            data-after="-300"
+            data-points="300"
+            data-dt-element-name="time703"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time703">X</span> ms</small>
+    </div>
+    <div style="display: inline-block; position: relative;">
+        <div data-netdata="system.cpu"
+            data-chart-library="gauge"
+            data-width="125"
+            data-height="100"
+            data-after="-300"
+            data-points="150"
+            data-dt-element-name="time703a"
+            ></div>
+        <br/>
+        <small>rendered in <span id="time703a">X</span> ms</small>
+    </div>
 </div>
 
 
@@ -521,37 +521,37 @@ NetData was originaly developed with Google Charts.
 NetData is a complete Google Visualization API provider.
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="google"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time301"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time301">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="google"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time301"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time301">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="google"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time302"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time302">X</span> ms</small>
+    <div data-netdata="system.ipv4"
+        data-chart-library="google"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time302"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time302">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="google"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time303"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time303">X</span> ms</small>
+    <div data-netdata="system.cpu"
+        data-chart-library="google"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time303"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time303">X</span> ms</small>
 </div>
 
 
@@ -564,37 +564,37 @@ NetData is a complete Google Visualization API provider.
 Unfortunatelly, Morris Charts are very slow. Here we force them to lower their detail to get acceptable results.
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="morris"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time401"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time401">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="morris"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time401"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time401">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="morris"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time402"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time402">X</span> ms</small>
+    <div data-netdata="system.ipv4"
+        data-chart-library="morris"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time402"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time402">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="morris"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time403"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time403">X</span> ms</small>
+    <div data-netdata="system.cpu"
+        data-chart-library="morris"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time403"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time403">X</span> ms</small>
 </div>
 
 
@@ -610,37 +610,37 @@ C3 charts are not usable in large scale. They suffer from the following issues:
 So, to avoid flashing the charts, we destroy and re-create the charts on each update. Also, since they manipulate the data with javascript we were forced to lower the detail they render to get acceptable speeds.
 <br/>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.processes"
-               data-chart-library="c3"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time801"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time801">X</span> ms</small>
+    <div data-netdata="system.processes"
+        data-chart-library="c3"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time801"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time801">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.ipv4"
-               data-chart-library="c3"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time802"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time802">X</span> ms</small>
+    <div data-netdata="system.ipv4"
+        data-chart-library="c3"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time802"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time802">X</span> ms</small>
 </div>
 <div style="width: 33%; display: inline-block;">
-       <div data-netdata="system.cpu"
-               data-chart-library="c3"
-               data-width="100%"
-               data-height="200"
-               data-after="-300"
-               data-dt-element-name="time803"
-               ></div>
-       <br/>
-       <small>rendered in <span id="time803">X</span> ms</small>
+    <div data-netdata="system.cpu"
+        data-chart-library="c3"
+        data-width="100%"
+        data-height="200"
+        data-after="-300"
+        data-dt-element-name="time803"
+        ></div>
+    <br/>
+    <small>rendered in <span id="time803">X</span> ms</small>
 </div>
 
 
@@ -650,9 +650,9 @@ So, to avoid flashing the charts, we destroy and re-create the charts on each up
 </div> <!-- 1st container -->
 </body>
 </html>
-       <!-- you can set your netdata server globally, by ucommenting this -->
-       <!-- you can also give a different server per chart, with the attribute: data-host="http://netdata.server:19999" -->
-       <!-- <script> netdataServer = "http://box:19999"; </script> -->
+    <!-- you can set your netdata server globally, by ucommenting this -->
+    <!-- you can also give a different server per chart, with the attribute: data-host="http://netdata.server:19999" -->
+    <!-- <script> netdataServer = "http://box:19999"; </script> -->
 
-       <!-- load the dashboard manager - it will do the rest -->
-       <script type="text/javascript" src="dashboard.js?v39"></script>
+    <!-- load the dashboard manager - it will do the rest -->
+    <script type="text/javascript" src="dashboard.js?v39"></script>
index 0b994589908179211189ee6cd49bec375ef22b64..6bc35031701f65c5ea6c723518a605a8b7d8edb8 100644 (file)
@@ -1,21 +1,21 @@
 // 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 netdataNoRegistry = true;               // Don't update the registry for this access
-// var netdataRegistryCallback = null; // Callback function that will be invoked with one param,
+// 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 netdataNoRegistry = true;        // Don't update the registry for this access
+// var netdataRegistryCallback = null;  // Callback function that will be invoked with one param,
 //                                         the URLs from the registry
-// var netdataShowHelp = true;                 // enable/disable help
+// var netdataShowHelp = true;          // enable/disable help
 //
 // 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
 
 //(function(window, document, undefined) {
 
-       // ------------------------------------------------------------------------
-       // compatibility fixes
-
-       // fix IE issue with console
-       if(!window.console) { window.console = { log: function(){} }; }
-
-       // if string.endsWith is not defined, define it
-       if(typeof String.prototype.endsWith !== 'function') {
-               String.prototype.endsWith = function(s) {
-                       if(s.length > this.length) return false;
-                       return this.slice(-s.length) === s;
-               };
-       }
-
-       // if string.startsWith is not defined, define it
-       if(typeof String.prototype.startsWith !== 'function') {
-               String.prototype.startsWith = function(s) {
-                       if(s.length > this.length) return false;
-                       return this.slice(s.length) === s;
-               };
-       }
-
-       // global namespace
-       var NETDATA = window.NETDATA || {};
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Detect the netdata server
-
-       // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
-       // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
-       NETDATA._scriptSource = function() {
-               var script = null;
-
-               if(typeof document.currentScript !== 'undefined') {
-                       script = document.currentScript;
-               }
-               else {
-                       var all_scripts = document.getElementsByTagName('script');
-                       script = all_scripts[all_scripts.length - 1];
-               }
-
-               if (typeof script.getAttribute.length !== 'undefined')
-                       script = script.src;
-               else
-                       script = script.getAttribute('src', -1);
-
-               return script;
-       };
-
-       if(typeof netdataServer !== 'undefined')
-               NETDATA.serverDefault = netdataServer;
-       else {
-               var s = NETDATA._scriptSource();
-               if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
-               else {
-                       console.log('WARNING: Cannot detect the URL of the netdata server.');
-                       NETDATA.serverDefault = null;
-               }
-       }
-
-       if(NETDATA.serverDefault === null)
-               NETDATA.serverDefault = '';
-       else if(NETDATA.serverDefault.slice(-1) !== '/')
-               NETDATA.serverDefault += '/';
-
-       // default URLs for all the external files we need
-       // make them RELATIVE so that the whole thing can also be
-       // installed under a web server
-       NETDATA.jQuery                  = NETDATA.serverDefault + 'lib/jquery-1.12.0.min.js';
-       NETDATA.peity_js                = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
-       NETDATA.sparkline_js            = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
-       NETDATA.easypiechart_js         = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
-       NETDATA.gauge_js                        = NETDATA.serverDefault + 'lib/gauge.min.js';
-       NETDATA.dygraph_js              = NETDATA.serverDefault + 'lib/dygraph-combined.js';
-       NETDATA.dygraph_smooth_js   = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
-       NETDATA.raphael_js              = NETDATA.serverDefault + 'lib/raphael-min.js';
-       NETDATA.morris_js               = NETDATA.serverDefault + 'lib/morris.min.js';
-       NETDATA.d3_js                           = NETDATA.serverDefault + 'lib/d3.min.js';
-       NETDATA.c3_js                           = NETDATA.serverDefault + 'lib/c3.min.js';
-       NETDATA.c3_css                          = NETDATA.serverDefault + 'css/c3.min.css';
-       NETDATA.morris_css              = NETDATA.serverDefault + 'css/morris.css';
-       NETDATA.google_js               = 'https://www.google.com/jsapi';
-
-       NETDATA.themes = {
-               white: {
-                       bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.min.css',
-                       dashboard_css: NETDATA.serverDefault + 'dashboard.css',
-                       background: '#FFFFFF',
-                       foreground: '#000000',
-                       grid: '#DDDDDD',
-                       axis: '#CCCCCC',
-                       colors: [       '#3366CC', '#DC3912',   '#109618', '#FF9900',   '#990099', '#DD4477',
-                                               '#3B3EAC', '#66AA00',   '#0099C6', '#B82E2E',   '#AAAA11', '#5574A6',
-                                               '#994499', '#22AA99',   '#6633CC', '#E67300',   '#316395', '#8B0707',
-                                               '#329262', '#3B3EAC' ],
-                       easypiechart_track: '#f0f0f0',
-                       easypiechart_scale: '#dfe0e0',
-                       gauge_pointer: '#C0C0C0',
-                       gauge_stroke: '#F0F0F0',
-                       gauge_gradient: false
-               },
-               slate: {
-                       bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
-                       dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css',
-                       background: '#272b30',
-                       foreground: '#C8C8C8',
-                       grid: '#373b40',
-                       axis: '#373b40',
-/*                     colors: [       '#55bb33', '#ff2222',   '#0099C6', '#faa11b',   '#adbce0', '#DDDD00',
-                                               '#4178ba', '#f58122',   '#a5cc39', '#f58667',   '#f5ef89', '#cf93c0',
-                                               '#a5d18a', '#b8539d',   '#3954a3', '#c8a9cf',   '#c7de8a', '#fad20a',
-                                               '#a6a479', '#a66da8' ],
+    // ------------------------------------------------------------------------
+    // compatibility fixes
+
+    // fix IE issue with console
+    if(!window.console) { window.console = { log: function(){} }; }
+
+    // if string.endsWith is not defined, define it
+    if(typeof String.prototype.endsWith !== 'function') {
+        String.prototype.endsWith = function(s) {
+            if(s.length > this.length) return false;
+            return this.slice(-s.length) === s;
+        };
+    }
+
+    // if string.startsWith is not defined, define it
+    if(typeof String.prototype.startsWith !== 'function') {
+        String.prototype.startsWith = function(s) {
+            if(s.length > this.length) return false;
+            return this.slice(s.length) === s;
+        };
+    }
+
+    // global namespace
+    var NETDATA = window.NETDATA || {};
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Detect the netdata server
+
+    // http://stackoverflow.com/questions/984510/what-is-my-script-src-url
+    // http://stackoverflow.com/questions/6941533/get-protocol-domain-and-port-from-url
+    NETDATA._scriptSource = function() {
+        var script = null;
+
+        if(typeof document.currentScript !== 'undefined') {
+            script = document.currentScript;
+        }
+        else {
+            var all_scripts = document.getElementsByTagName('script');
+            script = all_scripts[all_scripts.length - 1];
+        }
+
+        if (typeof script.getAttribute.length !== 'undefined')
+            script = script.src;
+        else
+            script = script.getAttribute('src', -1);
+
+        return script;
+    };
+
+    if(typeof netdataServer !== 'undefined')
+        NETDATA.serverDefault = netdataServer;
+    else {
+        var s = NETDATA._scriptSource();
+        if(s) NETDATA.serverDefault = s.replace(/\/dashboard.js(\?.*)*$/g, "");
+        else {
+            console.log('WARNING: Cannot detect the URL of the netdata server.');
+            NETDATA.serverDefault = null;
+        }
+    }
+
+    if(NETDATA.serverDefault === null)
+        NETDATA.serverDefault = '';
+    else if(NETDATA.serverDefault.slice(-1) !== '/')
+        NETDATA.serverDefault += '/';
+
+    // default URLs for all the external files we need
+    // make them RELATIVE so that the whole thing can also be
+    // installed under a web server
+    NETDATA.jQuery              = NETDATA.serverDefault + 'lib/jquery-1.12.0.min.js';
+    NETDATA.peity_js            = NETDATA.serverDefault + 'lib/jquery.peity.min.js';
+    NETDATA.sparkline_js        = NETDATA.serverDefault + 'lib/jquery.sparkline.min.js';
+    NETDATA.easypiechart_js     = NETDATA.serverDefault + 'lib/jquery.easypiechart.min.js';
+    NETDATA.gauge_js            = NETDATA.serverDefault + 'lib/gauge.min.js';
+    NETDATA.dygraph_js          = NETDATA.serverDefault + 'lib/dygraph-combined.js';
+    NETDATA.dygraph_smooth_js   = NETDATA.serverDefault + 'lib/dygraph-smooth-plotter.js';
+    NETDATA.raphael_js          = NETDATA.serverDefault + 'lib/raphael-min.js';
+    NETDATA.morris_js           = NETDATA.serverDefault + 'lib/morris.min.js';
+    NETDATA.d3_js               = NETDATA.serverDefault + 'lib/d3.min.js';
+    NETDATA.c3_js               = NETDATA.serverDefault + 'lib/c3.min.js';
+    NETDATA.c3_css              = NETDATA.serverDefault + 'css/c3.min.css';
+    NETDATA.morris_css          = NETDATA.serverDefault + 'css/morris.css';
+    NETDATA.google_js           = 'https://www.google.com/jsapi';
+
+    NETDATA.themes = {
+        white: {
+            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.min.css',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.css',
+            background: '#FFFFFF',
+            foreground: '#000000',
+            grid: '#DDDDDD',
+            axis: '#CCCCCC',
+            colors: [   '#3366CC', '#DC3912',   '#109618', '#FF9900',   '#990099', '#DD4477',
+                        '#3B3EAC', '#66AA00',   '#0099C6', '#B82E2E',   '#AAAA11', '#5574A6',
+                        '#994499', '#22AA99',   '#6633CC', '#E67300',   '#316395', '#8B0707',
+                        '#329262', '#3B3EAC' ],
+            easypiechart_track: '#f0f0f0',
+            easypiechart_scale: '#dfe0e0',
+            gauge_pointer: '#C0C0C0',
+            gauge_stroke: '#F0F0F0',
+            gauge_gradient: false
+        },
+        slate: {
+            bootstrap_css: NETDATA.serverDefault + 'css/bootstrap.slate.min.css',
+            dashboard_css: NETDATA.serverDefault + 'dashboard.slate.css',
+            background: '#272b30',
+            foreground: '#C8C8C8',
+            grid: '#373b40',
+            axis: '#373b40',
+/*          colors: [   '#55bb33', '#ff2222',   '#0099C6', '#faa11b',   '#adbce0', '#DDDD00',
+                        '#4178ba', '#f58122',   '#a5cc39', '#f58667',   '#f5ef89', '#cf93c0',
+                        '#a5d18a', '#b8539d',   '#3954a3', '#c8a9cf',   '#c7de8a', '#fad20a',
+                        '#a6a479', '#a66da8' ],
 */
-                       colors: [       '#66AA00', '#FE3912',   '#3366CC', '#D66300',   '#0099C6', '#DDDD00',
-                                               '#5054e6', '#EE9911',   '#BB44CC', '#e45757',   '#ef0aef', '#CC7700',
-                                               '#22AA99', '#109618',   '#905bfd', '#f54882',   '#4381bf', '#ff3737',
-                                               '#329262', '#3B3EFF' ],
-                       easypiechart_track: '#373b40',
-                       easypiechart_scale: '#373b40',
-                       gauge_pointer: '#474b50',
-                       gauge_stroke: '#373b40',
-                       gauge_gradient: false
-               }
-       };
-
-       if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
-               NETDATA.themes.current = NETDATA.themes[netdataTheme];
-       else
-               NETDATA.themes.current = NETDATA.themes.white;
-
-       if(typeof netdataShowHelp === 'undefined')
-               netdataShowHelp = true;
-
-       NETDATA.colors = NETDATA.themes.current.colors;
-
-       // these are the colors Google Charts are using
-       // we have them here to attempt emulate their look and feel on the other chart libraries
-       // http://there4.io/2012/05/02/google-chart-color-list/
-       //NETDATA.colors                = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
-       //                                              '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
-       //                                              '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
+            colors: [   '#66AA00', '#FE3912',   '#3366CC', '#D66300',   '#0099C6', '#DDDD00',
+                        '#5054e6', '#EE9911',   '#BB44CC', '#e45757',   '#ef0aef', '#CC7700',
+                        '#22AA99', '#109618',   '#905bfd', '#f54882',   '#4381bf', '#ff3737',
+                        '#329262', '#3B3EFF' ],
+            easypiechart_track: '#373b40',
+            easypiechart_scale: '#373b40',
+            gauge_pointer: '#474b50',
+            gauge_stroke: '#373b40',
+            gauge_gradient: false
+        }
+    };
+
+    if(typeof netdataTheme !== 'undefined' && typeof NETDATA.themes[netdataTheme] !== 'undefined')
+        NETDATA.themes.current = NETDATA.themes[netdataTheme];
+    else
+        NETDATA.themes.current = NETDATA.themes.white;
+
+    if(typeof netdataShowHelp === 'undefined')
+        netdataShowHelp = true;
+
+    NETDATA.colors = NETDATA.themes.current.colors;
+
+    // these are the colors Google Charts are using
+    // we have them here to attempt emulate their look and feel on the other chart libraries
+    // http://there4.io/2012/05/02/google-chart-color-list/
+    //NETDATA.colors        = [ '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099', '#3B3EAC', '#0099C6',
+    //                      '#DD4477', '#66AA00', '#B82E2E', '#316395', '#994499', '#22AA99', '#AAAA11',
+    //                      '#6633CC', '#E67300', '#8B0707', '#329262', '#5574A6', '#3B3EAC' ];
 
-       // an alternative set
-       // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
-       //                         (blue)     (red)      (orange)   (green)    (pink)     (brown)    (purple)   (yellow)   (gray)
-       //NETDATA.colors                = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
+    // an alternative set
+    // http://www.mulinblog.com/a-color-palette-optimized-for-data-visualization/
+    //                         (blue)     (red)      (orange)   (green)    (pink)     (brown)    (purple)   (yellow)   (gray)
+    //NETDATA.colors        = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
 
-       // ----------------------------------------------------------------------------------------------------------------
-       // the defaults for all charts
+    // ----------------------------------------------------------------------------------------------------------------
+    // the defaults for all charts
 
-       // if the user does not specify any of these, the following will be used
+    // if the user does not specify any of these, the following will be used
 
-       NETDATA.chartDefaults = {
-               host: NETDATA.serverDefault,    // the server to get data from
-               width: '100%',                                  // the chart width - can be null
-               height: '100%',                                 // the chart height - can be null
-               min_width: null,                                // the chart minimum width - can be null
-               library: 'dygraph',                             // the graphing library to use
-               method: 'average',                              // the grouping method
-               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
-       };
+    NETDATA.chartDefaults = {
+        host: NETDATA.serverDefault,    // the server to get data from
+        width: '100%',                  // the chart width - can be null
+        height: '100%',                 // the chart height - can be null
+        min_width: null,                // the chart minimum width - can be null
+        library: 'dygraph',             // the graphing library to use
+        method: 'average',              // the grouping method
+        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
+    };
 
-       // ----------------------------------------------------------------------------------------------------------------
-       // global options
+    // ----------------------------------------------------------------------------------------------------------------
+    // global options
 
-       NETDATA.options = {
-               pauseCallback: null,                    // a callback when we are really paused
+    NETDATA.options = {
+        pauseCallback: null,            // a callback when we are really paused
 
-               pause: false,                                   // when enabled we don't auto-refresh the charts
+        pause: false,                   // when enabled we don't auto-refresh the charts
 
-               targets: null,                                  // an array of all the state objects that are
-                                                                               // currently active (independently of their
-                                                                               // viewport visibility)
+        targets: null,                  // an array of all the state objects that are
+                                        // currently active (independently of their
+                                        // viewport visibility)
 
-               updated_dom: true,                              // when true, the DOM has been updated with
-                                                                               // new elements we have to check.
+        updated_dom: true,              // when true, the DOM has been updated with
+                                        // new elements we have to check.
 
-               auto_refresher_fast_weight: 0,  // this is the current time in ms, spent
-                                                                               // rendering charts continiously.
-                                                                               // used with .current.fast_render_timeframe
+        auto_refresher_fast_weight: 0,  // this is the current time in ms, spent
+                                        // rendering charts continiously.
+                                        // used with .current.fast_render_timeframe
 
-               page_is_visible: true,                  // when true, this page is visible
+        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 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
-                                                                               // rendering the chart that is panned or zoomed).
-                                                                               // Used with .current.global_pan_sync_time
+        auto_refresher_stop_until: 0,   // timestamp in ms - used internaly, 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
+                                        // rendering the chart that is panned or zoomed).
+                                        // Used with .current.global_pan_sync_time
 
-               last_resized: new Date().getTime(), // the timestamp of the last resize request
+        last_resized: new Date().getTime(), // the timestamp of the last resize request
 
-               last_page_scroll: 0,                    // the timestamp the last time the page was scrolled
+        last_page_scroll: 0,            // the timestamp the last time the page was scrolled
 
-               // the current profile
-               // we may have many...
-               current: {
-                       pixels_per_point: 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
-                                                                               // the final is calculated every time, so a change
-                                                                               // here will have immediate effect on the next chart
-                                                                               // update
+        // the current profile
+        // we may have many...
+        current: {
+            pixels_per_point: 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
+                                        // the final is calculated every time, so a change
+                                        // here will have immediate effect on the next chart
+                                        // update
 
-                       idle_between_charts: 100,       // ms - how much time to wait between chart updates
+            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
-                                                                               // rendering has been reached
-                                                                               // this setting is used to make it render e.g. 10
-                                                                               // charts at once, sleep idle_between_charts time
-                                                                               // and continue for another 10 charts.
+            fast_render_timeframe: 200, // ms - render continously until this time of continious
+                                        // rendering has been reached
+                                        // this setting is used to make it render e.g. 10
+                                        // charts at once, sleep idle_between_charts time
+                                        // and continue for another 10 charts.
 
-                       idle_between_loops: 500,        // ms - if all charts have been updated, wait this
-                                                                               // time before starting again.
+            idle_between_loops: 500,    // ms - if all charts have been updated, wait this
+                                        // time before starting again.
 
-                       idle_parallel_loops: 100,       // ms - the time between parallel refresher updates
+            idle_parallel_loops: 100,   // ms - the time between parallel refresher updates
 
-                       idle_lost_focus: 500,           // ms - when the window does not have focus, check
-                                                                               // if focus has been regained, every this time
+            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
-                                                                               // of time
+            global_pan_sync_time: 1000, // ms - when you pan or zoon a chart, the background
+                                        // autorefreshing of charts is paused for this amount
+                                        // of time
 
-                       sync_selection_delay: 1500,     // ms - when you pan or zoom a chart, wait this amount
-                                                                               // of time before setting up synchronized selections
-                                                                               // on hover.
-
-                       sync_selection: true,           // enable or disable selection sync
-
-                       pan_and_zoom_delay: 50,         // when panning or zooming, how ofter to update the chart
-
-                       sync_pan_and_zoom: true,        // enable or disable pan and zoom sync
-
-                       pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
-
-                       update_only_visible: true,      // enable or disable visibility management
-
-                       parallel_refresher: true,       // 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
-
-                       show_help: netdataShowHelp,     // when enabled the charts will show some help
-                       show_help_delay_show_ms: 500,
-                       show_help_delay_hide_ms: 0,
-
-                       eliminate_zero_dimensions: true, // do not show dimensions with just zeros
-
-                       stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
-                       stop_updates_while_resizing: 1000,      // ms - time to stop auto-refreshes while resizing the charts
-
-                       double_click_speed: 500,        // ms - time between clicks / taps to detect double click/tap
-
-                       smooth_plot: true,                      // enable smooth plot, where possible
-
-                       charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
-
-                       color_fill_opacity_line: 1.0,
-                       color_fill_opacity_area: 0.2,
-                       color_fill_opacity_stacked: 0.8,
-
-                       pan_and_zoom_factor: 0.25,              // the increment when panning and zooming with the toolbox
-                       pan_and_zoom_factor_multiplier_control: 2.0,
-                       pan_and_zoom_factor_multiplier_shift: 3.0,
-                       pan_and_zoom_factor_multiplier_alt: 4.0,
-
-                       setOptionCallback: function() { ; }
-               },
-
-               debug: {
-                       show_boxes:             false,
-                       main_loop:                      false,
-                       focus:                          false,
-                       visibility:             false,
-                       chart_data_url:         false,
-                       chart_errors:           false, // FIXME
-                       chart_timing:           false,
-                       chart_calls:            false,
-                       libraries:                      false,
-                       dygraph:                        false
-               }
-       };
-
-       NETDATA.statistics = {
-               refreshes_total: 0,
-               refreshes_active: 0,
-               refreshes_active_max: 0
-       };
-
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // local storage options
-
-       NETDATA.localStorage = {
-               default: {},
-               current: {},
-               callback: {} // only used for resetting back to defaults
-       };
-
-       NETDATA.localStorageGet = function(key, def, callback) {
-               var ret = def;
-
-               if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
-                       NETDATA.localStorage.default[key.toString()] = def;
-                       NETDATA.localStorage.callback[key.toString()] = callback;
-               }
-
-               if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
-                       try {
-                               // console.log('localStorage: loading "' + key.toString() + '"');
-                               ret = localStorage.getItem(key.toString());
-                               // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
-                               if(ret === null || ret === 'undefined') {
-                                       // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
-                                       localStorage.setItem(key.toString(), JSON.stringify(def));
-                                       ret = def;
-                               }
-                               else {
-                                       // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
-                                       ret = JSON.parse(ret);
-                                       // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
-                               }
-                       }
-                       catch(error) {
-                               console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
-                               ret = def;
-                       }
-               }
-
-               if(typeof ret === 'undefined' || ret === 'undefined') {
-                       console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
-                       ret = def;
-               }
-
-               NETDATA.localStorage.current[key.toString()] = ret;
-               return ret;
-       };
-
-       NETDATA.localStorageSet = function(key, value, callback) {
-               if(typeof value === 'undefined' || value === 'undefined') {
-                       console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
-               }
-
-               if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
-                       NETDATA.localStorage.default[key.toString()] = value;
-                       NETDATA.localStorage.current[key.toString()] = value;
-                       NETDATA.localStorage.callback[key.toString()] = callback;
-               }
-
-               if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
-                       // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
-                       try {
-                               localStorage.setItem(key.toString(), JSON.stringify(value));
-                       }
-                       catch(e) {
-                               console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
-                       }
-               }
-
-               NETDATA.localStorage.current[key.toString()] = value;
-               return value;
-       };
-
-       NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
-               for(var i in obj) {
-                       if(typeof obj[i] === 'object') {
-                               //console.log('object ' + prefix + '.' + i.toString());
-                               NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
-                               continue;
-                       }
-
-                       obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
-               }
-       };
-
-       NETDATA.setOption = function(key, value) {
-               if(key.toString() === 'setOptionCallback') {
-                       if(typeof NETDATA.options.current.setOptionCallback === 'function') {
-                               NETDATA.options.current[key.toString()] = value;
-                               NETDATA.options.current.setOptionCallback();
-                       }
-               }
-               else if(NETDATA.options.current[key.toString()] !== value) {
-                       var name = 'options.' + key.toString();
-
-                       if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
-                               console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
-
-                       //console.log(NETDATA.localStorage);
-                       //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
-                       //console.log(NETDATA.options);
-                       NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
-
-                       if(typeof NETDATA.options.current.setOptionCallback === 'function')
-                               NETDATA.options.current.setOptionCallback();
-               }
-
-               return true;
-       };
-
-       NETDATA.getOption = function(key) {
-               return NETDATA.options.current[key.toString()];
-       };
-
-       // read settings from local storage
-       NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
-
-       // always start with this option enabled.
-       NETDATA.setOption('stop_updates_when_focus_is_lost', true);
-
-       NETDATA.resetOptions = function() {
-               for(var i in NETDATA.localStorage.default) {
-                       var a = i.split('.');
-
-                       if(a[0] === 'options') {
-                               if(a[1] === 'setOptionCallback') continue;
-                               if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
-                               if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
-
-                               NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
-                       }
-                       else if(a[0] === 'chart_heights') {
-                               if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
-                                       NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
-                               }
-                       }
-               }
-       }
-
-       // ----------------------------------------------------------------------------------------------------------------
-
-       if(NETDATA.options.debug.main_loop === true)
-               console.log('welcome to NETDATA');
-
-       NETDATA.onresize = function() {
-               NETDATA.options.last_resized = new Date().getTime();
-               NETDATA.onscroll();
-       };
-
-       NETDATA.onscroll = function() {
-               // console.log('onscroll');
-
-               NETDATA.options.last_page_scroll = new Date().getTime();
-               if(NETDATA.options.targets === null) return;
-
-               // when the user scrolls he sees that we have
-               // hidden all the not-visible charts
-               // using this little function we try to switch
-               // the charts back to visible quickly
-               var targets = NETDATA.options.targets;
-               var len = targets.length;
-               while(len--) targets[len].isVisible();
-       };
-
-       window.onresize = NETDATA.onresize;
-       window.onscroll = NETDATA.onscroll;
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Error Handling
-
-       NETDATA.errorCodes = {
-               100: { message: "Cannot load chart library", alert: true },
-               101: { message: "Cannot load jQuery", alert: true },
-               402: { message: "Chart library not found", alert: false },
-               403: { message: "Chart library not enabled/is failed", alert: false },
-               404: { message: "Chart not found", alert: false },
-               405: { message: "Cannot download charts index from server", alert: true },
-               406: { message: "Invalid charts index downloaded from server", alert: true },
-               407: { message: "Cannot HELLO netdata server", alert: false },
-               408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
-               409: { message: "Cannot ACCESS netdata registry", alert: false },
-               410: { message: "Netdata registry ACCESS failed", alert: false },
-               411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
-               412: { message: "Netdata registry DELETE failed", alert: false },
-               413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
-               414: { message: "Netdata registry SWITCH failed", alert: false }
-       };
-       NETDATA.errorLast = {
-               code: 0,
-               message: "",
-               datetime: 0
-       };
-
-       NETDATA.error = function(code, msg) {
-               NETDATA.errorLast.code = code;
-               NETDATA.errorLast.message = msg;
-               NETDATA.errorLast.datetime = new Date().getTime();
-
-               console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
-
-               var ret = true;
-               if(typeof netdataErrorCallback === 'function') {
-                  ret = netdataErrorCallback('system', code, msg);
-               }
-
-               if(ret && NETDATA.errorCodes[code].alert)
-                       alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
-       };
-
-       NETDATA.errorReset = function() {
-               NETDATA.errorLast.code = 0;
-               NETDATA.errorLast.message = "You are doing fine!";
-               NETDATA.errorLast.datetime = 0;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Chart Registry
-
-       // When multiple charts need the same chart, we avoid downloading it
-       // multiple times (and having it in browser memory multiple time)
-       // by using this registry.
-
-       // Every time we download a chart definition, we save it here with .add()
-       // Then we try to get it back with .get(). If that fails, we download it.
-
-       NETDATA.chartRegistry = {
-               charts: {},
-
-               fixid: function(id) {
-                       return id.replace(/:/g, "_").replace(/\//g, "_");
-               },
-
-               add: function(host, id, data) {
-                       host = this.fixid(host);
-                       id   = this.fixid(id);
-
-                       if(typeof this.charts[host] === 'undefined')
-                               this.charts[host] = {};
-
-                       //console.log('added ' + host + '/' + id);
-                       this.charts[host][id] = data;
-               },
-
-               get: function(host, id) {
-                       host = this.fixid(host);
-                       id   = this.fixid(id);
-
-                       if(typeof this.charts[host] === 'undefined')
-                               return null;
-
-                       if(typeof this.charts[host][id] === 'undefined')
-                               return null;
-
-                       //console.log('cached ' + host + '/' + id);
-                       return this.charts[host][id];
-               },
-
-               downloadAll: function(host, callback) {
-                       while(host.slice(-1) === '/')
-                               host = host.substring(0, host.length - 1);
-
-                       var self = this;
-
-                       $.ajax({
-                               url: host + '/api/v1/charts',
-                               async: true,
-                               cache: false,
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function(data) {
-                               if(data !== null) {
-                                       var h = NETDATA.chartRegistry.fixid(host);
-                                       self.charts[h] = data.charts;
-                               }
-                               else NETDATA.error(406, host + '/api/v1/charts');
-
-                               if(typeof callback === 'function')
-                                       callback(data);
-                       })
-                       .fail(function() {
-                               NETDATA.error(405, host + '/api/v1/charts');
-
-                               if(typeof callback === 'function')
-                                       callback(null);
-                       });
-               }
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Global Pan and Zoom on charts
-
-       // Using this structure are synchronize all the charts, so that
-       // when you pan or zoom one, all others are automatically refreshed
-       // to the same timespan.
-
-       NETDATA.globalPanAndZoom = {
-               seq: 0,                                 // timestamp ms
-                                                               // 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
-
-               master: null,                   // the master chart (state), to which all others
-                                                               // are synchronized
-
-               force_before_ms: null,  // the timespan to sync all other charts
-               force_after_ms: null,
-
-               callback: null,
-
-               // set a new master
-               setMaster: function(state, after, before) {
-                       if(NETDATA.options.current.sync_pan_and_zoom === false)
-                               return;
-
-                       if(this.master !== null && this.master !== state)
-                               this.master.resetChart(true, true);
-
-                       var now = new Date().getTime();
-                       this.master = state;
-                       this.seq = now;
-                       this.force_after_ms = after;
-                       this.force_before_ms = before;
-                       NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
-
-                       if(typeof this.callback === 'function')
-                               this.callback(true, after, before);
-               },
-
-               // clear the master
-               clearMaster: function() {
-                       if(this.master !== null) {
-                               var st = this.master;
-                               this.master = null;
-                               st.resetChart();
-                       }
-
-                       this.master = null;
-                       this.seq = 0;
-                       this.force_after_ms = null;
-                       this.force_before_ms = null;
-                       NETDATA.options.auto_refresher_stop_until = 0;
-
-                       if(typeof this.callback === 'function')
-                               this.callback(false, 0, 0);
-               },
-
-               // is the given state the master of the global
-               // pan and zoom sync?
-               isMaster: function(state) {
-                       if(this.master === state) return true;
-                       return false;
-               },
-
-               // 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;
-               },
-
-               // check if a chart, other than the master
-               // needs to be refreshed, due to the global pan and zoom
-               shouldBeAutoRefreshed: function(state) {
-                       if(this.master === null || this.seq === 0)
-                               return false;
-
-                       //if(state.needsRecreation())
-                       //      return true;
-
-                       if(state.tm.pan_and_zoom_seq === this.seq)
-                               return false;
-
-                       return true;
-               }
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // dimensions selection
-
-       // FIXME
-       // move color assignment to dimensions, here
-
-       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.selected_count > parent.unselected_count)
-                       this.selected = true;
-               else
-                       this.selected = false;
-
-               this.setOptions(name_div, value_div, color);
-       };
-
-       dimensionStatus.prototype.invalidate = function() {
-               this.name_div = null;
-               this.value_div = null;
-               this.enabled = false;
-       };
-
-       dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
-               this.color = color;
-
-               if(this.name_div != name_div) {
-                       this.name_div = name_div;
-                       this.name_div.title = this.label;
-                       this.name_div.style.color = this.color;
-                       if(this.selected === false)
-                               this.name_div.className = 'netdata-legend-name not-selected';
-                       else
-                               this.name_div.className = 'netdata-legend-name selected';
-               }
-
-               if(this.value_div != value_div) {
-                       this.value_div = value_div;
-                       this.value_div.title = this.label;
-                       this.value_div.style.color = this.color;
-                       if(this.selected === false)
-                               this.value_div.className = 'netdata-legend-value not-selected';
-                       else
-                               this.value_div.className = 'netdata-legend-value selected';
-               }
-
-               this.enabled = true;
-               this.setHandler();
-       };
-
-       dimensionStatus.prototype.setHandler = function() {
-               if(this.enabled === false) return;
-
-               var ds = this;
-
-               // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
-               this.name_div.onclick = this.value_div.onclick = function(e) {
-                       e.preventDefault();
-                       if(ds.isSelected()) {
-                               // this is selected
-                               if(e.shiftKey === true || e.ctrlKey === true) {
-                                       // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
-                                       ds.unselect();
-
-                                       if(ds.parent.countSelected() === 0)
-                                               ds.parent.selectAll();
-                               }
-                               else {
-                                       // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
-                                       if(ds.parent.countSelected() === 1) {
-                                               ds.parent.selectAll();
-                                       }
-                                       else {
-                                               ds.parent.selectNone();
-                                               ds.select();
-                                       }
-                               }
-                       }
-                       else {
-                               // this is not selected
-                               if(e.shiftKey === true || e.ctrlKey === true) {
-                                       // control or shift key is pressed -> select this too
-                                       ds.select();
-                               }
-                               else {
-                                       // no key is pressed -> select only this
-                                       ds.parent.selectNone();
-                                       ds.select();
-                               }
-                       }
-
-                       ds.parent.state.redrawChart();
-               }
-       };
-
-       dimensionStatus.prototype.select = function() {
-               if(this.enabled === false) return;
-
-               this.name_div.className = 'netdata-legend-name selected';
-               this.value_div.className = 'netdata-legend-value selected';
-               this.selected = true;
-       };
-
-       dimensionStatus.prototype.unselect = function() {
-               if(this.enabled === false) return;
-
-               this.name_div.className = 'netdata-legend-name not-selected';
-               this.value_div.className = 'netdata-legend-value hidden';
-               this.selected = false;
-       };
-
-       dimensionStatus.prototype.isSelected = function() {
-               return(this.enabled === true && this.selected === true);
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-
-       dimensionsVisibility = function(state) {
-               this.state = state;
-               this.len = 0;
-               this.dimensions = {};
-               this.selected_count = 0;
-               this.unselected_count = 0;
-       };
-
-       dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
-               if(typeof this.dimensions[label] === 'undefined') {
-                       this.len++;
-                       this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
-               }
-               else
-                       this.dimensions[label].setOptions(name_div, value_div, color);
-
-               return this.dimensions[label];
-       };
-
-       dimensionsVisibility.prototype.dimensionGet = function(label) {
-               return this.dimensions[label];
-       };
-
-       dimensionsVisibility.prototype.invalidateAll = function() {
-               for(var d in this.dimensions)
-                       this.dimensions[d].invalidate();
-       };
-
-       dimensionsVisibility.prototype.selectAll = function() {
-               for(var d in this.dimensions)
-                       this.dimensions[d].select();
-       };
-
-       dimensionsVisibility.prototype.countSelected = function() {
-               var i = 0;
-               for(var d in this.dimensions)
-                       if(this.dimensions[d].isSelected()) i++;
-
-               return i;
-       };
-
-       dimensionsVisibility.prototype.selectNone = function() {
-               for(var d in this.dimensions)
-                       this.dimensions[d].unselect();
-       };
-
-       dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
-               var ret = new Array();
-               this.selected_count = 0;
-               this.unselected_count = 0;
-
-               for(var i = 0, len = array.length; i < len ; i++) {
-                       var ds = this.dimensions[array[i]];
-                       if(typeof ds === 'undefined') {
-                               // console.log(array[i] + ' is not found');
-                               ret.push(false);
-                               continue;
-                       }
-
-                       if(ds.isSelected()) {
-                               ret.push(true);
-                               this.selected_count++;
-                       }
-                       else {
-                               ret.push(false);
-                               this.unselected_count++;
-                       }
-               }
-
-               if(this.selected_count === 0 && this.unselected_count !== 0) {
-                       this.selectAll();
-                       return this.selected2BooleanArray(array);
-               }
-
-               return ret;
-       };
-
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // global selection sync
-
-       NETDATA.globalSelectionSync = {
-               state: null,
-               dont_sync_before: 0,
-               last_t: 0,
-               slaves: [],
-
-               stop: function() {
-                       if(this.state !== null)
-                               this.state.globalSelectionSyncStop();
-               },
-
-               delay: function() {
-                       if(this.state !== null) {
-                               this.state.globalSelectionSyncDelay();
-                       }
-               }
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Our state object, where all per-chart values are stored
-
-       chartState = function(element) {
-               var self = $(element);
-               this.element = element;
-
-               // IMPORTANT:
-               // all private functions should use 'that', instead of 'this'
-               var that = this;
-
-               /* error() - private
-                * show an error instead of the chart
-                */
-               var error = function(msg) {
-                       var ret = true;
-
-                       if(typeof netdataErrorCallback === 'function') {
-                               ret = netdataErrorCallback('chart', that.id, msg);
-                       }
-
-                       if(ret) {
-                               that.element.innerHTML = that.id + ': ' + msg;
-                               that.enabled = false;
-                               that.current = that.pan;
-                       }
-               };
-
-               // GUID - a unique identifier for the chart
-               this.uuid = NETDATA.guid();
-
-               // string - the name of chart
-               this.id = self.data('netdata');
-
-               // string - the key for localStorage settings
-               this.settings_id = self.data('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;
-
-               if(this.settings_id !== null) {
-                       this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
-                               // this is the callback that will be called
-                               // if and when the user resets all localStorage variables
-                               // to their defaults
-
-                               resizeChartToHeight(height);
-                       });
-               }
-
-               // 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;
-
-               // 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,
-                       nano: null,
-                       nano_options: null,
-                       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;      // the units of the chart dimensions
-
-               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?
-               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.auto = {
-                       name: 'auto',
-                       autorefresh: true,
-                       force_update_at: 0, // the timestamp to force the update at
-                       force_before_ms: null,
-                       force_after_ms: null
-               };
-               this.pan = {
-                       name: 'pan',
-                       autorefresh: false,
-                       force_update_at: 0, // the timestamp to force the update at
-                       force_before_ms: null,
-                       force_after_ms: null
-               };
-               this.zoom = {
-                       name: 'zoom',
-                       autorefresh: false,
-                       force_update_at: 0, // the timestamp to force the update at
-                       force_before_ms: null,
-                       force_after_ms: null
-               };
-
-               // this is a pointer to one of the sub-classes below
-               // 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];
-
-               // milliseconds - the time the last refresh took
-               this.refresh_dt_ms = 0;
-
-               // 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
-
-               if(refresh_dt_element_name !== null)
-                       this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
-               else
-                       this.refresh_dt_element = null;
-
-               this.dimensions_visibility = new dimensionsVisibility(this);
-
-               this._updating = false;
-
-               // ============================================================================================================
-               // PRIVATE FUNCTIONS
-
-               var createDOM = 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 = '';
-
-                       that.element.innerHTML = '';
-
-                       that.element_message = document.createElement('div');
-                       that.element_message.className = ' netdata-message 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);
-
-                       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.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';
-
-                               that.element_legend = null;
-                       }
-                       that.element_legend_childs.series = null;
-
-                       if(typeof(that.width) === 'string')
-                               $(that.element).css('width', that.width);
-                       else if(typeof(that.width) === 'number')
-                               $(that.element).css('width', that.width + 'px');
-
-                       if(typeof(that.library.aspect_ratio) === 'undefined') {
-                               if(typeof(that.height) === 'string')
-                                       $(that.element).css('height', that.height);
-                               else if(typeof(that.height) === 'number')
-                                       $(that.element).css('height', that.height + '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).css('height', (that.element.offsetWidth * 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 = new Date().getTime();
-
-                       showLoading();
-               };
-
-               /* init() private
-                * initialize state variables
-                * destroy all (possibly) created state elements
-                * create the basic DOM for a chart
-                */
-               var init = function() {
-                       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
-
-                       that.tm.last_initialized = new Date().getTime();
-                       createDOM();
-
-                       that.setMode('auto');
-               };
-
-               var maxMessageFontSize = function() {
-                       // normally we want a font size, as tall as the element
-                       var h = that.element_message.clientHeight;
-
-                       // but give it some air, 20% let's say, or 5 pixels min
-                       var lost = Math.max(h * 0.2, 5);
-                       h -= lost;
-
-                       // center the text, vertically
-                       var paddingTop = (lost - 5) / 2;
-
-                       // but check the width too
-                       // it should fit 10 characters in it
-                       var w = that.element_message.clientWidth / 10;
-                       if(h > w) {
-                               paddingTop += (h - w) / 2;
-                               h = w;
-                       }
-
-                       // and don't make it too huge
-                       // 5% of the screen size is good
-                       if(h > screen.height / 20) {
-                               paddingTop += (h - (screen.height / 20)) / 2;
-                               h = screen.height / 20;
-                       }
-
-                       // set it
-                       that.element_message.style.fontSize = h.toString() + 'px';
-                       that.element_message.style.paddingTop = paddingTop.toString() + 'px';
-               };
-
-               var showMessage = function(msg) {
-                       that.element_message.className = 'netdata-message';
-                       that.element_message.innerHTML = msg;
-                       that.element_message.style.fontSize = 'x-small';
-                       that.element_message.style.paddingTop = '0px';
-                       that.___messageHidden___ = undefined;
-               };
-
-               var showMessageIcon = function(icon) {
-                       that.element_message.innerHTML = icon;
-                       that.element_message.className = 'netdata-message icon';
-                       maxMessageFontSize();
-                       that.___messageHidden___ = undefined;
-               };
-
-               var hideMessage = function() {
-                       if(typeof that.___messageHidden___ === 'undefined') {
-                               that.___messageHidden___ = true;
-                               that.element_message.className = 'netdata-message hidden';
-                       }
-               };
-
-               var showRendering = function() {
-                       var icon;
-                       if(that.chart !== null) {
-                               if(that.chart.chart_type === 'line')
-                                       icon = '<i class="fa fa-line-chart"></i>';
-                               else
-                                       icon = '<i class="fa fa-area-chart"></i>';
-                       }
-                       else
-                               icon = '<i class="fa fa-area-chart"></i>';
-
-                       showMessageIcon(icon + ' netdata');
-               };
-
-               var showLoading = function() {
-                       if(that.chart_created === false) {
-                               showMessageIcon('<i class="fa fa-refresh"></i> netdata');
-                               return true;
-                       }
-                       return false;
-               };
-
-               var isHidden = function() {
-                       if(typeof that.___chartIsHidden___ !== 'undefined')
-                               return true;
-
-                       return false;
-               };
-
-               // hide the chart, when it is not visible - called from isVisible()
-               var hideChart = function() {
-                       // hide it, if it is not already hidden
-                       if(isHidden() === true) return;
-
-                       if(that.chart_created === true) {
-                               if(NETDATA.options.current.destroy_on_hide === true) {
-                                       // we should destroy it
-                                       init();
-                               }
-                               else {
-                                       showRendering();
-                                       that.element_chart.style.display = 'none';
-                                       if(that.element_legend !== null) that.element_legend.style.display = 'none';
-                                       that.tm.last_hidden = new Date().getTime();
-
-                                       // de-allocate data
-                                       // This works, but I not sure there are no corner cases somewhere
-                                       // so it is commented - if the user has memory issues he can
-                                       // set Destroy on Hide for all charts
-                                       // that.data = null;
-                               }
-                       }
-
-                       that.___chartIsHidden___ = true;
-               };
-
-               // unhide the chart, when it is visible - called from isVisible()
-               var unhideChart = function() {
-                       if(isHidden() === false) return;
-
-                       that.___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();
-                       }
-                       else {
-                               that.tm.last_unhidden = new Date().getTime();
-                               that.element_chart.style.display = '';
-                               if(that.element_legend !== null) that.element_legend.style.display = '';
-                               resizeChart();
-                               hideMessage();
-                       }
-               };
-
-               var canBeRendered = function() {
-                       if(isHidden() === true || that.isVisible(true) === false)
-                               return false;
-
-                       return true;
-               };
-
-               // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
-               var callChartLibraryUpdateSafely = function(data) {
-                       var status;
-
-                       if(canBeRendered() === false)
-                               return false;
-
-                       if(NETDATA.options.debug.chart_errors === true)
-                               status = that.library.update(that, data);
-                       else {
-                               try {
-                                       status = that.library.update(that, data);
-                               }
-                               catch(err) {
-                                       status = false;
-                               }
-                       }
-
-                       if(status === false) {
-                               error('chart failed to be updated as ' + that.library_name);
-                               return false;
-                       }
-
-                       return true;
-               };
-
-               // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
-               var callChartLibraryCreateSafely = function(data) {
-                       var status;
-
-                       if(canBeRendered() === false)
-                               return false;
-
-                       if(NETDATA.options.debug.chart_errors === true)
-                               status = that.library.create(that, data);
-                       else {
-                               try {
-                                       status = that.library.create(that, data);
-                               }
-                               catch(err) {
-                                       status = false;
-                               }
-                       }
-
-                       if(status === false) {
-                               error('chart failed to be created as ' + that.library_name);
-                               return false;
-                       }
-
-                       that.chart_created = true;
-                       that.updates_since_last_creation = 0;
-                       return true;
-               };
-
-               // ----------------------------------------------------------------------------------------------------------------
-               // Chart Resize
-
-               // resizeChart() - private
-               // 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.chart_created === false) return;
-
-                               if(that.needsRecreation()) {
-                                       init();
-                               }
-                               else if(typeof that.library.resize === 'function') {
-                                       that.library.resize(that);
-
-                                       if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
-                                               $(that.element_legend_childs.nano).nanoScroller();
-
-                                       maxMessageFontSize();
-                               }
-
-                               that.tm.last_resized = new Date().getTime();
-                       }
-               };
-
-               // this is the actual chart resize algorithm
-               // it will:
-               // - resize the entire container
-               // - update the internal states
-               // - resize the chart as the div changes height
-               // - update the scrollbar of the legend
-               var resizeChartToHeight = function(h) {
-                       // console.log(h);
-                       that.element.style.height = h;
-
-                       if(that.settings_id !== null)
-                               NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
-
-                       var now = new Date().getTime();
-                       NETDATA.options.last_page_scroll = now;
-                       NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
-
-                       // force a resize
-                       that.tm.last_resized = 0;
-                       resizeChart();
-               };
-
-               this.resizeHandler = function(e) {
-                       e.preventDefault();
-
-                       if(typeof this.event_resize === 'undefined'
-                               || this.event_resize.chart_original_w === 'undefined'
-                               || this.event_resize.chart_original_h === 'undefined')
-                               this.event_resize = {
-                                       chart_original_w: this.element.clientWidth,
-                                       chart_original_h: this.element.clientHeight,
-                                       last: 0
-                               };
-
-                       if(e.type === 'touchstart') {
-                               this.event_resize.mouse_start_x = e.touches.item(0).pageX;
-                               this.event_resize.mouse_start_y = e.touches.item(0).pageY;
-                       }
-                       else {
-                               this.event_resize.mouse_start_x = e.clientX;
-                               this.event_resize.mouse_start_y = e.clientY;
-                       }
-
-                       this.event_resize.chart_start_w = this.element.clientWidth;
-                       this.event_resize.chart_start_h = this.element.clientHeight;
-                       this.event_resize.chart_last_w = this.element.clientWidth;
-                       this.event_resize.chart_last_h = this.element.clientHeight;
-
-                       var now = new Date().getTime();
-                       if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
-                               // double click / double tap event
-
-                               // the optimal height of the chart
-                               // showing the entire legend
-                               var optimal = this.event_resize.chart_last_h
-                                               + this.element_legend_childs.content.scrollHeight
-                                               - this.element_legend_childs.content.clientHeight;
-
-                               // if we are not optimal, be optimal
-                               if(this.event_resize.chart_last_h != optimal)
-                                       resizeChartToHeight(optimal.toString() + 'px');
-
-                               // else if we do not have the original height
-                               // reset to the original height
-                               else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
-                                       resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
-                       }
-                       else {
-                               this.event_resize.last = now;
-
-                               // process movement event
-                               document.onmousemove =
-                               document.ontouchmove =
-                               this.element_legend_childs.resize_handler.onmousemove =
-                               this.element_legend_childs.resize_handler.ontouchmove =
-                                       function(e) {
-                                               var y = null;
-
-                                               switch(e.type) {
-                                                       case 'mousemove': y = e.clientY; break;
-                                                       case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
-                                               }
-
-                                               if(y !== null) {
-                                                       var     newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
-
-                                                       if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
-                                                               resizeChartToHeight(newH.toString() + 'px');
-                                                               that.event_resize.chart_last_h = newH;
-                                                       }
-                                               }
-                                       };
-
-                               // process end event
-                               document.onmouseup =
-                               document.ontouchend =
-                               this.element_legend_childs.resize_handler.onmouseup =
-                               this.element_legend_childs.resize_handler.ontouchend =
-                                       function(e) {
-                                               // remove all the hooks
-                                               document.onmouseup =
-                                               document.onmousemove =
-                                               document.ontouchmove =
-                                               document.ontouchend =
-                                               that.element_legend_childs.resize_handler.onmousemove =
-                                               that.element_legend_childs.resize_handler.ontouchmove =
-                                               that.element_legend_childs.resize_handler.onmouseout =
-                                               that.element_legend_childs.resize_handler.onmouseup =
-                                               that.element_legend_childs.resize_handler.ontouchend =
-                                                       null;
-
-                                               // allow auto-refreshes
-                                               NETDATA.options.auto_refresher_stop_until = 0;
-                                       };
-                       }
-               };
-
-
-               var noDataToShow = function() {
-                       showMessageIcon('<i class="fa fa-warning"></i> empty');
-                       that.legendUpdateDOM();
-                       that.tm.last_autorefreshed = new Date().getTime();
-                       // 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;
-               };
-
-               // ============================================================================================================
-               // PUBLIC FUNCTIONS
-
-               this.error = function(msg) {
-                       error(msg);
-               };
-
-               this.setMode = function(m) {
-                       if(this.current !== null && this.current.name === m) return;
-
-                       if(m === 'auto')
-                               this.current = this.auto;
-                       else if(m === 'pan')
-                               this.current = this.pan;
-                       else if(m === 'zoom')
-                               this.current = this.zoom;
-                       else
-                               this.current = this.auto;
-
-                       this.current.force_update_at = 0;
-                       this.current.force_before_ms = null;
-                       this.current.force_after_ms = null;
-
-                       this.tm.last_mode_switch = new Date().getTime();
-               };
-
-               // ----------------------------------------------------------------------------------------------------------------
-               // global selection sync
-
-               // prevent to global selection sync for some time
-               this.globalSelectionSyncDelay = function(ms) {
-                       if(NETDATA.options.current.sync_selection === false)
-                               return;
-
-                       if(typeof ms === 'number')
-                               NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
-                       else
-                               NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
-               };
-
-               // can we globally apply selection sync?
-               this.globalSelectionSyncAbility = function() {
-                       if(NETDATA.options.current.sync_selection === false)
-                               return false;
-
-                       if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
-                               return false;
-
-                       return true;
-               };
-
-               this.globalSelectionSyncIsMaster = function() {
-                       if(NETDATA.globalSelectionSync.state === this)
-                               return true;
-                       else
-                               return false;
-               };
-
-               // this chart is the master of the global selection sync
-               this.globalSelectionSyncBeMaster = function() {
-                       // am I the master?
-                       if(this.globalSelectionSyncIsMaster()) {
-                               if(this.debug === true)
-                                       this.log('sync: I am the master already.');
-
-                               return;
-                       }
-
-                       if(NETDATA.globalSelectionSync.state) {
-                               if(this.debug === true)
-                                       this.log('sync: I am not the sync master. Resetting global sync.');
-
-                               this.globalSelectionSyncStop();
-                       }
-
-                       // become the master
-                       if(this.debug === true)
-                               this.log('sync: becoming sync master.');
-
-                       this.selected = true;
-                       NETDATA.globalSelectionSync.state = this;
-
-                       // find the all slaves
-                       var targets = NETDATA.options.targets;
-                       var len = targets.length;
-                       while(len--) {
-                               st = targets[len];
-
-                               if(st === this) {
-                                       if(this.debug === true)
-                                               st.log('sync: not adding me to sync');
-                               }
-                               else if(st.globalSelectionSyncIsEligible()) {
-                                       if(this.debug === true)
-                                               st.log('sync: adding to sync as slave');
-
-                                       st.globalSelectionSyncBeSlave();
-                               }
-                       }
-
-                       // this.globalSelectionSyncDelay(100);
-               };
-
-               // can the chart participate to the global selection sync as a slave?
-               this.globalSelectionSyncIsEligible = function() {
-                       if(this.enabled === true
-                               && this.library !== null
-                               && typeof this.library.setSelection === 'function'
-                               && this.isVisible() === true
-                               && this.chart_created === true)
-                               return true;
-
-                       return false;
-               };
-
-               // this chart becomes a slave of the global selection sync
-               this.globalSelectionSyncBeSlave = function() {
-                       if(NETDATA.globalSelectionSync.state !== this)
-                               NETDATA.globalSelectionSync.slaves.push(this);
-               };
-
-               // sync all the visible charts to the given time
-               // this is to be called from the chart libraries
-               this.globalSelectionSync = function(t) {
-                       if(this.globalSelectionSyncAbility() === false) {
-                               if(this.debug === true)
-                                       this.log('sync: cannot sync (yet?).');
-
-                               return;
-                       }
-
-                       if(this.globalSelectionSyncIsMaster() === false) {
-                               if(this.debug === true)
-                                       this.log('sync: trying to be sync master.');
-
-                               this.globalSelectionSyncBeMaster();
-
-                               if(this.globalSelectionSyncAbility() === false) {
-                                       if(this.debug === true)
-                                               this.log('sync: cannot sync (yet?).');
-
-                                       return;
-                               }
-                       }
-
-                       NETDATA.globalSelectionSync.last_t = t;
-                       $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
-                               st.setSelection(t);
-                       });
-               };
-
-               // stop syncing all charts to the given time
-               this.globalSelectionSyncStop = function() {
-                       if(NETDATA.globalSelectionSync.slaves.length) {
-                               if(this.debug === true)
-                                       this.log('sync: cleaning up...');
-
-                               $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
-                                       if(st === that) {
-                                               if(that.debug === true)
-                                                       st.log('sync: not adding me to sync stop');
-                                       }
-                                       else {
-                                               if(that.debug === true)
-                                                       st.log('sync: removed slave from sync');
-
-                                               st.clearSelection();
-                                       }
-                               });
-
-                               NETDATA.globalSelectionSync.last_t = 0;
-                               NETDATA.globalSelectionSync.slaves = [];
-                               NETDATA.globalSelectionSync.state = null;
-                       }
-
-                       this.clearSelection();
-               };
-
-               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(this.selected === true && this.debug === true)
-                               this.log('selection set to ' + t.toString());
-
-                       return this.selected;
-               };
-
-               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(this.selected === false && this.debug === true)
-                                       this.log('selection cleared');
-
-                               this.legendReset();
-                       }
-
-                       return this.selected;
-               };
-
-               // 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;
-               };
-
-               this.calculateRowForTime = function(t) {
-                       if(this.timeIsVisible(t) === false) return -1;
-                       return Math.floor((t - this.data_after) / this.data_update_every);
-               };
-
-               // ----------------------------------------------------------------------------------------------------------------
-
-               // 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)
-                                       this.log('pauseChart()');
-
-                               this.paused = true;
-                       }
-               };
-
-               this.unpauseChart = function() {
-                       if(this.paused === true) {
-                               if(this.debug === true)
-                                       this.log('unpauseChart()');
-
-                               this.paused = false;
-                       }
-               };
-
-               this.resetChart = function(dont_clear_master, dont_update) {
-                       if(this.debug === true)
-                               this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
-
-                       if(typeof dont_clear_master === 'undefined')
-                               dont_clear_master = false;
-
-                       if(typeof dont_update === 'undefined')
-                               dont_update = false;
-
-                       if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
-                               if(this.debug === true)
-                                       this.log('resetChart() diverting to clearMaster().');
-                               // this will call us back with master === true
-                               NETDATA.globalPanAndZoom.clearMaster();
-                               return;
-                       }
-
-                       this.clearSelection();
-
-                       this.tm.pan_and_zoom_seq = 0;
-
-                       this.setMode('auto');
-                       this.current.force_update_at = 0;
-                       this.current.force_before_ms = null;
-                       this.current.force_after_ms = null;
-                       this.tm.last_autorefreshed = 0;
-                       this.paused = false;
-                       this.selected = false;
-                       this.enabled = true;
-                       // this.debug = false;
-
-                       // do not update the chart here
-                       // or the chart will flip-flop when it is the master
-                       // of a selection sync and another chart becomes
-                       // the new master
-
-                       if(dont_update !== true && this.isVisible() === true) {
-                               this.updateChart();
-                       }
-               };
-
-               this.updateChartPanOrZoom = function(after, before) {
-                       var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
-                       var ret = true;
-
-                       if(this.debug === true)
-                               this.log(logme);
-
-                       if(before < after) {
-                               if(this.debug === true)
-                                       this.log(logme + 'flipped parameters, rejecting it.');
-
-                               return false;
-                       }
-
-                       if(typeof this.fixed_min_duration === 'undefined')
-                               this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
-
-                       var min_duration = this.fixed_min_duration;
-                       var current_duration = Math.round(this.view_before - this.view_after);
-
-                       // round the numbers
-                       after = Math.round(after);
-                       before = Math.round(before);
-
-                       // align them to update_every
-                       // stretching them further away
-                       after -= after % this.data_update_every;
-                       before += this.data_update_every - (before % this.data_update_every);
-
-                       // the final wanted duration
-                       var wanted_duration = before - after;
-
-                       // to allow panning, accept just a point below our minimum
-                       if((current_duration - this.data_update_every) < min_duration)
-                               min_duration = current_duration - this.data_update_every;
-
-                       // we do it, but we adjust to minimum size and return false
-                       // when the wanted size is below the current and the minimum
-                       // and we zoom
-                       if(wanted_duration < current_duration && wanted_duration < min_duration) {
-                               if(this.debug === true)
-                                       this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
-
-                               min_duration = this.fixed_min_duration;
-
-                               var dt = (min_duration - wanted_duration) / 2;
-                               before += dt;
-                               after -= dt;
-                               wanted_duration = before - after;
-                               ret = false;
-                       }
-
-                       var tolerance = this.data_update_every * 2;
-                       var movement = Math.abs(before - this.view_before);
-
-                       if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
-                               if(this.debug === true)
-                                       this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
-                               return false;
-                       }
-
-                       if(this.current.name === 'auto') {
-                               this.log(logme + 'caller called me with mode: ' + this.current.name);
-                               this.setMode('pan');
-                       }
-
-                       if(this.debug === true)
-                               this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
-
-                       this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
-                       this.current.force_after_ms = after;
-                       this.current.force_before_ms = before;
-                       NETDATA.globalPanAndZoom.setMaster(this, after, before);
-                       return ret;
-               };
-
-               this.legendFormatValue = function(value) {
-                       if(value === null || value === 'undefined') return '-';
-                       if(typeof value !== 'number') return value;
-
-                       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();
-               };
-
-               this.legendSetLabelValue = function(label, value) {
-                       var series = this.element_legend_childs.series[label];
-                       if(typeof series === 'undefined') return;
-                       if(series.value === null && series.user === null) return;
-
-                       // if the value has not changed, skip DOM update
-                       //if(series.last === value) return;
-
-                       var s, r;
-                       if(typeof value === 'number') {
-                               var v = Math.abs(value);
-                               s = r = this.legendFormatValue(value);
-
-                               if(typeof series.last === 'number') {
-                                       if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                                       else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                                       else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                               }
-                               else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
-                               series.last = v;
-                       }
-                       else {
-                               s = r = value;
-                               series.last = value;
-                       }
-
-                       if(series.value !== null) series.value.innerHTML = s;
-                       if(series.user !== null) series.user.innerHTML = r;
-               };
-
-               this.legendSetDate = function(ms) {
-                       if(typeof ms !== 'number') {
-                               this.legendShowUndefined();
-                               return;
-                       }
-
-                       var d = new Date(ms);
-
-                       if(this.element_legend_childs.title_date)
-                               this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
-
-                       if(this.element_legend_childs.title_time)
-                               this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
-
-                       if(this.element_legend_childs.title_units)
-                               this.element_legend_childs.title_units.innerHTML = this.units;
-               };
-
-               this.legendShowUndefined = function() {
-                       if(this.element_legend_childs.title_date)
-                               this.element_legend_childs.title_date.innerHTML = '&nbsp;';
-
-                       if(this.element_legend_childs.title_time)
-                               this.element_legend_childs.title_time.innerHTML = this.chart.name;
-
-                       if(this.element_legend_childs.title_units)
-                               this.element_legend_childs.title_units.innerHTML = '&nbsp;';
-
-                       if(this.data && this.element_legend_childs.series !== null) {
-                               var labels = this.data.dimension_names;
-                               var i = labels.length;
-                               while(i--) {
-                                       var label = labels[i];
-
-                                       if(typeof label === 'undefined') continue;
-                                       if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
-                                       this.legendSetLabelValue(label, null);
-                               }
-                       }
-               };
-
-               this.legendShowLatestValues = function() {
-                       if(this.chart === null) return;
-                       if(this.selected) return;
-
-                       if(this.data === null || this.element_legend_childs.series === null) {
-                               this.legendShowUndefined();
-                               return;
-                       }
-
-                       var show_undefined = true;
-                       if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
-                               show_undefined = false;
-
-                       if(show_undefined) {
-                               this.legendShowUndefined();
-                               return;
-                       }
-
-                       this.legendSetDate(this.view_before);
-
-                       var labels = this.data.dimension_names;
-                       var i = labels.length;
-                       while(i--) {
-                               var label = labels[i];
-
-                               if(typeof label === 'undefined') continue;
-                               if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
-
-                               if(show_undefined)
-                                       this.legendSetLabelValue(label, null);
-                               else
-                                       this.legendSetLabelValue(label, this.data.view_latest_values[i]);
-                       }
-               };
-
-               this.legendReset = function() {
-                       this.legendShowLatestValues();
-               };
-
-               // this should be called just ONCE per dimension per chart
-               this._chartDimensionColor = function(label) {
-                       if(this.colors === null) this.chartColors();
-
-                       if(typeof this.colors_assigned[label] === 'undefined') {
-                               if(this.colors_available.length === 0) {
-                                       for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
-                                               this.colors_available.push(NETDATA.themes.current.colors[i]);
-                               }
-
-                               this.colors_assigned[label] = this.colors_available.shift();
-
-                               if(this.debug === true)
-                                       this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
-                       }
-                       else {
-                               if(this.debug === true)
-                                       this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
-                       }
-
-                       this.colors.push(this.colors_assigned[label]);
-                       return this.colors_assigned[label];
-               };
-
-               this.chartColors = function() {
-                       if(this.colors !== null) return this.colors;
-
-                       this.colors = new Array();
-                       this.colors_available = new Array();
-                       var i, len;
-
-                       var c = $(this.element).data('colors');
-                       // 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) {
-                                               for(i = 0, len = c.length; i < len ; i++) {
-                                                       added++;
-                                                       this.colors_available.push(c[i]);
-                                                       // this.log('adding color: ' + c[i]);
-                                               }
-                                       }
-                               }
-                       }
-
-                       // push all the standard colors too
-                       for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
-                               this.colors_available.push(NETDATA.themes.current.colors[i]);
-
-                       return this.colors;
-               };
-
-               this.legendUpdateDOM = function() {
-                       var needed = false;
-
-                       // check that the legend DOM is up to date for the downloaded dimensions
-                       if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
-                               // this.log('the legend does not have any series - requesting legend update');
-                               needed = true;
-                       }
-                       else if(this.data === null) {
-                               // this.log('the chart does not have any data - requesting legend update');
-                               needed = true;
-                       }
-                       else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
-                               needed = true;
-                       }
-                       else {
-                               var labels = this.data.dimension_names.toString();
-                               if(labels !== this.element_legend_childs.series.labels_key) {
-                                       needed = true;
-
-                                       if(this.debug === true)
-                                               this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
-                               }
-                       }
-
-                       if(needed === false) {
-                               // make sure colors available
-                               this.chartColors();
-
-                               // do we have to update the current values?
-                               // we do this, only when the visible chart is current
-                               if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
-                                       if(this.debug === true)
-                                               this.log('chart is in latest position... updating values on legend...');
-
-                                       //var labels = this.data.dimension_names;
-                                       //var i = labels.length;
-                                       //while(i--)
-                                       //      this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
-                               }
-                               return;
-                       }
-                       if(this.colors === null) {
-                               // this is the first time we update the chart
-                               // let's assign colors to all dimensions
-                               if(this.library.track_colors() === true)
-                                       for(var dim in this.chart.dimensions)
-                                               this._chartDimensionColor(this.chart.dimensions[dim].name);
-                       }
-                       // we will re-generate the colors for the chart
-                       // based on the selected dimensions
-                       this.colors = null;
-
-                       if(this.debug === true)
-                               this.log('updating Legend DOM');
-
-                       // mark all dimensions as invalid
-                       this.dimensions_visibility.invalidateAll();
-
-                       var genLabel = function(state, parent, dim, name, count) {
-                               var color = state._chartDimensionColor(name);
-
-                               var user_element = null;
-                               var user_id = self.data('show-value-of-' + dim + '-at') || null;
-                               if(user_id !== null) {
-                                       user_element = document.getElementById(user_id) || null;
-                                       if(user_element === null)
-                                               state.log('Cannot find element with id: ' + user_id);
-                               }
-
-                               state.element_legend_childs.series[name] = {
-                                       name: document.createElement('span'),
-                                       value: document.createElement('span'),
-                                       user: user_element,
-                                       last: null
-                               };
-
-                               var label = state.element_legend_childs.series[name];
-
-                               // create the dimension visibility tracking for this label
-                               state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
-
-                               var rgb = NETDATA.colorHex2Rgb(color);
-                               label.name.innerHTML = '<table class="netdata-legend-name-table-'
-                                       + state.chart.chart_type
-                                       + '" style="background-color: '
-                                       + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
-                                       + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
-
-                               var text = document.createTextNode(' ' + name);
-                               label.name.appendChild(text);
-
-                               if(count > 0)
-                                       parent.appendChild(document.createElement('br'));
-
-                               parent.appendChild(label.name);
-                               parent.appendChild(label.value);
-                       };
-
-                       var content = document.createElement('div');
-
-                       if(this.hasLegend()) {
-                               this.element_legend_childs = {
-                                       content: content,
-                                       resize_handler: document.createElement('div'),
-                                       toolbox: document.createElement('div'),
-                                       toolbox_left: document.createElement('div'),
-                                       toolbox_right: document.createElement('div'),
-                                       toolbox_reset: document.createElement('div'),
-                                       toolbox_zoomin: document.createElement('div'),
-                                       toolbox_zoomout: document.createElement('div'),
-                                       toolbox_volume: document.createElement('div'),
-                                       title_date: document.createElement('span'),
-                                       title_time: document.createElement('span'),
-                                       title_units: document.createElement('span'),
-                                       nano: document.createElement('div'),
-                                       nano_options: {
-                                               paneClass: 'netdata-legend-series-pane',
-                                               sliderClass: 'netdata-legend-series-slider',
-                                               contentClass: 'netdata-legend-series-content',
-                                               enabledClass: '__enabled',
-                                               flashedClass: '__flashed',
-                                               activeClass: '__active',
-                                               tabIndex: -1,
-                                               alwaysVisible: true,
-                                               sliderMinHeight: 10
-                                       },
-                                       series: {}
-                               };
-
-                               this.element_legend.innerHTML = '';
-
-                               if(this.library.toolboxPanAndZoom !== null) {
-
-                                       function get_pan_and_zoom_step(event) {
-                                               if (event.ctrlKey)
-                                                       return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
-
-                                               else if (event.shiftKey)
-                                                       return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
-
-                                               else if (event.altKey)
-                                                       return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
-
-                                               else
-                                                       return NETDATA.options.current.pan_and_zoom_factor;
-                                       }
-
-                                       this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
-                                       this.element.appendChild(this.element_legend_childs.toolbox);
-
-                                       this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
-                                       this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
-                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
-                                       this.element_legend_childs.toolbox_left.onclick = function(e) {
-                                               e.preventDefault();
-
-                                               var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
-                                               var before = that.view_before - step;
-                                               var after = that.view_after - step;
-                                               if(after >= that.netdata_first)
-                                                       that.library.toolboxPanAndZoom(that, after, before);
-                                       };
-                                       if(NETDATA.options.current.show_help === true)
-                                               $(this.element_legend_childs.toolbox_left).popover({
-                                               container: "body",
-                                               animation: false,
-                                               html: true,
-                                               trigger: 'hover',
-                                               placement: 'bottom',
-                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                                               title: 'Pan Left',
-                                               content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
-                                       });
-
-
-                                       this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
-                                       this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
-                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
-                                       this.element_legend_childs.toolbox_reset.onclick = function(e) {
-                                               e.preventDefault();
-                                               NETDATA.resetAllCharts(that);
-                                       };
-                                       if(NETDATA.options.current.show_help === true)
-                                               $(this.element_legend_childs.toolbox_reset).popover({
-                                               container: "body",
-                                               animation: false,
-                                               html: true,
-                                               trigger: 'hover',
-                                               placement: 'bottom',
-                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                                               title: 'Chart Reset',
-                                               content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
-                                       });
-                                       
-                                       this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
-                                       this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
-                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
-                                       this.element_legend_childs.toolbox_right.onclick = function(e) {
-                                               e.preventDefault();
-                                               var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
-                                               var before = that.view_before + step;
-                                               var after = that.view_after + step;
-                                               if(before <= that.netdata_last)
-                                                       that.library.toolboxPanAndZoom(that, after, before);
-                                       };
-                                       if(NETDATA.options.current.show_help === true)
-                                               $(this.element_legend_childs.toolbox_right).popover({
-                                               container: "body",
-                                               animation: false,
-                                               html: true,
-                                               trigger: 'hover',
-                                               placement: 'bottom',
-                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                                               title: 'Pan Right',
-                                               content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
-                                       });
-
-                                       
-                                       this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
-                                       this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
-                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
-                                       this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
-                                               e.preventDefault();
-                                               var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
-                                               var before = that.view_before - dt;
-                                               var after = that.view_after + dt;
-                                               that.library.toolboxPanAndZoom(that, after, before);
-                                       };
-                                       if(NETDATA.options.current.show_help === true)
-                                               $(this.element_legend_childs.toolbox_zoomin).popover({
-                                               container: "body",
-                                               animation: false,
-                                               html: true,
-                                               trigger: 'hover',
-                                               placement: 'bottom',
-                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                                               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.<br/><small>Help, can be disabled from the settings.</small>'
-                                       });
-                                       
-                                       this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
-                                       this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
-                                       this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
-                                       this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
-                                               e.preventDefault();
-                                               var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
-                                               var before = that.view_before + dt;
-                                               var after = that.view_after - dt;
-
-                                               that.library.toolboxPanAndZoom(that, after, before);
-                                       };
-                                       if(NETDATA.options.current.show_help === true)
-                                               $(this.element_legend_childs.toolbox_zoomout).popover({
-                                               container: "body",
-                                               animation: false,
-                                               html: true,
-                                               trigger: 'hover',
-                                               placement: 'bottom',
-                                               delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                                               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.<br/><small>Help, can be disabled from the settings.</small>'
-                                       });
-                                       
-                                       //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
-                                       //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
-                                       //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
-                                       //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
-                                       //this.element_legend_childs.toolbox_volume.onclick = function(e) {
-                                               //e.preventDefault();
-                                               //alert('clicked toolbox_volume on ' + that.id);
-                                       //}
-                               }
-                               else {
-                                       this.element_legend_childs.toolbox = null;
-                                       this.element_legend_childs.toolbox_left = null;
-                                       this.element_legend_childs.toolbox_reset = null;
-                                       this.element_legend_childs.toolbox_right = null;
-                                       this.element_legend_childs.toolbox_zoomin = null;
-                                       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 = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
-                               this.element.appendChild(this.element_legend_childs.resize_handler);
-                               if(NETDATA.options.current.show_help === true)
-                                       $(this.element_legend_childs.resize_handler).popover({
-                                       container: "body",
-                                       animation: false,
-                                       html: true,
-                                       trigger: 'hover',
-                                       placement: 'bottom',
-                                       delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
-                                       title: 'Chart Resize',
-                                       content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
-                               });
-
-                               // mousedown event
-                               this.element_legend_childs.resize_handler.onmousedown =
-                                       function(e) {
-                                               that.resizeHandler(e);
-                                       };
-
-                               // touchstart event
-                               this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
-                                       that.resizeHandler(e);
-                               }, false);
-
-                               this.element_legend_childs.title_date.className += " netdata-legend-title-date";
-                               this.element_legend.appendChild(this.element_legend_childs.title_date);
-
-                               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.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.element_legend.appendChild(document.createElement('br'));
-
-                               this.element_legend_childs.nano.className = 'netdata-legend-series';
-                               this.element_legend.appendChild(this.element_legend_childs.nano);
-
-                               content.className = 'netdata-legend-series-content';
-                               this.element_legend_childs.nano.appendChild(content);
-
-                               if(NETDATA.options.current.show_help === true)
-                                       $(content).popover({
-                                       container: "body",
-                                       animation: false,
-                                       html: true,
-                                       trigger: 'hover',
-                                       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.<br/><small>Help, can be disabled from the settings.</small>'
-                               });
-                       }
-                       else {
-                               this.element_legend_childs = {
-                                       content: content,
-                                       resize_handler: null,
-                                       toolbox: null,
-                                       toolbox_left: null,
-                                       toolbox_right: null,
-                                       toolbox_reset: null,
-                                       toolbox_zoomin: null,
-                                       toolbox_zoomout: null,
-                                       toolbox_volume: null,
-                                       title_date: null,
-                                       title_time: null,
-                                       title_units: null,
-                                       nano: null,
-                                       nano_options: null,
-                                       series: {}
-                               };
-                       }
-
-                       if(this.data) {
-                               this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
-                               if(this.debug === true)
-                                       this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
-
-                               for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
-                                       genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
-                               }
-                       }
-                       else {
-                               var tmp = new Array();
-                               for(var dim in this.chart.dimensions) {
-                                       tmp.push(this.chart.dimensions[dim].name);
-                                       genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
-                               }
-                               this.element_legend_childs.series.labels_key = tmp.toString();
-                               if(this.debug === true)
-                                       this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
-                       }
-
-                       // create a hidden div to be used for hidding
-                       // the original legend of the chart library
-                       var el = document.createElement('div');
-                       if(this.element_legend !== null)
-                               this.element_legend.appendChild(el);
-                       el.style.display = 'none';
-
-                       this.element_legend_childs.hidden = document.createElement('div');
-                       el.appendChild(this.element_legend_childs.hidden);
-
-                       if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
-                               $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
-
-                       this.legendShowLatestValues();
-               };
-
-               this.hasLegend = function() {
-                       if(typeof this.___hasLegendCache___ !== 'undefined')
-                               return this.___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;
-                       }
-
-                       this.___hasLegendCache___ = leg;
-                       return leg;
-               };
-
-               this.legendWidth = function() {
-                       return (this.hasLegend())?140:0;
-               };
-
-               this.legendHeight = function() {
-                       return $(this.element).height();
-               };
-
-               this.chartWidth = function() {
-                       return $(this.element).width() - this.legendWidth();
-               };
-
-               this.chartHeight = function() {
-                       return $(this.element).height();
-               };
-
-               this.chartPixelsPerPoint = function() {
-                       // force an options provided detail
-                       var px = this.pixels_per_point;
-
-                       if(this.library && px < this.library.pixels_per_point(this))
-                               px = this.library.pixels_per_point(this);
-
-                       if(px < NETDATA.options.current.pixels_per_point)
-                               px = NETDATA.options.current.pixels_per_point;
-
-                       return px;
-               };
-
-               this.needsRecreation = function() {
-                       return (
-                                       this.chart_created === true
-                                       && this.library
-                                       && this.library.autoresize() === false
-                                       && this.tm.last_resized < NETDATA.options.last_resized
-                               );
-               };
-
-               this.chartURL = function() {
-                       var after, before, points_multiplier = 1;
-                       if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
-                               this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
-
-                               after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
-                               before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
-                               this.view_after = after * 1000;
-                               this.view_before = before * 1000;
-
-                               this.requested_padding = null;
-                               points_multiplier = 1;
-                       }
-                       else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
-                               this.tm.pan_and_zoom_seq = 0;
-
-                               before = Math.round(this.current.force_before_ms / 1000);
-                               after  = Math.round(this.current.force_after_ms / 1000);
-                               this.view_after = after * 1000;
-                               this.view_before = before * 1000;
-
-                               if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
-                                       this.requested_padding = Math.round((before - after) / 2);
-                                       after -= this.requested_padding;
-                                       before += this.requested_padding;
-                                       this.requested_padding *= 1000;
-                                       points_multiplier = 2;
-                               }
-
-                               this.current.force_before_ms = null;
-                               this.current.force_after_ms = null;
-                       }
-                       else {
-                               this.tm.pan_and_zoom_seq = 0;
-
-                               before = this.before;
-                               after  = this.after;
-                               this.view_after = after * 1000;
-                               this.view_before = before * 1000;
-
-                               this.requested_padding = null;
-                               points_multiplier = 1;
-                       }
-
-                       this.requested_after = after * 1000;
-                       this.requested_before = before * 1000;
-
-                       this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
-
-                       // build the data URL
-                       this.data_url = this.host + this.chart.data_url;
-                       this.data_url += "&format="  + this.library.format();
-                       this.data_url += "&points="  + (this.data_points * points_multiplier).toString();
-                       this.data_url += "&group="   + this.method;
-                       this.data_url += "&options=" + this.library.options(this);
-                       this.data_url += '|jsonwrap';
-
-                       if(NETDATA.options.current.eliminate_zero_dimensions === true)
-                               this.data_url += '|nonzero';
-
-                       if(this.append_options !== null)
-                               this.data_url += '|' + this.append_options.toString();
-
-                       if(after)
-                               this.data_url += "&after="  + after.toString();
-
-                       if(before)
-                               this.data_url += "&before=" + before.toString();
-
-                       if(this.dimensions)
-                               this.data_url += "&dimensions=" + this.dimensions;
-
-                       if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
-                               this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
-               };
-
-               this.redrawChart = function() {
-                       if(this.data !== null)
-                               this.updateChartWithData(this.data);
-               };
-
-               this.updateChartWithData = function(data) {
-                       if(this.debug === true)
-                               this.log('updateChartWithData() called.');
-
-                       // this may force the chart to be re-created
-                       resizeChart();
-
-                       this.data = data;
-                       this.updates_counter++;
-                       this.updates_since_last_unhide++;
-                       this.updates_since_last_creation++;
-
-                       var started = new Date().getTime();
-
-                       // if the result is JSON, find the latest update-every
-                       this.data_update_every = data.view_update_every * 1000;
-                       this.data_after = data.after * 1000;
-                       this.data_before = data.before * 1000;
-                       this.netdata_first = data.first_entry * 1000;
-                       this.netdata_last = data.last_entry * 1000;
-                       this.data_points = data.points;
-                       data.state = this;
-
-                       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);
-                                       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);
-                                       this.view_before = this.data_before;
-                               }
-                       }
-                       else {
-                               this.view_after = this.data_after;
-                               this.view_before = this.data_before;
-                       }
-
-                       if(this.debug === true) {
-                               this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
-
-                               if(this.current.force_after_ms)
-                                       this.log('STATUS: forced    : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
-                               else
-                                       this.log('STATUS: forced    : unset');
-
-                               this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
-                               this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
-                               this.log('STATUS: rendered  : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
-                               this.log('STATUS: points    : ' + (this.data_points).toString());
-                       }
-
-                       if(this.data_points === 0) {
-                               noDataToShow();
-                               return;
-                       }
-
-                       if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
-                               if(this.debug === true)
-                                       this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
-
-                               this.chart_created = false;
-                       }
-
-                       // check and update the legend
-                       this.legendUpdateDOM();
-
-                       if(this.chart_created === true
-                               && typeof this.library.update === 'function') {
-
-                               if(this.debug === true)
-                                       this.log('updating chart...');
-
-                               if(callChartLibraryUpdateSafely(data) === false)
-                                       return;
-                       }
-                       else {
-                               if(this.debug === true)
-                                       this.log('creating chart...');
-
-                               if(callChartLibraryCreateSafely(data) === false)
-                                       return;
-                       }
-                       hideMessage();
-                       this.legendShowLatestValues();
-                       if(this.selected === true)
-                               NETDATA.globalSelectionSync.stop();
-
-                       // update the performance counters
-                       var now = new Date().getTime();
-                       this.tm.last_updated = now;
-
-                       // don't update last_autorefreshed if this chart is
-                       // forced to be updated with global PanAndZoom
-                       if(NETDATA.globalPanAndZoom.isActive())
-                               this.tm.last_autorefreshed = 0;
-                       else {
-                               if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
-                                       this.tm.last_autorefreshed = now - (now % this.data_update_every);
-                               else
-                                       this.tm.last_autorefreshed = now;
-                       }
-
-                       this.refresh_dt_ms = now - started;
-                       NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
-
-                       if(this.refresh_dt_element !== null)
-                               this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
-               };
-
-               this.updateChart = function(callback) {
-                       if(this.debug === true)
-                               this.log('updateChart() called.');
-
-                       if(this._updating === true) {
-                               if(this.debug === true)
-                                       this.log('I am already updating...');
-
-                               if(typeof callback === 'function') callback();
-                               return false;
-                       }
-
-                       // due to late initialization of charts and libraries
-                       // we need to check this too
-                       if(this.enabled === false) {
-                               if(this.debug === true)
-                                       this.log('I am not enabled');
-
-                               if(typeof callback === 'function') callback();
-                               return false;
-                       }
-
-                       if(canBeRendered() === false) {
-                               if(typeof callback === 'function') callback();
-                               return false;
-                       }
-
-                       if(this.chart === null) {
-                               this.getChart(function() { that.updateChart(callback); });
-                               return false;
-                       }
-
-                       if(this.library.initialized === false) {
-                               if(this.library.enabled === true) {
-                                       this.library.initialize(function() { that.updateChart(callback); });
-                                       return false;
-                               }
-                               else {
-                                       error('chart library "' + this.library_name + '" is not available.');
-                                       if(typeof callback === 'function') callback();
-                                       return false;
-                               }
-                       }
-
-                       this.clearSelection();
-                       this.chartURL();
-
-                       if(this.debug === true)
-                               this.log('updating from ' + this.data_url);
-
-                       NETDATA.statistics.refreshes_total++;
-                       NETDATA.statistics.refreshes_active++;
-
-                       if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
-                               NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
-
-                       this._updating = true;
-
-                       this.xhr = $.ajax( {
-                               url: this.data_url,
-                               cache: false,
-                               async: true,
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .success(function(data) {
-                               if(that.debug === true)
-                                       that.log('data received. updating chart.');
-
-                               that.updateChartWithData(data);
-                       })
-                       .fail(function() {
-                               error('data download failed for url: ' + that.data_url);
-                       })
-                       .always(function() {
-                               NETDATA.statistics.refreshes_active--;
-                               that._updating = false;
-                               if(typeof callback === 'function') callback();
-                       });
-
-                       return true;
-               };
-
-               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___;
-
-                       this.tm.last_visible_check = new Date().getTime();
-
-                       var wh = window.innerHeight;
-                       var x = this.element.getBoundingClientRect();
-                       var ret = 0;
-                       var tolerance = 0;
-
-                       if(x.width === 0 || x.height === 0) {
-                               hideChart();
-                               this.___isVisible___ = false;
-                               return this.___isVisible___;
-                       }
-
-                       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;
-                       }
-
-                       if(ret > tolerance) {
-                               // the chart is too far
-
-                               hideChart();
-                               this.___isVisible___ = false;
-                               return this.___isVisible___;
-                       }
-                       else {
-                               // the chart is inside or very close
-
-                               unhideChart();
-                               this.___isVisible___ = true;
-                               return this.___isVisible___;
-                       }
-               };
-
-               this.isAutoRefreshable = function() {
-                       return (this.current.autorefresh);
-               };
-
-               this.canBeAutoRefreshed = function() {
-                       var now = new Date().getTime();
-
-                       if(this.running === true) {
-                               if(this.debug === true)
-                                       this.log('I am already running');
-
-                               return false;
-                       }
-
-                       if(this.enabled === false) {
-                               if(this.debug === true)
-                                       this.log('I am not enabled');
-
-                               return false;
-                       }
-
-                       if(this.library === null || this.library.enabled === false) {
-                               error('charting library "' + this.library_name + '" is not available');
-                               if(this.debug === true)
-                                       this.log('My chart library ' + this.library_name + ' is not available');
-
-                               return false;
-                       }
-
-                       if(this.isVisible() === false) {
-                               if(NETDATA.options.debug.visibility === true || this.debug === true)
-                                       this.log('I am not visible');
-
-                               return false;
-                       }
-
-                       if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
-                               if(this.debug === true)
-                                       this.log('timed force update detected - allowing this update');
-
-                               this.current.force_update_at = 0;
-                               return true;
-                       }
-
-                       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');
-
-                                       return false;
-                               }
-
-                               if(this.needsRecreation() === true) {
-                                       if(this.debug === true)
-                                               this.log('canBeAutoRefreshed(): needs re-creation.');
-
-                                       return true;
-                               }
-
-                               // options valid only for autoRefresh()
-                               if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
-                                       if(NETDATA.globalPanAndZoom.isActive()) {
-                                               if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
-                                                       if(this.debug === true)
-                                                               this.log('canBeAutoRefreshed(): global panning: I need an update.');
-
-                                                       return true;
-                                               }
-                                               else {
-                                                       if(this.debug === true)
-                                                               this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
-
-                                                       return false;
-                                               }
-                                       }
-
-                                       if(this.selected === true) {
-                                               if(this.debug === true)
-                                                       this.log('canBeAutoRefreshed(): I have a selection in place.');
-
-                                               return false;
-                                       }
-
-                                       if(this.paused === true) {
-                                               if(this.debug === true)
-                                                       this.log('canBeAutoRefreshed(): I am paused.');
-
-                                               return false;
-                                       }
-
-                                       if(now - this.tm.last_autorefreshed >= this.data_update_every) {
-                                               if(this.debug === true)
-                                                       this.log('canBeAutoRefreshed(): It is time to update me.');
-
-                                               return true;
-                                       }
-                               }
-                       }
-
-                       return false;
-               };
-
-               this.autoRefresh = function(callback) {
-                       if(this.canBeAutoRefreshed() === true && this.running === false) {
-                               var state = this;
-
-                               state.running = true;
-                               state.updateChart(function() {
-                                       state.running = false;
-
-                                       if(typeof callback !== 'undefined')
-                                               callback();
-                               });
-                       }
-                       else {
-                               if(typeof callback !== 'undefined')
-                                       callback();
-                       }
-               };
-
-               this._defaultsFromDownloadedChart = function(chart) {
-                       this.chart = chart;
-                       this.chart_url = chart.url;
-                       this.data_update_every = chart.update_every * 1000;
-                       this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
-                       this.tm.last_info_downloaded = new Date().getTime();
-
-                       if(this.title === null)
-                               this.title = chart.title;
-
-                       if(this.units === null)
-                               this.units = chart.units;
-               };
-
-               // fetch the chart description from the netdata server
-               this.getChart = function(callback) {
-                       this.chart = NETDATA.chartRegistry.get(this.host, this.id);
-                       if(this.chart) {
-                               this._defaultsFromDownloadedChart(this.chart);
-                               if(typeof callback === 'function') callback();
-                       }
-                       else {
-                               this.chart_url = "/api/v1/chart?chart=" + this.id;
-
-                               if(this.debug === true)
-                                       this.log('downloading ' + this.chart_url);
-
-                               $.ajax( {
-                                       url:  this.host + this.chart_url,
-                                       cache: false,
-                                       async: true,
-                                       xhrFields: { withCredentials: true } // required for the cookie
-                               })
-                               .done(function(chart) {
-                                       chart.url = that.chart_url;
-                                       that._defaultsFromDownloadedChart(chart);
-                                       NETDATA.chartRegistry.add(that.host, that.id, chart);
-                               })
-                               .fail(function() {
-                                       NETDATA.error(404, that.chart_url);
-                                       error('chart not found on url "' + that.chart_url + '"');
-                               })
-                               .always(function() {
-                                       if(typeof callback === 'function') callback();
-                               });
-                       }
-               };
-
-               // ============================================================================================================
-               // INITIALIZATION
-
-               init();
-       };
-
-       NETDATA.resetAllCharts = function(state) {
-               // first clear the global selection sync
-               // to make sure no chart is in selected state
-               state.globalSelectionSyncStop();
-
-               // there are 2 possibilities here
-               // a. state is the global Pan and Zoom master
-               // b. state is not the global Pan and Zoom master
-               var master = true;
-               if(NETDATA.globalPanAndZoom.isMaster(state) === false)
-                       master = false;
-
-               // clear the global Pan and Zoom
-               // this will also refresh the master
-               // and unblock any charts currently mirroring the master
-               NETDATA.globalPanAndZoom.clearMaster();
-
-               // if we were not the master, reset our status too
-               // this is required because most probably the mouse
-               // is over this chart, blocking it from auto-refreshing
-               if(master === false && (state.paused === true || state.selected === true))
-                       state.resetChart();
-       };
-
-       // get or create a chart state, given a DOM element
-       NETDATA.chartState = function(element) {
-               var state = $(element).data('netdata-state-object') || null;
-               if(state === null) {
-                       state = new chartState(element);
-                       $(element).data('netdata-state-object', state);
-               }
-               return state;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Library functions
-
-       // Load a script without jquery
-       // This is used to load jquery - after it is loaded, we use jquery
-       NETDATA._loadjQuery = function(callback) {
-               if(typeof jQuery === 'undefined') {
-                       if(NETDATA.options.debug.main_loop === true)
-                               console.log('loading ' + NETDATA.jQuery);
-
-                       var script = document.createElement('script');
-                       script.type = 'text/javascript';
-                       script.async = true;
-                       script.src = NETDATA.jQuery;
-
-                       // script.onabort = onError;
-                       script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
-                       if(typeof callback === "function")
-                               script.onload = callback;
-
-                       var s = document.getElementsByTagName('script')[0];
-                       s.parentNode.insertBefore(script, s);
-               }
-               else if(typeof callback === "function")
-                       callback();
-       };
-
-       NETDATA._loadCSS = function(filename) {
-               // don't use jQuery here
-               // styles are loaded before jQuery
-               // to eliminate showing an unstyled page to the user
-
-               var fileref = document.createElement("link");
-               fileref.setAttribute("rel", "stylesheet");
-               fileref.setAttribute("type", "text/css");
-               fileref.setAttribute("href", filename);
-
-               if (typeof fileref !== 'undefined')
-                       document.getElementsByTagName("head")[0].appendChild(fileref);
-       };
-
-       NETDATA.colorHex2Rgb = function(hex) {
-               // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
-               var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
-                       hex = hex.replace(shorthandRegex, function(m, r, g, b) {
-                       return r + r + g + g + b + b;
-               });
-
-               var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
-               return result ? {
-                       r: parseInt(result[1], 16),
-                       g: parseInt(result[2], 16),
-                       b: parseInt(result[3], 16)
-               } : null;
-       };
-
-       NETDATA.colorLuminance = function(hex, lum) {
-               // validate hex string
-               hex = String(hex).replace(/[^0-9a-f]/gi, '');
-               if (hex.length < 6)
-                       hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
-
-               lum = lum || 0;
-
-               // convert to decimal and change luminosity
-               var rgb = "#", c, i;
-               for (i = 0; i < 3; i++) {
-                       c = parseInt(hex.substr(i*2,2), 16);
-                       c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
-                       rgb += ("00"+c).substr(c.length);
-               }
-
-               return rgb;
-       };
-
-       NETDATA.guid = function() {
-               function s4() {
-                       return Math.floor((1 + Math.random()) * 0x10000)
-                                       .toString(16)
-                                       .substring(1);
-                       }
-
-                       return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
-       };
-
-       NETDATA.zeropad = function(x) {
-               if(x > -10 && x < 10) return '0' + x.toString();
-               else return x.toString();
-       };
-
-       // user function to signal us the DOM has been
-       // updated.
-       NETDATA.updatedDom = function() {
-               NETDATA.options.updated_dom = true;
-       };
-
-       NETDATA.ready = function(callback) {
-               NETDATA.options.pauseCallback = callback;
-       };
-
-       NETDATA.pause = function(callback) {
-               if(NETDATA.options.pause === true)
-                       callback();
-               else
-                       NETDATA.options.pauseCallback = callback;
-       };
-
-       NETDATA.unpause = function() {
-               NETDATA.options.pauseCallback = null;
-               NETDATA.options.updated_dom = true;
-               NETDATA.options.pause = false;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-
-       // this is purely sequencial charts refresher
-       // it is meant to be autonomous
-       NETDATA.chartRefresherNoParallel = function(index) {
-               if(NETDATA.options.debug.mail_loop === true)
-                       console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
-
-               if(NETDATA.options.updated_dom === true) {
-                       // the dom has been updated
-                       // get the dom parts again
-                       NETDATA.parseDom(NETDATA.chartRefresher);
-                       return;
-               }
-               if(index >= NETDATA.options.targets.length) {
-                       if(NETDATA.options.debug.main_loop === true)
-                               console.log('waiting to restart main loop...');
-
-                       NETDATA.options.auto_refresher_fast_weight = 0;
-
-                       setTimeout(function() {
-                               NETDATA.chartRefresher();
-                       }, NETDATA.options.current.idle_between_loops);
-               }
-               else {
-                       var state = NETDATA.options.targets[index];
-
-                       if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
-                               if(NETDATA.options.debug.main_loop === true)
-                                       console.log('fast rendering...');
-
-                               state.autoRefresh(function() {
-                                       NETDATA.chartRefresherNoParallel(++index);
-                               });
-                       }
-                       else {
-                               if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
-                               NETDATA.options.auto_refresher_fast_weight = 0;
-
-                               setTimeout(function() {
-                                       state.autoRefresh(function() {
-                                               NETDATA.chartRefresherNoParallel(++index);
-                                       });
-                               }, NETDATA.options.current.idle_between_charts);
-                       }
-               }
-       };
-
-       // 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() {
-               if(NETDATA.options.pause === true) {
-                       // console.log('auto-refresher is paused');
-                       setTimeout(NETDATA.chartRefresher,
-                               NETDATA.chartRefresherWaitTime());
-                       return;
-               }
-
-               if(typeof NETDATA.options.pauseCallback === 'function') {
-                       // console.log('auto-refresher is calling pauseCallback');
-                       NETDATA.options.pause = true;
-                       NETDATA.options.pauseCallback();
-                       NETDATA.chartRefresher();
-                       return;
-               }
-
-               if(NETDATA.options.current.parallel_refresher === false) {
-                       NETDATA.chartRefresherNoParallel(0);
-                       return;
-               }
-
-               if(NETDATA.options.updated_dom === true) {
-                       // the dom has been updated
-                       // get the dom parts again
-                       NETDATA.parseDom(NETDATA.chartRefresher);
-                       return;
-               }
-
-               var parallel = new Array();
-               var targets = NETDATA.options.targets;
-               var len = targets.length;
-               var state;
-               while(len--) {
-                       state = targets[len];
-                       if(state.isVisible() === false || state.running === true)
-                               continue;
-
-                       if(state.library.initialized === false) {
-                               if(state.library.enabled === true) {
-                                       state.library.initialize(NETDATA.chartRefresher);
-                                       return;
-                               }
-                               else {
-                                       state.error('chart library "' + state.library_name + '" is not enabled.');
-                               }
-                       }
-
-                       parallel.unshift(state);
-               }
-
-               if(parallel.length > 0) {
-                       // this will execute the jobs in parallel
-                       $(parallel).each(function() {
-                               this.autoRefresh();
-                       })
-               }
-
-               // run the next refresh iteration
-               setTimeout(NETDATA.chartRefresher,
-                       NETDATA.chartRefresherWaitTime());
-       };
-
-       NETDATA.parseDom = function(callback) {
-               NETDATA.options.last_page_scroll = new Date().getTime();
-               NETDATA.options.updated_dom = false;
-
-               var targets = $('div[data-netdata]'); //.filter(':visible');
-
-               if(NETDATA.options.debug.main_loop === true)
-                       console.log('DOM updated - there are ' + targets.length + ' charts on page.');
-
-               NETDATA.options.targets = new Array();
-               var len = targets.length;
-               while(len--) {
-                       // the initialization will take care of sizing
-                       // and the "loading..." message
-                       NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
-               }
-
-               if(typeof callback === 'function') callback();
-       };
-
-       // this is the main function - where everything starts
-       NETDATA.start = function() {
-               // this should be called only once
-
-               NETDATA.options.page_is_visible = true;
-
-               $(window).blur(function() {
-                       if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
-                               NETDATA.options.page_is_visible = false;
-                               if(NETDATA.options.debug.focus === true)
-                                       console.log('Lost Focus!');
-                       }
-               });
-
-               $(window).focus(function() {
-                       if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
-                               NETDATA.options.page_is_visible = true;
-                               if(NETDATA.options.debug.focus === true)
-                                       console.log('Focus restored!');
-                       }
-               });
-
-               if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
-                       if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
-                               NETDATA.options.page_is_visible = false;
-                               if(NETDATA.options.debug.focus === true)
-                                       console.log('Document has no focus!');
-                       }
-               }
-
-               // bootstrap tab switching
-               $('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);
-
-               // bootstrap collapse switching
-               $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
-               $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
-
-               NETDATA.parseDom(NETDATA.chartRefresher);
-
-               // Registry initialization
-               setTimeout(NETDATA.registry.init, 1000);
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // peity
-
-       NETDATA.peityInitialize = function(callback) {
-               if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
-                       $.ajax({
-                               url: NETDATA.peity_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function() {
-                               NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
-                       })
-                       .fail(function() {
-                               NETDATA.chartLibraries.peity.enabled = false;
-                               NETDATA.error(100, NETDATA.peity_js);
-                       })
-                       .always(function() {
-                               if(typeof callback === "function")
-                                       callback();
-                       });
-               }
-               else {
-                       NETDATA.chartLibraries.peity.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       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.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_instance).peity('line', state.peity_options);
-               return true;
-       };
-
-       NETDATA.peityChartCreate = function(state, data) {
-               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,
-                       width: state.chartWidth(),
-                       height: state.chartHeight(),
-                       fill: NETDATA.themes.current.foreground
-               };
-
-               NETDATA.peityChartUpdate(state, data);
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // sparkline
-
-       NETDATA.sparklineInitialize = function(callback) {
-               if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
-                       $.ajax({
-                               url: NETDATA.sparkline_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function() {
-                               NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
-                       })
-                       .fail(function() {
-                               NETDATA.chartLibraries.sparkline.enabled = false;
-                               NETDATA.error(100, NETDATA.sparkline_js);
-                       })
-                       .always(function() {
-                               if(typeof callback === "function")
-                                       callback();
-                       });
-               }
-               else {
-                       NETDATA.chartLibraries.sparkline.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.sparklineChartUpdate = function(state, data) {
-               state.sparkline_options.width = state.chartWidth();
-               state.sparkline_options.height = state.chartHeight();
-
-               $(state.element_chart).sparkline(data.result, state.sparkline_options);
-               return true;
-       };
-
-       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;
-
-               if(spotColor === 'disable') spotColor='';
-               if(minSpotColor === 'disable') minSpotColor='';
-               if(maxSpotColor === 'disable') maxSpotColor='';
-
-               state.sparkline_options = {
-                       type: type,
-                       lineColor: lineColor,
-                       fillColor: fillColor,
-                       chartRangeMin: chartRangeMin,
-                       chartRangeMax: chartRangeMax,
-                       composite: composite,
-                       enableTagOptions: enableTagOptions,
-                       tagOptionPrefix: tagOptionPrefix,
-                       tagValuesAttribute: tagValuesAttribute,
-                       disableHiddenCheck: disableHiddenCheck,
-                       defaultPixelsPerValue: defaultPixelsPerValue,
-                       spotColor: spotColor,
-                       minSpotColor: minSpotColor,
-                       maxSpotColor: maxSpotColor,
-                       spotRadius: spotRadius,
-                       valueSpots: valueSpots,
-                       highlightSpotColor: highlightSpotColor,
-                       highlightLineColor: highlightLineColor,
-                       lineWidth: lineWidth,
-                       normalRangeMin: normalRangeMin,
-                       normalRangeMax: normalRangeMax,
-                       drawNormalOnTop: drawNormalOnTop,
-                       xvalues: xvalues,
-                       chartRangeClip: chartRangeClip,
-                       chartRangeMinX: chartRangeMinX,
-                       chartRangeMaxX: chartRangeMaxX,
-                       disableInteraction: disableInteraction,
-                       disableTooltips: disableTooltips,
-                       disableHighlight: disableHighlight,
-                       highlightLighten: highlightLighten,
-                       highlightColor: highlightColor,
-                       tooltipContainer: tooltipContainer,
-                       tooltipClassname: tooltipClassname,
-                       tooltipChartTitle: state.title,
-                       tooltipFormat: tooltipFormat,
-                       tooltipPrefix: tooltipPrefix,
-                       tooltipSuffix: tooltipSuffix,
-                       tooltipSkipNull: tooltipSkipNull,
-                       tooltipValueLookups: tooltipValueLookups,
-                       tooltipFormatFieldlist: tooltipFormatFieldlist,
-                       tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
-                       numberFormatter: numberFormatter,
-                       numberDigitGroupSep: numberDigitGroupSep,
-                       numberDecimalMark: numberDecimalMark,
-                       numberDigitGroupCount: numberDigitGroupCount,
-                       animatedZooms: animatedZooms,
-                       width: state.chartWidth(),
-                       height: state.chartHeight()
-               };
-
-               $(state.element_chart).sparkline(data.result, state.sparkline_options);
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // dygraph
-
-       NETDATA.dygraph = {
-               smooth: false
-       };
-
-       NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
-               if(after < state.netdata_first)
-                       after = state.netdata_first;
-
-               if(before > state.netdata_last)
-                       before = state.netdata_last;
-
-               state.setMode('zoom');
-               state.globalSelectionSyncStop();
-               state.globalSelectionSyncDelay();
-               state.dygraph_user_action = true;
-               state.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') {
-                       var r = state.calculateRowForTime(t);
-                       if(r !== -1)
-                               state.dygraph_instance.setSelection(r);
-                       else {
-                               state.dygraph_instance.clearSelection();
-                               state.legendShowUndefined();
-                       }
-               }
-
-               return true;
-       };
-
-       NETDATA.dygraphClearSelection = function(state, t) {
-               if(typeof state.dygraph_instance !== 'undefined') {
-                       state.dygraph_instance.clearSelection();
-               }
-               return true;
-       };
-
-       NETDATA.dygraphSmoothInitialize = function(callback) {
-               $.ajax({
-                       url: NETDATA.dygraph_smooth_js,
-                       cache: true,
-                       dataType: "script",
-                       xhrFields: { withCredentials: true } // required for the cookie
-               })
-               .done(function() {
-                       NETDATA.dygraph.smooth = true;
-                       smoothPlotter.smoothing = 0.3;
-               })
-               .fail(function() {
-                       NETDATA.dygraph.smooth = false;
-               })
-               .always(function() {
-                       if(typeof callback === "function")
-                               callback();
-               });
-       };
-
-       NETDATA.dygraphInitialize = function(callback) {
-               if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
-                       $.ajax({
-                               url: NETDATA.dygraph_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function() {
-                               NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
-                       })
-                       .fail(function() {
-                               NETDATA.chartLibraries.dygraph.enabled = false;
-                               NETDATA.error(100, NETDATA.dygraph_js);
-                       })
-                       .always(function() {
-                               if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
-                                       NETDATA.dygraphSmoothInitialize(callback);
-                               else if(typeof callback === "function")
-                                       callback();
-                       });
-               }
-               else {
-                       NETDATA.chartLibraries.dygraph.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.dygraphChartUpdate = function(state, data) {
-               var dygraph = state.dygraph_instance;
-
-               if(typeof dygraph === 'undefined')
-                       return NETDATA.dygraphChartCreate(state, data);
-
-               // when the chart is not visible, and hidden
-               // if there is a window resize, dygraph detects
-               // its element size as 0x0.
-               // this will make it re-appear properly
-
-               if(state.tm.last_unhidden > state.dygraph_last_rendered)
-                       dygraph.resize();
-
-               var options = {
-                               file: data.result.data,
-                               colors: state.chartColors(),
-                               labels: data.result.labels,
-                               labelsDivWidth: state.chartWidth() - 70,
-                               visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
-               };
-
-               if(state.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.valueRange = state.dygraph_options.valueRange;
-                       options.isZoomedIgnoreProgrammaticZoom = true;
-                       state.dygraph_force_zoom = false;
-               }
-               else if(state.current.name !== 'auto') {
-                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                               state.log('dygraphChartUpdate() loose update');
-
-                       options.valueRange = state.dygraph_options.valueRange;
-               }
-               else {
-                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                               state.log('dygraphChartUpdate() strict update');
-
-                       options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
-                       options.valueRange = state.dygraph_options.valueRange;
-                       options.isZoomedIgnoreProgrammaticZoom = true;
-               }
-
-               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)) {
-                               NETDATA.dygraphChartCreate(state, data);
-                               return;
-                       }
-               }
-
-               dygraph.updateOptions(options);
-
-               state.dygraph_last_rendered = new Date().getTime();
-               return true;
-       };
-
-       NETDATA.dygraphChartCreate = function(state, data) {
-               if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                       state.log('dygraphChartCreate()');
-
-               var self = $(state.element);
-
-               var chart_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 strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
-               var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
-
-               state.dygraph_options = {
-                       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', // 'onmouseover',
-                       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') || false,
-                       xRangePad: self.data('dygraph-xrangepad') || 0,
-                       yRangePad: self.data('dygraph-yrangepad') || 1,
-
-                       valueRange: self.data('dygraph-valuerange') || 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') || 0.3,
-
-                       drawGrid: self.data('dygraph-drawgrid') || true,
-                       drawXGrid: self.data('dygraph-drawxgrid') || undefined,
-                       drawYGrid: self.data('dygraph-drawygrid') || undefined,
-                       gridLinePattern: self.data('dygraph-gridlinepattern') || null,
-                       gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
-                       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),
-                       axes: {
-                               x: {
-                                       pixelsPerLabel: 50,
-                                       ticker: Dygraph.dateTicker,
-                                       axisLabelFormatter: function (d, 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();
-                                               // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
-                                       }
-                               },
-                               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;
-                                       }
-                               }
-                       },
-                       legendFormatter: function(data) {
-                               var elements = state.element_legend_childs;
-
-                               // if the hidden div is not there
-                               // we are not managing the legend
-                               if(elements.hidden === null) return;
-
-                               if (typeof data.x !== 'undefined') {
-                                       state.legendSetDate(data.x);
-                                       var i = data.series.length;
-                                       while(i--) {
-                                               var series = data.series[i];
-                                               if(!series.isVisible) continue;
-                                               state.legendSetLabelValue(series.label, series.y);
-                                       }
-                               }
-
-                               return '';
-                       },
-                       drawCallback: function(dygraph, is_initial) {
-                               if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
-                                       state.dygraph_user_action = false;
-
-                                       var x_range = dygraph.xAxisRange();
-                                       var after = Math.round(x_range[0]);
-                                       var before = Math.round(x_range[1]);
-
-                                       if(NETDATA.options.debug.dygraph === true)
-                                               state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
-
-                                       if(before <= state.netdata_last && after >= state.netdata_first)
-                                               state.updateChartPanOrZoom(after, before);
-                               }
-                       },
-                       zoomCallback: function(minDate, maxDate, yRanges) {
-                               if(NETDATA.options.debug.dygraph === true)
-                                       state.log('dygraphZoomCallback()');
-
-                               state.globalSelectionSyncStop();
-                               state.globalSelectionSyncDelay();
-                               state.setMode('zoom');
-
-                               // refresh it to the greatest possible zoom level
-                               state.dygraph_user_action = true;
-                               state.dygraph_force_zoom = true;
-                               state.updateChartPanOrZoom(minDate, maxDate);
-                       },
-                       highlightCallback: function(event, x, points, row, seriesName) {
-                               if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                       state.log('dygraphHighlightCallback()');
-
-                               state.pauseChart();
-
-                               // there is a bug in dygraph when the chart is zoomed enough
-                               // 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;
-                               // 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);
-
-                               // 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;
-                       },
-                       unhighlightCallback: function(event) {
-                               if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                       state.log('dygraphUnhighlightCallback()');
-
-                               state.unpauseChart();
-                               state.globalSelectionSyncStop();
-                       },
-                       interactionModel : {
-                               mousedown: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.mousedown()');
-
-                                       state.dygraph_user_action = true;
-                                       state.globalSelectionSyncStop();
-
-                                       if(NETDATA.options.debug.dygraph === true)
-                                               state.log('dygraphMouseDown()');
-
-                                       // Right-click should not initiate a zoom.
-                                       if(event.button && event.button === 2) return;
-
-                                       context.initializeMouseDown(event, dygraph, context);
-
-                                       if(event.button && event.button === 1) {
-                                               if (event.altKey || event.shiftKey) {
-                                                       state.setMode('pan');
-                                                       state.globalSelectionSyncDelay();
-                                                       Dygraph.startPan(event, dygraph, context);
-                                               }
-                                               else {
-                                                       state.setMode('zoom');
-                                                       state.globalSelectionSyncDelay();
-                                                       Dygraph.startZoom(event, dygraph, context);
-                                               }
-                                       }
-                                       else {
-                                               if (event.altKey || event.shiftKey) {
-                                                       state.setMode('zoom');
-                                                       state.globalSelectionSyncDelay();
-                                                       Dygraph.startZoom(event, dygraph, context);
-                                               }
-                                               else {
-                                                       state.setMode('pan');
-                                                       state.globalSelectionSyncDelay();
-                                                       Dygraph.startPan(event, dygraph, context);
-                                               }
-                                       }
-                               },
-                               mousemove: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.mousemove()');
-
-                                       if(context.isPanning) {
-                                               state.dygraph_user_action = true;
-                                               state.globalSelectionSyncStop();
-                                               state.globalSelectionSyncDelay();
-                                               state.setMode('pan');
-                                               Dygraph.movePan(event, dygraph, context);
-                                       }
-                                       else if(context.isZooming) {
-                                               state.dygraph_user_action = true;
-                                               state.globalSelectionSyncStop();
-                                               state.globalSelectionSyncDelay();
-                                               state.setMode('zoom');
-                                               Dygraph.moveZoom(event, dygraph, context);
-                                       }
-                               },
-                               mouseup: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.mouseup()');
-
-                                       if (context.isPanning) {
-                                               state.dygraph_user_action = true;
-                                               state.globalSelectionSyncDelay();
-                                               Dygraph.endPan(event, dygraph, context);
-                                       }
-                                       else if (context.isZooming) {
-                                               state.dygraph_user_action = true;
-                                               state.globalSelectionSyncDelay();
-                                               Dygraph.endZoom(event, dygraph, context);
-                                       }
-                               },
-                               click: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.click()');
-
-                                       event.preventDefault();
-                               },
-                               dblclick: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.dblclick()');
-                                       NETDATA.resetAllCharts(state);
-                               },
-                               mousewheel: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.mousewheel()');
-
-                                       // Take the offset of a mouse event on the dygraph canvas and
-                                       // convert it to a pair of percentages from the bottom left.
-                                       // (Not top left, bottom is where the lower value is.)
-                                       function offsetToPercentage(g, offsetX, offsetY) {
-                                               // This is calculating the pixel offset of the leftmost date.
-                                               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)
-                                               var yOffset = g.toDomCoords(null, yar0[1])[1];
-
-                                               // x y w and h are relative to the corner of the drawing area,
-                                               // so that the upper corner of the drawing area is (0, 0).
-                                               var x = offsetX - xOffset;
-                                               var y = offsetY - yOffset;
-
-                                               // This is computing the rightmost pixel, effectively defining the
-                                               // width.
-                                               var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
-
-                                               // This is computing the lowest pixel, effectively defining the height.
-                                               var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
-
-                                               // Percentage from the left.
-                                               var xPct = w === 0 ? 0 : (x / w);
-                                               // Percentage from the top.
-                                               var yPct = h === 0 ? 0 : (y / h);
-
-                                               // The (1-) part below changes it from "% distance down from the top"
-                                               // to "% distance up from the bottom".
-                                               return [xPct, (1-yPct)];
-                                       }
-
-                                       // Adjusts [x, y] toward each other by zoomInPercentage%
-                                       // Split it so the left/bottom axis gets xBias/yBias of that change and
-                                       // tight/top gets (1-xBias)/(1-yBias) of that change.
-                                       //
-                                       // If a bias is missing it splits it down the middle.
-                                       function zoomRange(g, zoomInPercentage, xBias, yBias) {
-                                               xBias = xBias || 0.5;
-                                               yBias = yBias || 0.5;
-
-                                               function adjustAxis(axis, zoomInPercentage, bias) {
-                                                       var delta = axis[1] - axis[0];
-                                                       var increment = delta * zoomInPercentage;
-                                                       var foo = [increment * bias, increment * (1-bias)];
-
-                                                       return [ axis[0] + foo[0], axis[1] - foo[1] ];
-                                               }
-
-                                               var yAxes = g.yAxisRanges();
-                                               var newYAxes = [];
-                                               for (var i = 0; i < yAxes.length; i++) {
-                                                       newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
-                                               }
-
-                                               return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
-                                       }
-
-                                       if(event.altKey || event.shiftKey) {
-                                               state.dygraph_user_action = true;
-
-                                               state.globalSelectionSyncStop();
-                                               state.globalSelectionSyncDelay();
-
-                                               // http://dygraphs.com/gallery/interaction-api.js
-                                               var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
-                                               var percentage = normal / 50;
-
-                                               if (!(event.offsetX && event.offsetY)){
-                                                       event.offsetX = event.layerX - event.target.offsetLeft;
-                                                       event.offsetY = event.layerY - event.target.offsetTop;
-                                               }
-
-                                               var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
-                                               var xPct = percentages[0];
-                                               var yPct = percentages[1];
-
-                                               var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
-
-                                               var after = new_x_range[0];
-                                               var before = new_x_range[1];
-
-                                               var first = state.netdata_first + state.data_update_every;
-                                               var last = state.netdata_last + state.data_update_every;
-
-                                               if(before > last) {
-                                                       after -= (before - last);
-                                                       before = last;
-                                               }
-                                               if(after < first) {
-                                                       after = first;
-                                               }
-
-                                               state.setMode('zoom');
-                                               if(state.updateChartPanOrZoom(after, before) === true)
-                                                       dygraph.updateOptions({ dateWindow: [ after, before ] });
-
-                                               event.preventDefault();
-                                       }
-                               },
-                               touchstart: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.touchstart()');
-
-                                       state.dygraph_user_action = true;
-                                       state.setMode('zoom');
-                                       state.pauseChart();
-
-                                       Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
-
-                                       // we overwrite the touch directions at the end, to overwrite
-                                       // the internal default of dygraphs
-                                       context.touchDirections = { x: true, y: false };
-
-                                       state.dygraph_last_touch_start = new Date().getTime();
-                                       state.dygraph_last_touch_move = 0;
-
-                                       if(typeof event.touches[0].pageX === 'number')
-                                               state.dygraph_last_touch_page_x = event.touches[0].pageX;
-                                       else
-                                               state.dygraph_last_touch_page_x = 0;
-                               },
-                               touchmove: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.touchmove()');
-
-                                       state.dygraph_user_action = true;
-                                       Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
-
-                                       state.dygraph_last_touch_move = new Date().getTime();
-                               },
-                               touchend: function(event, dygraph, context) {
-                                       if(NETDATA.options.debug.dygraph === true || state.debug === true)
-                                               state.log('interactionModel.touchend()');
-
-                                       state.dygraph_user_action = true;
-                                       Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
-
-                                       // 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
-                                               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)
-                                                       state.globalSelectionSync(t);
-                                       }
-
-                                       // if it was double tap within double click time, reset the charts
-                                       var now = new Date().getTime();
-                                       if(typeof state.dygraph_last_touch_end !== 'undefined') {
-                                               if(state.dygraph_last_touch_move === 0) {
-                                                       var dt = now - state.dygraph_last_touch_end;
-                                                       if(dt <= NETDATA.options.current.double_click_speed)
-                                                               NETDATA.resetAllCharts(state);
-                                               }
-                                       }
-
-                                       // remember the timestamp of the last touch end
-                                       state.dygraph_last_touch_end = now;
-                               }
-                       }
-               };
-
-               if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
-                       state.dygraph_options.drawGrid = false;
-                       state.dygraph_options.drawAxis = false;
-                       state.dygraph_options.title = undefined;
-                       state.dygraph_options.units = 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;
-               }
-
-               if(smooth === true) {
-                       state.dygraph_smooth_eligible = true;
-
-                       if(NETDATA.options.current.smooth_plot === true)
-                               state.dygraph_options.plotter = smoothPlotter;
-               }
-               else state.dygraph_smooth_eligible = false;
-
-               state.dygraph_instance = new Dygraph(state.element_chart,
-                       data.result.data, state.dygraph_options);
-
-               state.dygraph_force_zoom = false;
-               state.dygraph_user_action = false;
-               state.dygraph_last_rendered = new Date().getTime();
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // morris
-
-       NETDATA.morrisInitialize = function(callback) {
-               if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
-
-                       // morris requires raphael
-                       if(!NETDATA.chartLibraries.raphael.initialized) {
-                               if(NETDATA.chartLibraries.raphael.enabled) {
-                                       NETDATA.raphaelInitialize(function() {
-                                               NETDATA.morrisInitialize(callback);
-                                       });
-                               }
-                               else {
-                                       NETDATA.chartLibraries.morris.enabled = false;
-                                       if(typeof callback === "function")
-                                               callback();
-                               }
-                       }
-                       else {
-                               NETDATA._loadCSS(NETDATA.morris_css);
-
-                               $.ajax({
-                                       url: NETDATA.morris_js,
-                                       cache: true,
-                                       dataType: "script",
-                                       xhrFields: { withCredentials: true } // required for the cookie
-                               })
-                               .done(function() {
-                                       NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
-                               })
-                               .fail(function() {
-                                       NETDATA.chartLibraries.morris.enabled = false;
-                                       NETDATA.error(100, NETDATA.morris_js);
-                               })
-                               .always(function() {
-                                       if(typeof callback === "function")
-                                               callback();
-                               });
-                       }
-               }
-               else {
-                       NETDATA.chartLibraries.morris.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.morrisChartUpdate = function(state, data) {
-               state.morris_instance.setData(data.result.data);
-               return true;
-       };
-
-       NETDATA.morrisChartCreate = function(state, data) {
-
-               state.morris_options = {
-                               element: state.element_chart.id,
-                               data: data.result.data,
-                               xkey: 'time',
-                               ykeys: data.dimension_names,
-                               labels: data.dimension_names,
-                               lineWidth: 2,
-                               pointSize: 3,
-                               smooth: true,
-                               hideHover: 'auto',
-                               parseTime: true,
-                               continuousLine: false,
-                               behaveLikeLine: false
-               };
-
-               if(state.chart.chart_type === 'line')
-                       state.morris_instance = new Morris.Line(state.morris_options);
-
-               else if(state.chart.chart_type === 'area') {
-                       state.morris_options.behaveLikeLine = true;
-                       state.morris_instance = new Morris.Area(state.morris_options);
-               }
-               else // stacked
-                       state.morris_instance = new Morris.Area(state.morris_options);
-
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // raphael
-
-       NETDATA.raphaelInitialize = function(callback) {
-               if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
-                       $.ajax({
-                               url: NETDATA.raphael_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function() {
-                               NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
-                       })
-                       .fail(function() {
-                               NETDATA.chartLibraries.raphael.enabled = false;
-                               NETDATA.error(100, NETDATA.raphael_js);
-                       })
-                       .always(function() {
-                               if(typeof callback === "function")
-                                       callback();
-                       });
-               }
-               else {
-                       NETDATA.chartLibraries.raphael.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.raphaelChartUpdate = function(state, data) {
-               $(state.element_chart).raphael(data.result, {
-                       width: state.chartWidth(),
-                       height: state.chartHeight()
-               });
-
-               return false;
-       };
-
-       NETDATA.raphaelChartCreate = function(state, data) {
-               $(state.element_chart).raphael(data.result, {
-                       width: state.chartWidth(),
-                       height: state.chartHeight()
-               });
-
-               return false;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // C3
-
-       NETDATA.c3Initialize = function(callback) {
-               if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
-
-                       // C3 requires D3
-                       if(!NETDATA.chartLibraries.d3.initialized) {
-                               if(NETDATA.chartLibraries.d3.enabled) {
-                                       NETDATA.d3Initialize(function() {
-                                               NETDATA.c3Initialize(callback);
-                                       });
-                               }
-                               else {
-                                       NETDATA.chartLibraries.c3.enabled = false;
-                                       if(typeof callback === "function")
-                                               callback();
-                               }
-                       }
-                       else {
-                               NETDATA._loadCSS(NETDATA.c3_css);
-
-                               $.ajax({
-                                       url: NETDATA.c3_js,
-                                       cache: true,
-                                       dataType: "script",
-                                       xhrFields: { withCredentials: true } // required for the cookie
-                               })
-                               .done(function() {
-                                       NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
-                               })
-                               .fail(function() {
-                                       NETDATA.chartLibraries.c3.enabled = false;
-                                       NETDATA.error(100, NETDATA.c3_js);
-                               })
-                               .always(function() {
-                                       if(typeof callback === "function")
-                                               callback();
-                               });
-                       }
-               }
-               else {
-                       NETDATA.chartLibraries.c3.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.c3ChartUpdate = function(state, data) {
-               state.c3_instance.destroy();
-               return NETDATA.c3ChartCreate(state, data);
-
-               //state.c3_instance.load({
-               //      rows: data.result,
-               //      unload: true
-               //});
-
-               //return true;
-       };
-
-       NETDATA.c3ChartCreate = function(state, data) {
-
-               state.element_chart.id = 'c3-' + state.uuid;
-               // console.log('id = ' + state.element_chart.id);
-
-               state.c3_instance = c3.generate({
-                       bindto: '#' + state.element_chart.id,
-                       size: {
-                               width: state.chartWidth(),
-                               height: state.chartHeight()
-                       },
-                       color: {
-                               pattern: state.chartColors()
-                       },
-                       data: {
-                               x: 'time',
-                               rows: data.result,
-                               type: (state.chart.chart_type === 'line')?'spline':'area-spline'
-                       },
-                       axis: {
-                               x: {
-                                       type: 'timeseries',
-                                       tick: {
-                                               format: function(x) {
-                                                       return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
-                                               }
-                                       }
-                               }
-                       },
-                       grid: {
-                               x: {
-                                       show: true
-                               },
-                               y: {
-                                       show: true
-                               }
-                       },
-                       point: {
-                               show: false
-                       },
-                       line: {
-                               connectNull: false
-                       },
-                       transition: {
-                               duration: 0
-                       },
-                       interaction: {
-                               enabled: true
-                       }
-               });
-
-               // console.log(state.c3_instance);
-
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // D3
-
-       NETDATA.d3Initialize = function(callback) {
-               if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
-                       $.ajax({
-                               url: NETDATA.d3_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function() {
-                               NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
-                       })
-                       .fail(function() {
-                               NETDATA.chartLibraries.d3.enabled = false;
-                               NETDATA.error(100, NETDATA.d3_js);
-                       })
-                       .always(function() {
-                               if(typeof callback === "function")
-                                       callback();
-                       });
-               }
-               else {
-                       NETDATA.chartLibraries.d3.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.d3ChartUpdate = function(state, data) {
-               return false;
-       };
-
-       NETDATA.d3ChartCreate = function(state, data) {
-               return false;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // google charts
-
-       NETDATA.googleInitialize = function(callback) {
-               if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
-                       $.ajax({
-                               url: NETDATA.google_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                       .done(function() {
-                               NETDATA.registerChartLibrary('google', NETDATA.google_js);
-                               google.load('visualization', '1.1', {
-                                       'packages': ['corechart', 'controls'],
-                                       'callback': callback
-                               });
-                       })
-                       .fail(function() {
-                               NETDATA.chartLibraries.google.enabled = false;
-                               NETDATA.error(100, NETDATA.google_js);
-                               if(typeof callback === "function")
-                                       callback();
-                       });
-               }
-               else {
-                       NETDATA.chartLibraries.google.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.googleChartUpdate = function(state, data) {
-               var datatable = new google.visualization.DataTable(data.result);
-               state.google_instance.draw(datatable, state.google_options);
-               return true;
-       };
-
-       NETDATA.googleChartCreate = function(state, data) {
-               var datatable = new google.visualization.DataTable(data.result);
-
-               state.google_options = {
-                       colors: state.chartColors(),
-
-                       // do not set width, height - the chart resizes itself
-                       //width: state.chartWidth(),
-                       //height: state.chartHeight(),
-                       lineWidth: 1,
-                       title: state.title,
-                       fontSize: 11,
-                       hAxis: {
-                       //      title: "Time of Day",
-                       //      format:'HH:mm:ss',
-                               viewWindowMode: 'maximized',
-                               slantedText: false,
-                               format:'HH:mm:ss',
-                               textStyle: {
-                                       fontSize: 9
-                               },
-                               gridlines: {
-                                       color: '#EEE'
-                               }
-                       },
-                       vAxis: {
-                               title: state.units,
-                               viewWindowMode: 'pretty',
-                               minValue: -0.1,
-                               maxValue: 0.1,
-                               direction: 1,
-                               textStyle: {
-                                       fontSize: 9
-                               },
-                               gridlines: {
-                                       color: '#EEE'
-                               }
-                       },
-                       chartArea: {
-                               width: '65%',
-                               height: '80%'
-                       },
-                       focusTarget: 'category',
-                       annotation: {
-                               '1': {
-                                       style: 'line'
-                               }
-                       },
-                       pointsVisible: 0,
-                       titlePosition: 'out',
-                       titleTextStyle: {
-                               fontSize: 11
-                       },
-                       tooltip: {
-                               isHtml: false,
-                               ignoreBounds: true,
-                               textStyle: {
-                                       fontSize: 9
-                               }
-                       },
-                       curveType: 'function',
-                       areaOpacity: 0.3,
-                       isStacked: false
-               };
-
-               switch(state.chart.chart_type) {
-                       case "area":
-                               state.google_options.vAxis.viewWindowMode = 'maximized';
-                               state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
-                               state.google_instance = new google.visualization.AreaChart(state.element_chart);
-                               break;
-
-                       case "stacked":
-                               state.google_options.isStacked = true;
-                               state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
-                               state.google_options.vAxis.viewWindowMode = 'maximized';
-                               state.google_options.vAxis.minValue = null;
-                               state.google_options.vAxis.maxValue = null;
-                               state.google_instance = new google.visualization.AreaChart(state.element_chart);
-                               break;
-
-                       default:
-                       case "line":
-                               state.google_options.lineWidth = 2;
-                               state.google_instance = new google.visualization.LineChart(state.element_chart);
-                               break;
-               }
-
-               state.google_instance.draw(datatable, state.google_options);
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-
-       NETDATA.percentFromValueMax = function(value, max) {
-               if(value === null) value = 0;
-               if(max < value) max = value;
-
-               var pcent = 0;
-               if(max !== 0) {
-                       pcent = Math.round(value * 100 / max);
-                       if(pcent === 0 && value > 0) pcent = 1;
-               }
-
-               return pcent;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // easy-pie-chart
-
-       NETDATA.easypiechartInitialize = function(callback) {
-               if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
-                       $.ajax({
-                               url: NETDATA.easypiechart_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                               .done(function() {
-                                       NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
-                               })
-                               .fail(function() {
-                                       NETDATA.chartLibraries.easypiechart.enabled = false;
-                                       NETDATA.error(100, NETDATA.easypiechart_js);
-                               })
-                               .always(function() {
-                                       if(typeof callback === "function")
-                                               callback();
-                               })
-               }
-               else {
-                       NETDATA.chartLibraries.easypiechart.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.easypiechartClearSelection = function(state) {
-               if(typeof state.easyPieChartEvent !== 'undefined') {
-                       if(state.easyPieChartEvent.timer !== null)
-                               clearTimeout(state.easyPieChartEvent.timer);
-
-                       state.easyPieChartEvent.timer = null;
-               }
-
-               if(state.isAutoRefreshable() === true && state.data !== null) {
-                       NETDATA.easypiechartChartUpdate(state, state.data);
-               }
-               else {
-                       state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
-                       state.easyPieChart_instance.update(0);
-               }
-               state.easyPieChart_instance.enableAnimation();
-
-               return true;
-       };
-
-       NETDATA.easypiechartSetSelection = function(state, t) {
-               if(state.timeIsVisible(t) !== true)
-                       return NETDATA.easypiechartClearSelection(state);
-
-               var slot = state.calculateRowForTime(t);
-               if(slot < 0 || slot >= state.data.result.length)
-                       return NETDATA.easypiechartClearSelection(state);
-
-               if(typeof state.easyPieChartEvent === 'undefined') {
-                       state.easyPieChartEvent = {
-                               timer: null,
-                               value: 0,
-                               pcent: 0
-                       };
-               }
-
-               var value = state.data.result[state.data.result.length - 1 - slot];
-               var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
-               var pcent = NETDATA.percentFromValueMax(value, max);
-
-               state.easyPieChartEvent.value = value;
-               state.easyPieChartEvent.pcent = pcent;
-               state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
-
-               if(state.easyPieChartEvent.timer === null) {
-                       state.easyPieChart_instance.disableAnimation();
-
-                       state.easyPieChartEvent.timer = setTimeout(function() {
-                               state.easyPieChartEvent.timer = null;
-                               state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
-                       }, NETDATA.options.current.charts_selection_animation_delay);
-               }
-
-               return true;
-       };
-
-       NETDATA.easypiechartChartUpdate = function(state, data) {
-               var value, max, pcent;
-
-               if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
-                       value = null;
-                       max = 0;
-                       pcent = 0;
-               }
-               else {
-                       value = data.result[0];
-                       max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
-                       pcent = NETDATA.percentFromValueMax(value, max);
-               }
-
-               state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
-               state.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 max = self.data('easypiechart-max-value') || null;
-               var adjust = self.data('easypiechart-adjust') || null;
-
-               if(max === null) {
-                       max = data.max;
-                       state.easyPieChartMax = null;
-               }
-               else
-                       state.easyPieChartMax = max;
-
-               var pcent = NETDATA.percentFromValueMax(value, 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 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.innerHTML = state.legendFormatValue(value);
-               state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
-               state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
-               state.element_chart.appendChild(state.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.innerHTML = 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);
-
-               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.innerHTML = state.units;
-               state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
-               state.easyPieChartUnits.style.top = unittop.toString() + 'px';
-               state.element_chart.appendChild(state.easyPieChartUnits);
-
-               chart.easyPieChart({
-                       barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
-                       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-rotate') || {duration: 500, enabled: true},
-                       easing: self.data('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')
-                       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();
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // gauge.js
-
-       NETDATA.gaugeInitialize = function(callback) {
-               if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
-                       $.ajax({
-                               url: NETDATA.gauge_js,
-                               cache: true,
-                               dataType: "script",
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                               .done(function() {
-                                       NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
-                               })
-                               .fail(function() {
-                                       NETDATA.chartLibraries.gauge.enabled = false;
-                                       NETDATA.error(100, NETDATA.gauge_js);
-                               })
-                               .always(function() {
-                                       if(typeof callback === "function")
-                                               callback();
-                               })
-               }
-               else {
-                       NETDATA.chartLibraries.gauge.enabled = false;
-                       if(typeof callback === "function")
-                               callback();
-               }
-       };
-
-       NETDATA.gaugeAnimation = function(state, status) {
-               var speed = 32;
-
-               if(typeof status === 'boolean' && status === false)
-                       speed = 1000000000;
-               else if(typeof status === 'number')
-                       speed = status;
-
-               state.gauge_instance.animationSpeed = speed;
-               state.___gaugeOld__.speed = speed;
-       };
-
-       NETDATA.gaugeSet = function(state, value, min, max) {
-               if(typeof value !== 'number') value = 0;
-               if(typeof min !== 'number') min = 0;
-               if(typeof max !== 'number') max = 0;
-               if(value > max) max = value;
-               if(value < min) min = value;
-               if(min > max) {
-                       var t = min;
-                       min = max;
-                       max = t;
-               }
-               else if(min == max)
-                       max = min + 1;
-
-               // gauge.js has an issue if the needle
-               // is smaller than min or larger than max
-               // when we set the new values
-               // the needle will go crazy
-
-               // to prevent it, we always feed it
-               // with a percentage, so that the needle
-               // 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;
-
-               state.gauge_instance.set(pcent);
-
-               state.___gaugeOld__.value = value;
-               state.___gaugeOld__.min = min;
-               state.___gaugeOld__.max = max;
-       };
-
-       NETDATA.gaugeSetLabels = function(state, value, min, max) {
-               if(state.___gaugeOld__.valueLabel !== value) {
-                       state.___gaugeOld__.valueLabel = value;
-                       state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
-               }
-               if(state.___gaugeOld__.minLabel !== min) {
-                       state.___gaugeOld__.minLabel = min;
-                       state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
-               }
-               if(state.___gaugeOld__.maxLabel !== max) {
-                       state.___gaugeOld__.maxLabel = max;
-                       state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
-               }
-       };
-
-       NETDATA.gaugeClearSelection = function(state) {
-               if(typeof state.gaugeEvent !== 'undefined') {
-                       if(state.gaugeEvent.timer !== null)
-                               clearTimeout(state.gaugeEvent.timer);
-
-                       state.gaugeEvent.timer = null;
-               }
-
-               if(state.isAutoRefreshable() === true && state.data !== null) {
-                       NETDATA.gaugeChartUpdate(state, state.data);
-               }
-               else {
-                       NETDATA.gaugeAnimation(state, false);
-                       NETDATA.gaugeSet(state, null, null, null);
-                       NETDATA.gaugeSetLabels(state, null, null, null);
-               }
-
-               NETDATA.gaugeAnimation(state, true);
-               return true;
-       };
-
-       NETDATA.gaugeSetSelection = function(state, t) {
-               if(state.timeIsVisible(t) !== true)
-                       return NETDATA.gaugeClearSelection(state);
-
-               var slot = state.calculateRowForTime(t);
-               if(slot < 0 || slot >= state.data.result.length)
-                       return NETDATA.gaugeClearSelection(state);
-
-               if(typeof state.gaugeEvent === 'undefined') {
-                       state.gaugeEvent = {
-                               timer: null,
-                               value: 0,
-                               min: 0,
-                               max: 0
-                       };
-               }
-
-               var value = state.data.result[state.data.result.length - 1 - slot];
-               var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
-               var min = 0;
-
-               state.gaugeEvent.value = value;
-               state.gaugeEvent.max = max;
-               state.gaugeEvent.min = min;
-               NETDATA.gaugeSetLabels(state, value, min, max);
-
-               if(state.gaugeEvent.timer === null) {
-                       NETDATA.gaugeAnimation(state, false);
-
-                       state.gaugeEvent.timer = setTimeout(function() {
-                               state.gaugeEvent.timer = null;
-                               NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
-                       }, NETDATA.options.current.charts_selection_animation_delay);
-               }
-
-               return true;
-       };
-
-       NETDATA.gaugeChartUpdate = function(state, data) {
-               var value, min, max;
-
-               if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
-                       value = 0;
-                       min = 0;
-                       max = 1;
-                       NETDATA.gaugeSetLabels(state, null, null, null);
-               }
-               else {
-                       value = data.result[0];
-                       min = 0;
-                       max = (state.gaugeMax === null)?data.max:state.gaugeMax;
-                       if(value > max) max = value;
-                       NETDATA.gaugeSetLabels(state, value, min, max);
-               }
-
-               NETDATA.gaugeSet(state, value, min, max);
-               return true;
-       };
-
-       NETDATA.gaugeChartCreate = function(state, data) {
-               var self = $(state.element);
-               // var chart = $(state.element_chart);
-
-               var value = data.result[0];
-               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;
-
-               if(max === null) {
-                       max = data.max;
-                       state.gaugeMax = null;
-               }
-               else
-                       state.gaugeMax = max;
-
-               var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
-               //switch(adjust) {
-               //      case 'width': width = height * ratio; break;
-               //      case 'height':
-               //      default: height = width / ratio; break;
-               //}
-               //state.element.style.width = width.toString() + 'px';
-               //state.element.style.height = height.toString() + 'px';
-
-               var lum_d = 0.05;
-
-               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
-                       pointer: {
-                               length: 0.8,                    // 0.9 The radius of the inner circle
-                               strokeWidth: 0.035,             // The rotation offset
-                               color: pointerColor             // Fill color
-                       },
-                       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
-               };
-
-               if (generateGradient.constructor === Array) {
-                       // example options:
-                       // data-gauge-generate-gradient="[0, 50, 100]"
-                       // data-gauge-gradient-percent-color-0="#FFFFFF"
-                       // data-gauge-gradient-percent-color-50="#999900"
-                       // data-gauge-gradient-percent-color-100="#000000"
-
-                       options.percentColors = new Array();
-                       var len = generateGradient.length;
-                       while(len--) {
-                               var pcent = generateGradient[len];
-                               var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
-                               if(color !== false) {
-                                       var a = new Array();
-                                       a[0] = pcent / 100;
-                                       a[1] = color;
-                                       options.percentColors.unshift(a);
-                               }
-                       }
-                       if(options.percentColors.length === 0)
-                               delete options.percentColors;
-               }
-               else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
-                       options.percentColors = [
-                               [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
-                               [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
-                               [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
-                               [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
-                               [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
-                               [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
-                               [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
-                               [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
-                               [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
-                               [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
-                               [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);
-
-               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 titlefontsize = Math.round(valuefontsize / 2);
-               var titletop = 0;
-               state.gaugeChartTitle = document.createElement('span');
-               state.gaugeChartTitle.className = 'gaugeChartTitle';
-               state.gaugeChartTitle.innerHTML = 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);
-
-               var unitfontsize = Math.round(titlefontsize * 0.9);
-               state.gaugeChartUnits = document.createElement('span');
-               state.gaugeChartUnits.className = 'gaugeChartUnits';
-               state.gaugeChartUnits.innerHTML = 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);
-
-               // when we just re-create the chart
-               // do not animate the first update
-               var animate = true;
-               if(typeof state.gauge_instance !== 'undefined')
-                       animate = false;
-
-               state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
-
-               state.___gaugeOld__ = {
-                       value: value,
-                       min: 0,
-                       max: max,
-                       valueLabel: null,
-                       minLabel: null,
-                       maxLabel: null
-               };
-
-               // we will always feed a percentage
-               state.gauge_instance.minValue = 0;
-               state.gauge_instance.maxValue = 100;
-
-               NETDATA.gaugeAnimation(state, animate);
-               NETDATA.gaugeSet(state, value, 0, max);
-               NETDATA.gaugeSetLabels(state, value, 0, max);
-               NETDATA.gaugeAnimation(state, true);
-               return true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Charts Libraries Registration
-
-       NETDATA.chartLibraries = {
-               "dygraph": {
-                       initialize: NETDATA.dygraphInitialize,
-                       create: NETDATA.dygraphChartCreate,
-                       update: NETDATA.dygraphChartUpdate,
-                       resize: function(state) {
-                               if(typeof state.dygraph_instance.resize === 'function')
-                                       state.dygraph_instance.resize();
-                       },
-                       setSelection: NETDATA.dygraphSetSelection,
-                       clearSelection:  NETDATA.dygraphClearSelection,
-                       toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
-                       initialized: false,
-                       enabled: true,
-                       format: function(state) { return 'json'; },
-                       options: function(state) { return 'ms|flip'; },
-                       legend: function(state) {
-                               if(this.isSparkline(state) === false)
-                                       return 'right-side';
-                               else
-                                       return null;
-                       },
-                       autoresize: function(state) { return true; },
-                       max_updates_to_recreate: function(state) { return 5000; },
-                       track_colors: function(state) { return true; },
-                       pixels_per_point: function(state) {
-                               if(this.isSparkline(state) === false)
-                                       return 3;
-                               else
-                                       return 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;
-                               }
-                               return state.dygraph_sparkline;
-                       }
-               },
-               "sparkline": {
-                       initialize: NETDATA.sparklineInitialize,
-                       create: NETDATA.sparklineChartCreate,
-                       update: NETDATA.sparklineChartUpdate,
-                       resize: null,
-                       setSelection: undefined, // function(state, t) { return true; },
-                       clearSelection: undefined, // function(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; }
-               },
-               "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; },
-                       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; }
-               },
-               "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; },
-                       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; }
-               },
-               "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; },
-                       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; }
-               },
-               "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; },
-                       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; }
-               },
-               "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; },
-                       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; }
-               },
-               "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; },
-                       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; }
-               },
-               "easypiechart": {
-                       initialize: NETDATA.easypiechartInitialize,
-                       create: NETDATA.easypiechartChartCreate,
-                       update: NETDATA.easypiechartChartUpdate,
-                       resize: null,
-                       setSelection: NETDATA.easypiechartSetSelection,
-                       clearSelection: NETDATA.easypiechartClearSelection,
-                       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; },
-                       aspect_ratio: 100
-               },
-               "gauge": {
-                       initialize: NETDATA.gaugeInitialize,
-                       create: NETDATA.gaugeChartCreate,
-                       update: NETDATA.gaugeChartUpdate,
-                       resize: null,
-                       setSelection: NETDATA.gaugeSetSelection,
-                       clearSelection: NETDATA.gaugeClearSelection,
-                       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; },
-                       aspect_ratio: 70
-               }
-       };
-
-       NETDATA.registerChartLibrary = function(library, url) {
-               if(NETDATA.options.debug.libraries === true)
-                       console.log("registering chart library: " + library);
-
-               NETDATA.chartLibraries[library].url = url;
-               NETDATA.chartLibraries[library].initialized = true;
-               NETDATA.chartLibraries[library].enabled = true;
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Load required JS libraries and CSS
-
-       NETDATA.requiredJs = [
-               {
-                       url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
-                       isAlreadyLoaded: function() {
-                               // check if bootstrap is loaded
-                               if(typeof $().emulateTransitionEnd == 'function')
-                                       return true;
-                               else {
-                                       if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
-                                               return true;
-                                       else
-                                               return false;
-                               }
-                       }
-               },
-               {
-                       url: NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
-                       isAlreadyLoaded: function() { return false; }
-               },
-               {
-                       url: NETDATA.serverDefault + 'lib/bootstrap-toggle.min.js',
-                       isAlreadyLoaded: function() { return false; }
-               }
-       ];
-
-       NETDATA.requiredCSS = [
-               {
-                       url: NETDATA.themes.current.bootstrap_css,
-                       isAlreadyLoaded: function() {
-                               if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
-                                       return true;
-                               else
-                                       return false;
-                       }
-               },
-               {
-                       url: NETDATA.serverDefault + 'css/font-awesome.min.css',
-                       isAlreadyLoaded: function() { return false; }
-               },
-               {
-                       url: NETDATA.themes.current.dashboard_css,
-                       isAlreadyLoaded: function() { return false; }
-               },
-               {
-                       url: NETDATA.serverDefault + 'css/bootstrap-toggle.min.css',
-                       isAlreadyLoaded: function() { return false; }
-               }
-       ];
-
-       NETDATA.loadRequiredJs = function(index, callback) {
-               if(index >= NETDATA.requiredJs.length)  {
-                       if(typeof callback === 'function')
-                               callback();
-                       return;
-               }
-
-               if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
-                       NETDATA.loadRequiredJs(++index, callback);
-                       return;
-               }
-
-               if(NETDATA.options.debug.main_loop === true)
-                       console.log('loading ' + NETDATA.requiredJs[index].url);
-
-               $.ajax({
-                       url: NETDATA.requiredJs[index].url,
-                       cache: true,
-                       dataType: "script",
-                       xhrFields: { withCredentials: true } // required for the cookie
-               })
-               .success(function() {
-                       if(NETDATA.options.debug.main_loop === true)
-                               console.log('loaded ' + NETDATA.requiredJs[index].url);
-
-                       NETDATA.loadRequiredJs(++index, callback);
-               })
-               .fail(function() {
-                       alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
-               })
-       };
-
-       NETDATA.loadRequiredCSS = function(index) {
-               if(index >= NETDATA.requiredCSS.length)
-                       return;
-
-               if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
-                       NETDATA.loadRequiredCSS(++index);
-                       return;
-               }
-
-               if(NETDATA.options.debug.main_loop === true)
-                       console.log('loading ' + NETDATA.requiredCSS[index].url);
-
-               NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
-               NETDATA.loadRequiredCSS(++index);
-       };
-
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // 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
-               person_urls: null,
-
-               parsePersonUrls: function(person_urls) {
-                       // console.log(person_urls);
-                       NETDATA.registry.person_urls = person_urls;
-
-                       if(person_urls) {
-                               NETDATA.registry.machines = {};
-                               NETDATA.registry.machines_array = new Array();
-
-                               var now = new Date().getTime();
-                               var apu = person_urls;
-                               var i = apu.length;
-                               while(i--) {
-                                       if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
-                                               // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
-
-                                               var obj = {
-                                                       guid: apu[i][0],
-                                                       url: apu[i][1],
-                                                       last_t: apu[i][2],
-                                                       accesses: apu[i][3],
-                                                       name: apu[i][4],
-                                                       alternate_urls: new Array()
-                                               };
-                                               obj.alternate_urls.push(apu[i][1]);
-
-                                               NETDATA.registry.machines[apu[i][0]] = obj;
-                                               NETDATA.registry.machines_array.push(obj);
-                                       }
-                                       else {
-                                               // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
-
-                                               var pu = NETDATA.registry.machines[apu[i][0]];
-                                               if(pu.last_t < apu[i][2]) {
-                                                       pu.url = apu[i][1];
-                                                       pu.last_t = apu[i][2];
-                                                       pu.name = apu[i][4];
-                                               }
-                                               pu.accesses += apu[i][3];
-                                               pu.alternate_urls.push(apu[i][1]);
-                                       }
-                               }
-                       }
-
-                       if(typeof netdataRegistryCallback === 'function')
-                               netdataRegistryCallback(NETDATA.registry.machines_array);
-               },
-
-               init: function() {
-                       if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry)
-                               return;
-
-                       NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
-                               if(data) {
-                                       NETDATA.registry.server = data.registry;
-                                       NETDATA.registry.machine_guid = data.machine_guid;
-                                       NETDATA.registry.hostname = data.hostname;
-
-                                       NETDATA.registry.access(2, function (person_urls) {
-                                               NETDATA.registry.parsePersonUrls(person_urls);
-
-                                       });
-                               }
-                       });
-               },
-
-               hello: function(host, callback) {
-                       while(host.slice(-1) === '/')
-                               host = host.substring(0, host.length - 1);
-
-                       // send HELLO to a netdata server:
-                       // 1. verifies the server is reachable
-                       // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
-                       $.ajax({
-                                       url: host + '/api/v1/registry?action=hello',
-                                       async: true,
-                                       cache: false,
-                                       xhrFields: { withCredentials: true } // required for the cookie
-                               })
-                               .done(function(data) {
-                                       if(typeof data.status !== 'string' || data.status !== 'ok') {
-                                               NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
-                                               data = null;
-                                       }
-
-                                       if(typeof callback === 'function')
-                                               callback(data);
-                               })
-                               .fail(function() {
-                                       NETDATA.error(407, host);
-
-                                       if(typeof callback === 'function')
-                                               callback(null);
-                               });
-               },
-
-               access: function(max_redirects, callback) {
-                       // send ACCESS to a netdata registry:
-                       // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
-                       // 2. it responds with a list of netdata servers we know
-                       // the registry identifies us using a cookie it sets the first time we access it
-                       // the registry may respond with a redirect URL to send us to another registry
-                       $.ajax({
-                                       url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault), // + '&visible_url=' + encodeURIComponent(document.location),
-                                       async: true,
-                                       cache: false,
-                                       xhrFields: { withCredentials: true } // required for the cookie
-                               })
-                               .done(function(data) {
-                                       var redirect = null;
-                                       if(typeof data.registry === 'string')
-                                               redirect = data.registry;
-
-                                       if(typeof data.status !== 'string' || data.status !== 'ok') {
-                                               NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
-                                               data = null;
-                                       }
-
-                                       if(data === null) {
-                                               if(redirect !== null && max_redirects > 0) {
-                                                       NETDATA.registry.server = redirect;
-                                                       NETDATA.registry.access(max_redirects - 1, callback);
-                                               }
-                                               else {
-                                                       if(typeof callback === 'function')
-                                                               callback(null);
-                                               }
-                                       }
-                                       else {
-                                               if(typeof data.person_guid === 'string')
-                                                       NETDATA.registry.person_guid = data.person_guid;
-
-                                               if(typeof callback === 'function')
-                                                       callback(data.urls);
-                                       }
-                               })
-                               .fail(function() {
-                                       NETDATA.error(410, NETDATA.registry.server);
-
-                                       if(typeof callback === 'function')
-                                               callback(null);
-                               });
-               },
-
-               delete: function(delete_url, callback) {
-                       // send DELETE to a netdata registry:
-                       $.ajax({
-                               url: NETDATA.registry.server + '/api/v1/registry?action=delete&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&delete_url=' + encodeURIComponent(delete_url),
-                               async: true,
-                               cache: false,
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                               .done(function(data) {
-                                       if(typeof data.status !== 'string' || data.status !== 'ok') {
-                                               NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
-                                               data = null;
-                                       }
-
-                                       if(typeof callback === 'function')
-                                               callback(data);
-                               })
-                               .fail(function() {
-                                       NETDATA.error(412, NETDATA.registry.server);
-
-                                       if(typeof callback === 'function')
-                                               callback(null);
-                               });
-               },
-
-               switch: function(new_person_guid, callback) {
-                       // impersonate
-                       $.ajax({
-                               url: NETDATA.registry.server + '/api/v1/registry?action=switch&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&to=' + new_person_guid,
-                               async: true,
-                               cache: false,
-                               xhrFields: { withCredentials: true } // required for the cookie
-                       })
-                               .done(function(data) {
-                                       if(typeof data.status !== 'string' || data.status !== 'ok') {
-                                               NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
-                                               data = null;
-                                       }
-
-                                       if(typeof callback === 'function')
-                                               callback(data);
-                               })
-                               .fail(function() {
-                                       NETDATA.error(414, NETDATA.registry.server);
-
-                                       if(typeof callback === 'function')
-                                               callback(null);
-                               });
-               }
-       };
-
-       // ----------------------------------------------------------------------------------------------------------------
-       // Boot it!
-
-       NETDATA.errorReset();
-       NETDATA.loadRequiredCSS(0);
-
-       NETDATA._loadjQuery(function() {
-               NETDATA.loadRequiredJs(0, function() {
-                       if(typeof $().emulateTransitionEnd !== 'function') {
-                               // bootstrap is not available
-                               NETDATA.options.current.show_help = false;
-                       }
-
-                       if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
-                               if(NETDATA.options.debug.main_loop === true)
-                                       console.log('starting chart refresh thread');
-
-                               NETDATA.start();
-                       }
-               });
-       });
-
-       // window.NETDATA = NETDATA;
+            sync_selection_delay: 1500, // ms - when you pan or zoom a chart, wait this amount
+                                        // of time before setting up synchronized selections
+                                        // on hover.
+
+            sync_selection: true,       // enable or disable selection sync
+
+            pan_and_zoom_delay: 50,     // when panning or zooming, how ofter to update the chart
+
+            sync_pan_and_zoom: true,    // enable or disable pan and zoom sync
+
+            pan_and_zoom_data_padding: true, // fetch more data for the master chart when panning or zooming
+
+            update_only_visible: true,  // enable or disable visibility management
+
+            parallel_refresher: true,   // 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
+
+            show_help: netdataShowHelp, // when enabled the charts will show some help
+            show_help_delay_show_ms: 500,
+            show_help_delay_hide_ms: 0,
+
+            eliminate_zero_dimensions: true, // do not show dimensions with just zeros
+
+            stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
+            stop_updates_while_resizing: 1000,  // ms - time to stop auto-refreshes while resizing the charts
+
+            double_click_speed: 500,    // ms - time between clicks / taps to detect double click/tap
+
+            smooth_plot: true,          // enable smooth plot, where possible
+
+            charts_selection_animation_delay: 50, // delay to animate charts when syncing selection
+
+            color_fill_opacity_line: 1.0,
+            color_fill_opacity_area: 0.2,
+            color_fill_opacity_stacked: 0.8,
+
+            pan_and_zoom_factor: 0.25,      // the increment when panning and zooming with the toolbox
+            pan_and_zoom_factor_multiplier_control: 2.0,
+            pan_and_zoom_factor_multiplier_shift: 3.0,
+            pan_and_zoom_factor_multiplier_alt: 4.0,
+
+            setOptionCallback: function() { ; }
+        },
+
+        debug: {
+            show_boxes:         false,
+            main_loop:          false,
+            focus:              false,
+            visibility:         false,
+            chart_data_url:     false,
+            chart_errors:       false, // FIXME
+            chart_timing:       false,
+            chart_calls:        false,
+            libraries:          false,
+            dygraph:            false
+        }
+    };
+
+    NETDATA.statistics = {
+        refreshes_total: 0,
+        refreshes_active: 0,
+        refreshes_active_max: 0
+    };
+
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // local storage options
+
+    NETDATA.localStorage = {
+        default: {},
+        current: {},
+        callback: {} // only used for resetting back to defaults
+    };
+
+    NETDATA.localStorageGet = function(key, def, callback) {
+        var ret = def;
+
+        if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
+            NETDATA.localStorage.default[key.toString()] = def;
+            NETDATA.localStorage.callback[key.toString()] = callback;
+        }
+
+        if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
+            try {
+                // console.log('localStorage: loading "' + key.toString() + '"');
+                ret = localStorage.getItem(key.toString());
+                // console.log('netdata loaded: ' + key.toString() + ' = ' + ret.toString());
+                if(ret === null || ret === 'undefined') {
+                    // console.log('localStorage: cannot load it, saving "' + key.toString() + '" with value "' + JSON.stringify(def) + '"');
+                    localStorage.setItem(key.toString(), JSON.stringify(def));
+                    ret = def;
+                }
+                else {
+                    // console.log('localStorage: got "' + key.toString() + '" with value "' + ret + '"');
+                    ret = JSON.parse(ret);
+                    // console.log('localStorage: loaded "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
+                }
+            }
+            catch(error) {
+                console.log('localStorage: failed to read "' + key.toString() + '", using default: "' + def.toString() + '"');
+                ret = def;
+            }
+        }
+
+        if(typeof ret === 'undefined' || ret === 'undefined') {
+            console.log('localStorage: LOADED UNDEFINED "' + key.toString() + '" as value ' + ret + ' of type ' + typeof(ret));
+            ret = def;
+        }
+
+        NETDATA.localStorage.current[key.toString()] = ret;
+        return ret;
+    };
+
+    NETDATA.localStorageSet = function(key, value, callback) {
+        if(typeof value === 'undefined' || value === 'undefined') {
+            console.log('localStorage: ATTEMPT TO SET UNDEFINED "' + key.toString() + '" as value ' + value + ' of type ' + typeof(value));
+        }
+
+        if(typeof NETDATA.localStorage.default[key.toString()] === 'undefined') {
+            NETDATA.localStorage.default[key.toString()] = value;
+            NETDATA.localStorage.current[key.toString()] = value;
+            NETDATA.localStorage.callback[key.toString()] = callback;
+        }
+
+        if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
+            // console.log('localStorage: saving "' + key.toString() + '" with value "' + JSON.stringify(value) + '"');
+            try {
+                localStorage.setItem(key.toString(), JSON.stringify(value));
+            }
+            catch(e) {
+                console.log('localStorage: failed to save "' + key.toString() + '" with value: "' + value.toString() + '"');
+            }
+        }
+
+        NETDATA.localStorage.current[key.toString()] = value;
+        return value;
+    };
+
+    NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
+        for(var i in obj) {
+            if(typeof obj[i] === 'object') {
+                //console.log('object ' + prefix + '.' + i.toString());
+                NETDATA.localStorageGetRecursive(obj[i], prefix + '.' + i.toString(), callback);
+                continue;
+            }
+
+            obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
+        }
+    };
+
+    NETDATA.setOption = function(key, value) {
+        if(key.toString() === 'setOptionCallback') {
+            if(typeof NETDATA.options.current.setOptionCallback === 'function') {
+                NETDATA.options.current[key.toString()] = value;
+                NETDATA.options.current.setOptionCallback();
+            }
+        }
+        else if(NETDATA.options.current[key.toString()] !== value) {
+            var name = 'options.' + key.toString();
+
+            if(typeof NETDATA.localStorage.default[name.toString()] === 'undefined')
+                console.log('localStorage: setOption() on unsaved option: "' + name.toString() + '", value: ' + value);
+
+            //console.log(NETDATA.localStorage);
+            //console.log('setOption: setting "' + key.toString() + '" to "' + value + '" of type ' + typeof(value) + ' original type ' + typeof(NETDATA.options.current[key.toString()]));
+            //console.log(NETDATA.options);
+            NETDATA.options.current[key.toString()] = NETDATA.localStorageSet(name.toString(), value, null);
+
+            if(typeof NETDATA.options.current.setOptionCallback === 'function')
+                NETDATA.options.current.setOptionCallback();
+        }
+
+        return true;
+    };
+
+    NETDATA.getOption = function(key) {
+        return NETDATA.options.current[key.toString()];
+    };
+
+    // read settings from local storage
+    NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
+
+    // always start with this option enabled.
+    NETDATA.setOption('stop_updates_when_focus_is_lost', true);
+
+    NETDATA.resetOptions = function() {
+        for(var i in NETDATA.localStorage.default) {
+            var a = i.split('.');
+
+            if(a[0] === 'options') {
+                if(a[1] === 'setOptionCallback') continue;
+                if(typeof NETDATA.localStorage.default[i] === 'undefined') continue;
+                if(NETDATA.options.current[i] === NETDATA.localStorage.default[i]) continue;
+
+                NETDATA.setOption(a[1], NETDATA.localStorage.default[i]);
+            }
+            else if(a[0] === 'chart_heights') {
+                if(typeof NETDATA.localStorage.callback[i] === 'function' && typeof NETDATA.localStorage.default[i] !== 'undefined') {
+                    NETDATA.localStorage.callback[i](NETDATA.localStorage.default[i]);
+                }
+            }
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------------------
+
+    if(NETDATA.options.debug.main_loop === true)
+        console.log('welcome to NETDATA');
+
+    NETDATA.onresize = function() {
+        NETDATA.options.last_resized = new Date().getTime();
+        NETDATA.onscroll();
+    };
+
+    NETDATA.onscroll = function() {
+        // console.log('onscroll');
+
+        NETDATA.options.last_page_scroll = new Date().getTime();
+        if(NETDATA.options.targets === null) return;
+
+        // when the user scrolls he sees that we have
+        // hidden all the not-visible charts
+        // using this little function we try to switch
+        // the charts back to visible quickly
+        var targets = NETDATA.options.targets;
+        var len = targets.length;
+        while(len--) targets[len].isVisible();
+    };
+
+    window.onresize = NETDATA.onresize;
+    window.onscroll = NETDATA.onscroll;
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Error Handling
+
+    NETDATA.errorCodes = {
+        100: { message: "Cannot load chart library", alert: true },
+        101: { message: "Cannot load jQuery", alert: true },
+        402: { message: "Chart library not found", alert: false },
+        403: { message: "Chart library not enabled/is failed", alert: false },
+        404: { message: "Chart not found", alert: false },
+        405: { message: "Cannot download charts index from server", alert: true },
+        406: { message: "Invalid charts index downloaded from server", alert: true },
+        407: { message: "Cannot HELLO netdata server", alert: false },
+        408: { message: "Netdata servers sent invalid response to HELLO", alert: false },
+        409: { message: "Cannot ACCESS netdata registry", alert: false },
+        410: { message: "Netdata registry ACCESS failed", alert: false },
+        411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
+        412: { message: "Netdata registry DELETE failed", alert: false },
+        413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
+        414: { message: "Netdata registry SWITCH failed", alert: false }
+    };
+    NETDATA.errorLast = {
+        code: 0,
+        message: "",
+        datetime: 0
+    };
+
+    NETDATA.error = function(code, msg) {
+        NETDATA.errorLast.code = code;
+        NETDATA.errorLast.message = msg;
+        NETDATA.errorLast.datetime = new Date().getTime();
+
+        console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
+
+        var ret = true;
+        if(typeof netdataErrorCallback === 'function') {
+           ret = netdataErrorCallback('system', code, msg);
+        }
+
+        if(ret && NETDATA.errorCodes[code].alert)
+            alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
+    };
+
+    NETDATA.errorReset = function() {
+        NETDATA.errorLast.code = 0;
+        NETDATA.errorLast.message = "You are doing fine!";
+        NETDATA.errorLast.datetime = 0;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Chart Registry
+
+    // When multiple charts need the same chart, we avoid downloading it
+    // multiple times (and having it in browser memory multiple time)
+    // by using this registry.
+
+    // Every time we download a chart definition, we save it here with .add()
+    // Then we try to get it back with .get(). If that fails, we download it.
+
+    NETDATA.chartRegistry = {
+        charts: {},
+
+        fixid: function(id) {
+            return id.replace(/:/g, "_").replace(/\//g, "_");
+        },
+
+        add: function(host, id, data) {
+            host = this.fixid(host);
+            id   = this.fixid(id);
+
+            if(typeof this.charts[host] === 'undefined')
+                this.charts[host] = {};
+
+            //console.log('added ' + host + '/' + id);
+            this.charts[host][id] = data;
+        },
+
+        get: function(host, id) {
+            host = this.fixid(host);
+            id   = this.fixid(id);
+
+            if(typeof this.charts[host] === 'undefined')
+                return null;
+
+            if(typeof this.charts[host][id] === 'undefined')
+                return null;
+
+            //console.log('cached ' + host + '/' + id);
+            return this.charts[host][id];
+        },
+
+        downloadAll: function(host, callback) {
+            while(host.slice(-1) === '/')
+                host = host.substring(0, host.length - 1);
+
+            var self = this;
+
+            $.ajax({
+                url: host + '/api/v1/charts',
+                async: true,
+                cache: false,
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function(data) {
+                if(data !== null) {
+                    var h = NETDATA.chartRegistry.fixid(host);
+                    self.charts[h] = data.charts;
+                }
+                else NETDATA.error(406, host + '/api/v1/charts');
+
+                if(typeof callback === 'function')
+                    callback(data);
+            })
+            .fail(function() {
+                NETDATA.error(405, host + '/api/v1/charts');
+
+                if(typeof callback === 'function')
+                    callback(null);
+            });
+        }
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Global Pan and Zoom on charts
+
+    // Using this structure are synchronize all the charts, so that
+    // when you pan or zoom one, all others are automatically refreshed
+    // to the same timespan.
+
+    NETDATA.globalPanAndZoom = {
+        seq: 0,                 // timestamp ms
+                                // 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
+
+        master: null,           // the master chart (state), to which all others
+                                // are synchronized
+
+        force_before_ms: null,  // the timespan to sync all other charts
+        force_after_ms: null,
+
+        callback: null,
+
+        // set a new master
+        setMaster: function(state, after, before) {
+            if(NETDATA.options.current.sync_pan_and_zoom === false)
+                return;
+
+            if(this.master !== null && this.master !== state)
+                this.master.resetChart(true, true);
+
+            var now = new Date().getTime();
+            this.master = state;
+            this.seq = now;
+            this.force_after_ms = after;
+            this.force_before_ms = before;
+            NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.global_pan_sync_time;
+
+            if(typeof this.callback === 'function')
+                this.callback(true, after, before);
+        },
+
+        // clear the master
+        clearMaster: function() {
+            if(this.master !== null) {
+                var st = this.master;
+                this.master = null;
+                st.resetChart();
+            }
+
+            this.master = null;
+            this.seq = 0;
+            this.force_after_ms = null;
+            this.force_before_ms = null;
+            NETDATA.options.auto_refresher_stop_until = 0;
+
+            if(typeof this.callback === 'function')
+                this.callback(false, 0, 0);
+        },
+
+        // is the given state the master of the global
+        // pan and zoom sync?
+        isMaster: function(state) {
+            if(this.master === state) return true;
+            return false;
+        },
+
+        // 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;
+        },
+
+        // check if a chart, other than the master
+        // needs to be refreshed, due to the global pan and zoom
+        shouldBeAutoRefreshed: function(state) {
+            if(this.master === null || this.seq === 0)
+                return false;
+
+            //if(state.needsRecreation())
+            //  return true;
+
+            if(state.tm.pan_and_zoom_seq === this.seq)
+                return false;
+
+            return true;
+        }
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // dimensions selection
+
+    // FIXME
+    // move color assignment to dimensions, here
+
+    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.selected_count > parent.unselected_count)
+            this.selected = true;
+        else
+            this.selected = false;
+
+        this.setOptions(name_div, value_div, color);
+    };
+
+    dimensionStatus.prototype.invalidate = function() {
+        this.name_div = null;
+        this.value_div = null;
+        this.enabled = false;
+    };
+
+    dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
+        this.color = color;
+
+        if(this.name_div != name_div) {
+            this.name_div = name_div;
+            this.name_div.title = this.label;
+            this.name_div.style.color = this.color;
+            if(this.selected === false)
+                this.name_div.className = 'netdata-legend-name not-selected';
+            else
+                this.name_div.className = 'netdata-legend-name selected';
+        }
+
+        if(this.value_div != value_div) {
+            this.value_div = value_div;
+            this.value_div.title = this.label;
+            this.value_div.style.color = this.color;
+            if(this.selected === false)
+                this.value_div.className = 'netdata-legend-value not-selected';
+            else
+                this.value_div.className = 'netdata-legend-value selected';
+        }
+
+        this.enabled = true;
+        this.setHandler();
+    };
+
+    dimensionStatus.prototype.setHandler = function() {
+        if(this.enabled === false) return;
+
+        var ds = this;
+
+        // this.name_div.onmousedown = this.value_div.onmousedown = function(e) {
+        this.name_div.onclick = this.value_div.onclick = function(e) {
+            e.preventDefault();
+            if(ds.isSelected()) {
+                // this is selected
+                if(e.shiftKey === true || e.ctrlKey === true) {
+                    // control or shift key is pressed -> unselect this (except is none will remain selected, in which case select all)
+                    ds.unselect();
+
+                    if(ds.parent.countSelected() === 0)
+                        ds.parent.selectAll();
+                }
+                else {
+                    // no key is pressed -> select only this (except if it is the only selected already, in which case select all)
+                    if(ds.parent.countSelected() === 1) {
+                        ds.parent.selectAll();
+                    }
+                    else {
+                        ds.parent.selectNone();
+                        ds.select();
+                    }
+                }
+            }
+            else {
+                // this is not selected
+                if(e.shiftKey === true || e.ctrlKey === true) {
+                    // control or shift key is pressed -> select this too
+                    ds.select();
+                }
+                else {
+                    // no key is pressed -> select only this
+                    ds.parent.selectNone();
+                    ds.select();
+                }
+            }
+
+            ds.parent.state.redrawChart();
+        }
+    };
+
+    dimensionStatus.prototype.select = function() {
+        if(this.enabled === false) return;
+
+        this.name_div.className = 'netdata-legend-name selected';
+        this.value_div.className = 'netdata-legend-value selected';
+        this.selected = true;
+    };
+
+    dimensionStatus.prototype.unselect = function() {
+        if(this.enabled === false) return;
+
+        this.name_div.className = 'netdata-legend-name not-selected';
+        this.value_div.className = 'netdata-legend-value hidden';
+        this.selected = false;
+    };
+
+    dimensionStatus.prototype.isSelected = function() {
+        return(this.enabled === true && this.selected === true);
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+
+    dimensionsVisibility = function(state) {
+        this.state = state;
+        this.len = 0;
+        this.dimensions = {};
+        this.selected_count = 0;
+        this.unselected_count = 0;
+    };
+
+    dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
+        if(typeof this.dimensions[label] === 'undefined') {
+            this.len++;
+            this.dimensions[label] = new dimensionStatus(this, label, name_div, value_div, color);
+        }
+        else
+            this.dimensions[label].setOptions(name_div, value_div, color);
+
+        return this.dimensions[label];
+    };
+
+    dimensionsVisibility.prototype.dimensionGet = function(label) {
+        return this.dimensions[label];
+    };
+
+    dimensionsVisibility.prototype.invalidateAll = function() {
+        for(var d in this.dimensions)
+            this.dimensions[d].invalidate();
+    };
+
+    dimensionsVisibility.prototype.selectAll = function() {
+        for(var d in this.dimensions)
+            this.dimensions[d].select();
+    };
+
+    dimensionsVisibility.prototype.countSelected = function() {
+        var i = 0;
+        for(var d in this.dimensions)
+            if(this.dimensions[d].isSelected()) i++;
+
+        return i;
+    };
+
+    dimensionsVisibility.prototype.selectNone = function() {
+        for(var d in this.dimensions)
+            this.dimensions[d].unselect();
+    };
+
+    dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
+        var ret = new Array();
+        this.selected_count = 0;
+        this.unselected_count = 0;
+
+        for(var i = 0, len = array.length; i < len ; i++) {
+            var ds = this.dimensions[array[i]];
+            if(typeof ds === 'undefined') {
+                // console.log(array[i] + ' is not found');
+                ret.push(false);
+                continue;
+            }
+
+            if(ds.isSelected()) {
+                ret.push(true);
+                this.selected_count++;
+            }
+            else {
+                ret.push(false);
+                this.unselected_count++;
+            }
+        }
+
+        if(this.selected_count === 0 && this.unselected_count !== 0) {
+            this.selectAll();
+            return this.selected2BooleanArray(array);
+        }
+
+        return ret;
+    };
+
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // global selection sync
+
+    NETDATA.globalSelectionSync = {
+        state: null,
+        dont_sync_before: 0,
+        last_t: 0,
+        slaves: [],
+
+        stop: function() {
+            if(this.state !== null)
+                this.state.globalSelectionSyncStop();
+        },
+
+        delay: function() {
+            if(this.state !== null) {
+                this.state.globalSelectionSyncDelay();
+            }
+        }
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Our state object, where all per-chart values are stored
+
+    chartState = function(element) {
+        var self = $(element);
+        this.element = element;
+
+        // IMPORTANT:
+        // all private functions should use 'that', instead of 'this'
+        var that = this;
+
+        /* error() - private
+         * show an error instead of the chart
+         */
+        var error = function(msg) {
+            var ret = true;
+
+            if(typeof netdataErrorCallback === 'function') {
+                ret = netdataErrorCallback('chart', that.id, msg);
+            }
+
+            if(ret) {
+                that.element.innerHTML = that.id + ': ' + msg;
+                that.enabled = false;
+                that.current = that.pan;
+            }
+        };
+
+        // GUID - a unique identifier for the chart
+        this.uuid = NETDATA.guid();
+
+        // string - the name of chart
+        this.id = self.data('netdata');
+
+        // string - the key for localStorage settings
+        this.settings_id = self.data('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;
+
+        if(this.settings_id !== null) {
+            this.height = NETDATA.localStorageGet('chart_heights.' + this.settings_id, this.height, function(height) {
+                // this is the callback that will be called
+                // if and when the user resets all localStorage variables
+                // to their defaults
+
+                resizeChartToHeight(height);
+            });
+        }
+
+        // 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;
+
+        // 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,
+            nano: null,
+            nano_options: null,
+            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;  // the units of the chart dimensions
+
+        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?
+        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.auto = {
+            name: 'auto',
+            autorefresh: true,
+            force_update_at: 0, // the timestamp to force the update at
+            force_before_ms: null,
+            force_after_ms: null
+        };
+        this.pan = {
+            name: 'pan',
+            autorefresh: false,
+            force_update_at: 0, // the timestamp to force the update at
+            force_before_ms: null,
+            force_after_ms: null
+        };
+        this.zoom = {
+            name: 'zoom',
+            autorefresh: false,
+            force_update_at: 0, // the timestamp to force the update at
+            force_before_ms: null,
+            force_after_ms: null
+        };
+
+        // this is a pointer to one of the sub-classes below
+        // 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];
+
+        // milliseconds - the time the last refresh took
+        this.refresh_dt_ms = 0;
+
+        // 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
+
+        if(refresh_dt_element_name !== null)
+            this.refresh_dt_element = document.getElementById(refresh_dt_element_name) || null;
+        else
+            this.refresh_dt_element = null;
+
+        this.dimensions_visibility = new dimensionsVisibility(this);
+
+        this._updating = false;
+
+        // ============================================================================================================
+        // PRIVATE FUNCTIONS
+
+        var createDOM = 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 = '';
+
+            that.element.innerHTML = '';
+
+            that.element_message = document.createElement('div');
+            that.element_message.className = ' netdata-message 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);
+
+            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.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';
+
+                that.element_legend = null;
+            }
+            that.element_legend_childs.series = null;
+
+            if(typeof(that.width) === 'string')
+                $(that.element).css('width', that.width);
+            else if(typeof(that.width) === 'number')
+                $(that.element).css('width', that.width + 'px');
+
+            if(typeof(that.library.aspect_ratio) === 'undefined') {
+                if(typeof(that.height) === 'string')
+                    $(that.element).css('height', that.height);
+                else if(typeof(that.height) === 'number')
+                    $(that.element).css('height', that.height + '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).css('height', (that.element.offsetWidth * 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 = new Date().getTime();
+
+            showLoading();
+        };
+
+        /* init() private
+         * initialize state variables
+         * destroy all (possibly) created state elements
+         * create the basic DOM for a chart
+         */
+        var init = function() {
+            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
+
+            that.tm.last_initialized = new Date().getTime();
+            createDOM();
+
+            that.setMode('auto');
+        };
+
+        var maxMessageFontSize = function() {
+            // normally we want a font size, as tall as the element
+            var h = that.element_message.clientHeight;
+
+            // but give it some air, 20% let's say, or 5 pixels min
+            var lost = Math.max(h * 0.2, 5);
+            h -= lost;
+
+            // center the text, vertically
+            var paddingTop = (lost - 5) / 2;
+
+            // but check the width too
+            // it should fit 10 characters in it
+            var w = that.element_message.clientWidth / 10;
+            if(h > w) {
+                paddingTop += (h - w) / 2;
+                h = w;
+            }
+
+            // and don't make it too huge
+            // 5% of the screen size is good
+            if(h > screen.height / 20) {
+                paddingTop += (h - (screen.height / 20)) / 2;
+                h = screen.height / 20;
+            }
+
+            // set it
+            that.element_message.style.fontSize = h.toString() + 'px';
+            that.element_message.style.paddingTop = paddingTop.toString() + 'px';
+        };
+
+        var showMessage = function(msg) {
+            that.element_message.className = 'netdata-message';
+            that.element_message.innerHTML = msg;
+            that.element_message.style.fontSize = 'x-small';
+            that.element_message.style.paddingTop = '0px';
+            that.___messageHidden___ = undefined;
+        };
+
+        var showMessageIcon = function(icon) {
+            that.element_message.innerHTML = icon;
+            that.element_message.className = 'netdata-message icon';
+            maxMessageFontSize();
+            that.___messageHidden___ = undefined;
+        };
+
+        var hideMessage = function() {
+            if(typeof that.___messageHidden___ === 'undefined') {
+                that.___messageHidden___ = true;
+                that.element_message.className = 'netdata-message hidden';
+            }
+        };
+
+        var showRendering = function() {
+            var icon;
+            if(that.chart !== null) {
+                if(that.chart.chart_type === 'line')
+                    icon = '<i class="fa fa-line-chart"></i>';
+                else
+                    icon = '<i class="fa fa-area-chart"></i>';
+            }
+            else
+                icon = '<i class="fa fa-area-chart"></i>';
+
+            showMessageIcon(icon + ' netdata');
+        };
+
+        var showLoading = function() {
+            if(that.chart_created === false) {
+                showMessageIcon('<i class="fa fa-refresh"></i> netdata');
+                return true;
+            }
+            return false;
+        };
+
+        var isHidden = function() {
+            if(typeof that.___chartIsHidden___ !== 'undefined')
+                return true;
+
+            return false;
+        };
+
+        // hide the chart, when it is not visible - called from isVisible()
+        var hideChart = function() {
+            // hide it, if it is not already hidden
+            if(isHidden() === true) return;
+
+            if(that.chart_created === true) {
+                if(NETDATA.options.current.destroy_on_hide === true) {
+                    // we should destroy it
+                    init();
+                }
+                else {
+                    showRendering();
+                    that.element_chart.style.display = 'none';
+                    if(that.element_legend !== null) that.element_legend.style.display = 'none';
+                    that.tm.last_hidden = new Date().getTime();
+
+                    // de-allocate data
+                    // This works, but I not sure there are no corner cases somewhere
+                    // so it is commented - if the user has memory issues he can
+                    // set Destroy on Hide for all charts
+                    // that.data = null;
+                }
+            }
+
+            that.___chartIsHidden___ = true;
+        };
+
+        // unhide the chart, when it is visible - called from isVisible()
+        var unhideChart = function() {
+            if(isHidden() === false) return;
+
+            that.___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();
+            }
+            else {
+                that.tm.last_unhidden = new Date().getTime();
+                that.element_chart.style.display = '';
+                if(that.element_legend !== null) that.element_legend.style.display = '';
+                resizeChart();
+                hideMessage();
+            }
+        };
+
+        var canBeRendered = function() {
+            if(isHidden() === true || that.isVisible(true) === false)
+                return false;
+
+            return true;
+        };
+
+        // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
+        var callChartLibraryUpdateSafely = function(data) {
+            var status;
+
+            if(canBeRendered() === false)
+                return false;
+
+            if(NETDATA.options.debug.chart_errors === true)
+                status = that.library.update(that, data);
+            else {
+                try {
+                    status = that.library.update(that, data);
+                }
+                catch(err) {
+                    status = false;
+                }
+            }
+
+            if(status === false) {
+                error('chart failed to be updated as ' + that.library_name);
+                return false;
+            }
+
+            return true;
+        };
+
+        // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
+        var callChartLibraryCreateSafely = function(data) {
+            var status;
+
+            if(canBeRendered() === false)
+                return false;
+
+            if(NETDATA.options.debug.chart_errors === true)
+                status = that.library.create(that, data);
+            else {
+                try {
+                    status = that.library.create(that, data);
+                }
+                catch(err) {
+                    status = false;
+                }
+            }
+
+            if(status === false) {
+                error('chart failed to be created as ' + that.library_name);
+                return false;
+            }
+
+            that.chart_created = true;
+            that.updates_since_last_creation = 0;
+            return true;
+        };
+
+        // ----------------------------------------------------------------------------------------------------------------
+        // Chart Resize
+
+        // resizeChart() - private
+        // 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.chart_created === false) return;
+
+                if(that.needsRecreation()) {
+                    init();
+                }
+                else if(typeof that.library.resize === 'function') {
+                    that.library.resize(that);
+
+                    if(that.element_legend_childs.nano !== null && that.element_legend_childs.nano_options !== null)
+                        $(that.element_legend_childs.nano).nanoScroller();
+
+                    maxMessageFontSize();
+                }
+
+                that.tm.last_resized = new Date().getTime();
+            }
+        };
+
+        // this is the actual chart resize algorithm
+        // it will:
+        // - resize the entire container
+        // - update the internal states
+        // - resize the chart as the div changes height
+        // - update the scrollbar of the legend
+        var resizeChartToHeight = function(h) {
+            // console.log(h);
+            that.element.style.height = h;
+
+            if(that.settings_id !== null)
+                NETDATA.localStorageSet('chart_heights.' + that.settings_id, h);
+
+            var now = new Date().getTime();
+            NETDATA.options.last_page_scroll = now;
+            NETDATA.options.auto_refresher_stop_until = now + NETDATA.options.current.stop_updates_while_resizing;
+
+            // force a resize
+            that.tm.last_resized = 0;
+            resizeChart();
+        };
+
+        this.resizeHandler = function(e) {
+            e.preventDefault();
+
+            if(typeof this.event_resize === 'undefined'
+                || this.event_resize.chart_original_w === 'undefined'
+                || this.event_resize.chart_original_h === 'undefined')
+                this.event_resize = {
+                    chart_original_w: this.element.clientWidth,
+                    chart_original_h: this.element.clientHeight,
+                    last: 0
+                };
+
+            if(e.type === 'touchstart') {
+                this.event_resize.mouse_start_x = e.touches.item(0).pageX;
+                this.event_resize.mouse_start_y = e.touches.item(0).pageY;
+            }
+            else {
+                this.event_resize.mouse_start_x = e.clientX;
+                this.event_resize.mouse_start_y = e.clientY;
+            }
+
+            this.event_resize.chart_start_w = this.element.clientWidth;
+            this.event_resize.chart_start_h = this.element.clientHeight;
+            this.event_resize.chart_last_w = this.element.clientWidth;
+            this.event_resize.chart_last_h = this.element.clientHeight;
+
+            var now = new Date().getTime();
+            if(now - this.event_resize.last <= NETDATA.options.current.double_click_speed) {
+                // double click / double tap event
+
+                // the optimal height of the chart
+                // showing the entire legend
+                var optimal = this.event_resize.chart_last_h
+                        + this.element_legend_childs.content.scrollHeight
+                        - this.element_legend_childs.content.clientHeight;
+
+                // if we are not optimal, be optimal
+                if(this.event_resize.chart_last_h != optimal)
+                    resizeChartToHeight(optimal.toString() + 'px');
+
+                // else if we do not have the original height
+                // reset to the original height
+                else if(this.event_resize.chart_last_h != this.event_resize.chart_original_h)
+                    resizeChartToHeight(this.event_resize.chart_original_h.toString() + 'px');
+            }
+            else {
+                this.event_resize.last = now;
+
+                // process movement event
+                document.onmousemove =
+                document.ontouchmove =
+                this.element_legend_childs.resize_handler.onmousemove =
+                this.element_legend_childs.resize_handler.ontouchmove =
+                    function(e) {
+                        var y = null;
+
+                        switch(e.type) {
+                            case 'mousemove': y = e.clientY; break;
+                            case 'touchmove': y = e.touches.item(e.touches - 1).pageY; break;
+                        }
+
+                        if(y !== null) {
+                            var newH = that.event_resize.chart_start_h + y - that.event_resize.mouse_start_y;
+
+                            if(newH >= 70 && newH !== that.event_resize.chart_last_h) {
+                                resizeChartToHeight(newH.toString() + 'px');
+                                that.event_resize.chart_last_h = newH;
+                            }
+                        }
+                    };
+
+                // process end event
+                document.onmouseup =
+                document.ontouchend =
+                this.element_legend_childs.resize_handler.onmouseup =
+                this.element_legend_childs.resize_handler.ontouchend =
+                    function(e) {
+                        // remove all the hooks
+                        document.onmouseup =
+                        document.onmousemove =
+                        document.ontouchmove =
+                        document.ontouchend =
+                        that.element_legend_childs.resize_handler.onmousemove =
+                        that.element_legend_childs.resize_handler.ontouchmove =
+                        that.element_legend_childs.resize_handler.onmouseout =
+                        that.element_legend_childs.resize_handler.onmouseup =
+                        that.element_legend_childs.resize_handler.ontouchend =
+                            null;
+
+                        // allow auto-refreshes
+                        NETDATA.options.auto_refresher_stop_until = 0;
+                    };
+            }
+        };
+
+
+        var noDataToShow = function() {
+            showMessageIcon('<i class="fa fa-warning"></i> empty');
+            that.legendUpdateDOM();
+            that.tm.last_autorefreshed = new Date().getTime();
+            // 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;
+        };
+
+        // ============================================================================================================
+        // PUBLIC FUNCTIONS
+
+        this.error = function(msg) {
+            error(msg);
+        };
+
+        this.setMode = function(m) {
+            if(this.current !== null && this.current.name === m) return;
+
+            if(m === 'auto')
+                this.current = this.auto;
+            else if(m === 'pan')
+                this.current = this.pan;
+            else if(m === 'zoom')
+                this.current = this.zoom;
+            else
+                this.current = this.auto;
+
+            this.current.force_update_at = 0;
+            this.current.force_before_ms = null;
+            this.current.force_after_ms = null;
+
+            this.tm.last_mode_switch = new Date().getTime();
+        };
+
+        // ----------------------------------------------------------------------------------------------------------------
+        // global selection sync
+
+        // prevent to global selection sync for some time
+        this.globalSelectionSyncDelay = function(ms) {
+            if(NETDATA.options.current.sync_selection === false)
+                return;
+
+            if(typeof ms === 'number')
+                NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
+            else
+                NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
+        };
+
+        // can we globally apply selection sync?
+        this.globalSelectionSyncAbility = function() {
+            if(NETDATA.options.current.sync_selection === false)
+                return false;
+
+            if(NETDATA.globalSelectionSync.dont_sync_before > new Date().getTime())
+                return false;
+
+            return true;
+        };
+
+        this.globalSelectionSyncIsMaster = function() {
+            if(NETDATA.globalSelectionSync.state === this)
+                return true;
+            else
+                return false;
+        };
+
+        // this chart is the master of the global selection sync
+        this.globalSelectionSyncBeMaster = function() {
+            // am I the master?
+            if(this.globalSelectionSyncIsMaster()) {
+                if(this.debug === true)
+                    this.log('sync: I am the master already.');
+
+                return;
+            }
+
+            if(NETDATA.globalSelectionSync.state) {
+                if(this.debug === true)
+                    this.log('sync: I am not the sync master. Resetting global sync.');
+
+                this.globalSelectionSyncStop();
+            }
+
+            // become the master
+            if(this.debug === true)
+                this.log('sync: becoming sync master.');
+
+            this.selected = true;
+            NETDATA.globalSelectionSync.state = this;
+
+            // find the all slaves
+            var targets = NETDATA.options.targets;
+            var len = targets.length;
+            while(len--) {
+                st = targets[len];
+
+                if(st === this) {
+                    if(this.debug === true)
+                        st.log('sync: not adding me to sync');
+                }
+                else if(st.globalSelectionSyncIsEligible()) {
+                    if(this.debug === true)
+                        st.log('sync: adding to sync as slave');
+
+                    st.globalSelectionSyncBeSlave();
+                }
+            }
+
+            // this.globalSelectionSyncDelay(100);
+        };
+
+        // can the chart participate to the global selection sync as a slave?
+        this.globalSelectionSyncIsEligible = function() {
+            if(this.enabled === true
+                && this.library !== null
+                && typeof this.library.setSelection === 'function'
+                && this.isVisible() === true
+                && this.chart_created === true)
+                return true;
+
+            return false;
+        };
+
+        // this chart becomes a slave of the global selection sync
+        this.globalSelectionSyncBeSlave = function() {
+            if(NETDATA.globalSelectionSync.state !== this)
+                NETDATA.globalSelectionSync.slaves.push(this);
+        };
+
+        // sync all the visible charts to the given time
+        // this is to be called from the chart libraries
+        this.globalSelectionSync = function(t) {
+            if(this.globalSelectionSyncAbility() === false) {
+                if(this.debug === true)
+                    this.log('sync: cannot sync (yet?).');
+
+                return;
+            }
+
+            if(this.globalSelectionSyncIsMaster() === false) {
+                if(this.debug === true)
+                    this.log('sync: trying to be sync master.');
+
+                this.globalSelectionSyncBeMaster();
+
+                if(this.globalSelectionSyncAbility() === false) {
+                    if(this.debug === true)
+                        this.log('sync: cannot sync (yet?).');
+
+                    return;
+                }
+            }
+
+            NETDATA.globalSelectionSync.last_t = t;
+            $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
+                st.setSelection(t);
+            });
+        };
+
+        // stop syncing all charts to the given time
+        this.globalSelectionSyncStop = function() {
+            if(NETDATA.globalSelectionSync.slaves.length) {
+                if(this.debug === true)
+                    this.log('sync: cleaning up...');
+
+                $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
+                    if(st === that) {
+                        if(that.debug === true)
+                            st.log('sync: not adding me to sync stop');
+                    }
+                    else {
+                        if(that.debug === true)
+                            st.log('sync: removed slave from sync');
+
+                        st.clearSelection();
+                    }
+                });
+
+                NETDATA.globalSelectionSync.last_t = 0;
+                NETDATA.globalSelectionSync.slaves = [];
+                NETDATA.globalSelectionSync.state = null;
+            }
+
+            this.clearSelection();
+        };
+
+        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(this.selected === true && this.debug === true)
+                this.log('selection set to ' + t.toString());
+
+            return this.selected;
+        };
+
+        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(this.selected === false && this.debug === true)
+                    this.log('selection cleared');
+
+                this.legendReset();
+            }
+
+            return this.selected;
+        };
+
+        // 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;
+        };
+
+        this.calculateRowForTime = function(t) {
+            if(this.timeIsVisible(t) === false) return -1;
+            return Math.floor((t - this.data_after) / this.data_update_every);
+        };
+
+        // ----------------------------------------------------------------------------------------------------------------
+
+        // 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)
+                    this.log('pauseChart()');
+
+                this.paused = true;
+            }
+        };
+
+        this.unpauseChart = function() {
+            if(this.paused === true) {
+                if(this.debug === true)
+                    this.log('unpauseChart()');
+
+                this.paused = false;
+            }
+        };
+
+        this.resetChart = function(dont_clear_master, dont_update) {
+            if(this.debug === true)
+                this.log('resetChart(' + dont_clear_master + ', ' + dont_update + ') called');
+
+            if(typeof dont_clear_master === 'undefined')
+                dont_clear_master = false;
+
+            if(typeof dont_update === 'undefined')
+                dont_update = false;
+
+            if(dont_clear_master !== true && NETDATA.globalPanAndZoom.isMaster(this) === true) {
+                if(this.debug === true)
+                    this.log('resetChart() diverting to clearMaster().');
+                // this will call us back with master === true
+                NETDATA.globalPanAndZoom.clearMaster();
+                return;
+            }
+
+            this.clearSelection();
+
+            this.tm.pan_and_zoom_seq = 0;
+
+            this.setMode('auto');
+            this.current.force_update_at = 0;
+            this.current.force_before_ms = null;
+            this.current.force_after_ms = null;
+            this.tm.last_autorefreshed = 0;
+            this.paused = false;
+            this.selected = false;
+            this.enabled = true;
+            // this.debug = false;
+
+            // do not update the chart here
+            // or the chart will flip-flop when it is the master
+            // of a selection sync and another chart becomes
+            // the new master
+
+            if(dont_update !== true && this.isVisible() === true) {
+                this.updateChart();
+            }
+        };
+
+        this.updateChartPanOrZoom = function(after, before) {
+            var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
+            var ret = true;
+
+            if(this.debug === true)
+                this.log(logme);
+
+            if(before < after) {
+                if(this.debug === true)
+                    this.log(logme + 'flipped parameters, rejecting it.');
+
+                return false;
+            }
+
+            if(typeof this.fixed_min_duration === 'undefined')
+                this.fixed_min_duration = Math.round((this.chartWidth() / 30) * this.chart.update_every * 1000);
+
+            var min_duration = this.fixed_min_duration;
+            var current_duration = Math.round(this.view_before - this.view_after);
+
+            // round the numbers
+            after = Math.round(after);
+            before = Math.round(before);
+
+            // align them to update_every
+            // stretching them further away
+            after -= after % this.data_update_every;
+            before += this.data_update_every - (before % this.data_update_every);
+
+            // the final wanted duration
+            var wanted_duration = before - after;
+
+            // to allow panning, accept just a point below our minimum
+            if((current_duration - this.data_update_every) < min_duration)
+                min_duration = current_duration - this.data_update_every;
+
+            // we do it, but we adjust to minimum size and return false
+            // when the wanted size is below the current and the minimum
+            // and we zoom
+            if(wanted_duration < current_duration && wanted_duration < min_duration) {
+                if(this.debug === true)
+                    this.log(logme + 'too small: min_duration: ' + (min_duration / 1000).toString() + ', wanted: ' + (wanted_duration / 1000).toString());
+
+                min_duration = this.fixed_min_duration;
+
+                var dt = (min_duration - wanted_duration) / 2;
+                before += dt;
+                after -= dt;
+                wanted_duration = before - after;
+                ret = false;
+            }
+
+            var tolerance = this.data_update_every * 2;
+            var movement = Math.abs(before - this.view_before);
+
+            if(Math.abs(current_duration - wanted_duration) <= tolerance && movement <= tolerance && ret === true) {
+                if(this.debug === true)
+                    this.log(logme + 'REJECTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + false);
+                return false;
+            }
+
+            if(this.current.name === 'auto') {
+                this.log(logme + 'caller called me with mode: ' + this.current.name);
+                this.setMode('pan');
+            }
+
+            if(this.debug === true)
+                this.log(logme + 'ACCEPTING UPDATE: current/min duration: ' + (current_duration / 1000).toString() + '/' + (this.fixed_min_duration / 1000).toString() + ', wanted duration: ' + (wanted_duration / 1000).toString() + ', duration diff: ' + (Math.round(Math.abs(current_duration - wanted_duration) / 1000)).toString() + ', movement: ' + (movement / 1000).toString() + ', tolerance: ' + (tolerance / 1000).toString() + ', returning: ' + ret);
+
+            this.current.force_update_at = new Date().getTime() + NETDATA.options.current.pan_and_zoom_delay;
+            this.current.force_after_ms = after;
+            this.current.force_before_ms = before;
+            NETDATA.globalPanAndZoom.setMaster(this, after, before);
+            return ret;
+        };
+
+        this.legendFormatValue = function(value) {
+            if(value === null || value === 'undefined') return '-';
+            if(typeof value !== 'number') return value;
+
+            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();
+        };
+
+        this.legendSetLabelValue = function(label, value) {
+            var series = this.element_legend_childs.series[label];
+            if(typeof series === 'undefined') return;
+            if(series.value === null && series.user === null) return;
+
+            // if the value has not changed, skip DOM update
+            //if(series.last === value) return;
+
+            var s, r;
+            if(typeof value === 'number') {
+                var v = Math.abs(value);
+                s = r = this.legendFormatValue(value);
+
+                if(typeof series.last === 'number') {
+                    if(v > series.last) s += '<i class="fa fa-angle-up" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                    else if(v < series.last) s += '<i class="fa fa-angle-down" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                    else s += '<i class="fa fa-angle-left" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                }
+                else s += '<i class="fa fa-angle-right" style="width: 8px; text-align: center; overflow: hidden; vertical-align: middle;"></i>';
+                series.last = v;
+            }
+            else {
+                s = r = value;
+                series.last = value;
+            }
+
+            if(series.value !== null) series.value.innerHTML = s;
+            if(series.user !== null) series.user.innerHTML = r;
+        };
+
+        this.legendSetDate = function(ms) {
+            if(typeof ms !== 'number') {
+                this.legendShowUndefined();
+                return;
+            }
+
+            var d = new Date(ms);
+
+            if(this.element_legend_childs.title_date)
+                this.element_legend_childs.title_date.innerHTML = d.toLocaleDateString();
+
+            if(this.element_legend_childs.title_time)
+                this.element_legend_childs.title_time.innerHTML = d.toLocaleTimeString();
+
+            if(this.element_legend_childs.title_units)
+                this.element_legend_childs.title_units.innerHTML = this.units;
+        };
+
+        this.legendShowUndefined = function() {
+            if(this.element_legend_childs.title_date)
+                this.element_legend_childs.title_date.innerHTML = '&nbsp;';
+
+            if(this.element_legend_childs.title_time)
+                this.element_legend_childs.title_time.innerHTML = this.chart.name;
+
+            if(this.element_legend_childs.title_units)
+                this.element_legend_childs.title_units.innerHTML = '&nbsp;';
+
+            if(this.data && this.element_legend_childs.series !== null) {
+                var labels = this.data.dimension_names;
+                var i = labels.length;
+                while(i--) {
+                    var label = labels[i];
+
+                    if(typeof label === 'undefined') continue;
+                    if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
+                    this.legendSetLabelValue(label, null);
+                }
+            }
+        };
+
+        this.legendShowLatestValues = function() {
+            if(this.chart === null) return;
+            if(this.selected) return;
+
+            if(this.data === null || this.element_legend_childs.series === null) {
+                this.legendShowUndefined();
+                return;
+            }
+
+            var show_undefined = true;
+            if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every)
+                show_undefined = false;
+
+            if(show_undefined) {
+                this.legendShowUndefined();
+                return;
+            }
+
+            this.legendSetDate(this.view_before);
+
+            var labels = this.data.dimension_names;
+            var i = labels.length;
+            while(i--) {
+                var label = labels[i];
+
+                if(typeof label === 'undefined') continue;
+                if(typeof this.element_legend_childs.series[label] === 'undefined') continue;
+
+                if(show_undefined)
+                    this.legendSetLabelValue(label, null);
+                else
+                    this.legendSetLabelValue(label, this.data.view_latest_values[i]);
+            }
+        };
+
+        this.legendReset = function() {
+            this.legendShowLatestValues();
+        };
+
+        // this should be called just ONCE per dimension per chart
+        this._chartDimensionColor = function(label) {
+            if(this.colors === null) this.chartColors();
+
+            if(typeof this.colors_assigned[label] === 'undefined') {
+                if(this.colors_available.length === 0) {
+                    for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
+                        this.colors_available.push(NETDATA.themes.current.colors[i]);
+                }
+
+                this.colors_assigned[label] = this.colors_available.shift();
+
+                if(this.debug === true)
+                    this.log('label "' + label + '" got color "' + this.colors_assigned[label]);
+            }
+            else {
+                if(this.debug === true)
+                    this.log('label "' + label + '" already has color "' + this.colors_assigned[label] + '"');
+            }
+
+            this.colors.push(this.colors_assigned[label]);
+            return this.colors_assigned[label];
+        };
+
+        this.chartColors = function() {
+            if(this.colors !== null) return this.colors;
+
+            this.colors = new Array();
+            this.colors_available = new Array();
+            var i, len;
+
+            var c = $(this.element).data('colors');
+            // 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) {
+                        for(i = 0, len = c.length; i < len ; i++) {
+                            added++;
+                            this.colors_available.push(c[i]);
+                            // this.log('adding color: ' + c[i]);
+                        }
+                    }
+                }
+            }
+
+            // push all the standard colors too
+            for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
+                this.colors_available.push(NETDATA.themes.current.colors[i]);
+
+            return this.colors;
+        };
+
+        this.legendUpdateDOM = function() {
+            var needed = false;
+
+            // check that the legend DOM is up to date for the downloaded dimensions
+            if(typeof this.element_legend_childs.series !== 'object' || this.element_legend_childs.series === null) {
+                // this.log('the legend does not have any series - requesting legend update');
+                needed = true;
+            }
+            else if(this.data === null) {
+                // this.log('the chart does not have any data - requesting legend update');
+                needed = true;
+            }
+            else if(typeof this.element_legend_childs.series.labels_key === 'undefined') {
+                needed = true;
+            }
+            else {
+                var labels = this.data.dimension_names.toString();
+                if(labels !== this.element_legend_childs.series.labels_key) {
+                    needed = true;
+
+                    if(this.debug === true)
+                        this.log('NEW LABELS: "' + labels + '" NOT EQUAL OLD LABELS: "' + this.element_legend_childs.series.labels_key + '"');
+                }
+            }
+
+            if(needed === false) {
+                // make sure colors available
+                this.chartColors();
+
+                // do we have to update the current values?
+                // we do this, only when the visible chart is current
+                if(Math.abs(this.netdata_last - this.view_before) <= this.data_update_every) {
+                    if(this.debug === true)
+                        this.log('chart is in latest position... updating values on legend...');
+
+                    //var labels = this.data.dimension_names;
+                    //var i = labels.length;
+                    //while(i--)
+                    //  this.legendSetLabelValue(labels[i], this.data.latest_values[i]);
+                }
+                return;
+            }
+            if(this.colors === null) {
+                // this is the first time we update the chart
+                // let's assign colors to all dimensions
+                if(this.library.track_colors() === true)
+                    for(var dim in this.chart.dimensions)
+                        this._chartDimensionColor(this.chart.dimensions[dim].name);
+            }
+            // we will re-generate the colors for the chart
+            // based on the selected dimensions
+            this.colors = null;
+
+            if(this.debug === true)
+                this.log('updating Legend DOM');
+
+            // mark all dimensions as invalid
+            this.dimensions_visibility.invalidateAll();
+
+            var genLabel = function(state, parent, dim, name, count) {
+                var color = state._chartDimensionColor(name);
+
+                var user_element = null;
+                var user_id = self.data('show-value-of-' + dim + '-at') || null;
+                if(user_id !== null) {
+                    user_element = document.getElementById(user_id) || null;
+                    if(user_element === null)
+                        state.log('Cannot find element with id: ' + user_id);
+                }
+
+                state.element_legend_childs.series[name] = {
+                    name: document.createElement('span'),
+                    value: document.createElement('span'),
+                    user: user_element,
+                    last: null
+                };
+
+                var label = state.element_legend_childs.series[name];
+
+                // create the dimension visibility tracking for this label
+                state.dimensions_visibility.dimensionAdd(name, label.name, label.value, color);
+
+                var rgb = NETDATA.colorHex2Rgb(color);
+                label.name.innerHTML = '<table class="netdata-legend-name-table-'
+                    + state.chart.chart_type
+                    + '" style="background-color: '
+                    + 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + NETDATA.options.current['color_fill_opacity_' + state.chart.chart_type] + ')'
+                    + '"><tr class="netdata-legend-name-tr"><td class="netdata-legend-name-td"></td></tr></table>'
+
+                var text = document.createTextNode(' ' + name);
+                label.name.appendChild(text);
+
+                if(count > 0)
+                    parent.appendChild(document.createElement('br'));
+
+                parent.appendChild(label.name);
+                parent.appendChild(label.value);
+            };
+
+            var content = document.createElement('div');
+
+            if(this.hasLegend()) {
+                this.element_legend_childs = {
+                    content: content,
+                    resize_handler: document.createElement('div'),
+                    toolbox: document.createElement('div'),
+                    toolbox_left: document.createElement('div'),
+                    toolbox_right: document.createElement('div'),
+                    toolbox_reset: document.createElement('div'),
+                    toolbox_zoomin: document.createElement('div'),
+                    toolbox_zoomout: document.createElement('div'),
+                    toolbox_volume: document.createElement('div'),
+                    title_date: document.createElement('span'),
+                    title_time: document.createElement('span'),
+                    title_units: document.createElement('span'),
+                    nano: document.createElement('div'),
+                    nano_options: {
+                        paneClass: 'netdata-legend-series-pane',
+                        sliderClass: 'netdata-legend-series-slider',
+                        contentClass: 'netdata-legend-series-content',
+                        enabledClass: '__enabled',
+                        flashedClass: '__flashed',
+                        activeClass: '__active',
+                        tabIndex: -1,
+                        alwaysVisible: true,
+                        sliderMinHeight: 10
+                    },
+                    series: {}
+                };
+
+                this.element_legend.innerHTML = '';
+
+                if(this.library.toolboxPanAndZoom !== null) {
+
+                    function get_pan_and_zoom_step(event) {
+                        if (event.ctrlKey)
+                            return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
+
+                        else if (event.shiftKey)
+                            return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
+
+                        else if (event.altKey)
+                            return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
+
+                        else
+                            return NETDATA.options.current.pan_and_zoom_factor;
+                    }
+
+                    this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
+                    this.element.appendChild(this.element_legend_childs.toolbox);
+
+                    this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
+                    this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
+                    this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
+                    this.element_legend_childs.toolbox_left.onclick = function(e) {
+                        e.preventDefault();
+
+                        var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
+                        var before = that.view_before - step;
+                        var after = that.view_after - step;
+                        if(after >= that.netdata_first)
+                            that.library.toolboxPanAndZoom(that, after, before);
+                    };
+                    if(NETDATA.options.current.show_help === true)
+                        $(this.element_legend_childs.toolbox_left).popover({
+                        container: "body",
+                        animation: false,
+                        html: true,
+                        trigger: 'hover',
+                        placement: 'bottom',
+                        delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                        title: 'Pan Left',
+                        content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+                    });
+
+
+                    this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
+                    this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
+                    this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
+                    this.element_legend_childs.toolbox_reset.onclick = function(e) {
+                        e.preventDefault();
+                        NETDATA.resetAllCharts(that);
+                    };
+                    if(NETDATA.options.current.show_help === true)
+                        $(this.element_legend_childs.toolbox_reset).popover({
+                        container: "body",
+                        animation: false,
+                        html: true,
+                        trigger: 'hover',
+                        placement: 'bottom',
+                        delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                        title: 'Chart Reset',
+                        content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+                    });
+                    
+                    this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
+                    this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
+                    this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
+                    this.element_legend_childs.toolbox_right.onclick = function(e) {
+                        e.preventDefault();
+                        var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
+                        var before = that.view_before + step;
+                        var after = that.view_after + step;
+                        if(before <= that.netdata_last)
+                            that.library.toolboxPanAndZoom(that, after, before);
+                    };
+                    if(NETDATA.options.current.show_help === true)
+                        $(this.element_legend_childs.toolbox_right).popover({
+                        container: "body",
+                        animation: false,
+                        html: true,
+                        trigger: 'hover',
+                        placement: 'bottom',
+                        delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                        title: 'Pan Right',
+                        content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+                    });
+
+                    
+                    this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
+                    this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
+                    this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
+                    this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
+                        e.preventDefault();
+                        var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
+                        var before = that.view_before - dt;
+                        var after = that.view_after + dt;
+                        that.library.toolboxPanAndZoom(that, after, before);
+                    };
+                    if(NETDATA.options.current.show_help === true)
+                        $(this.element_legend_childs.toolbox_zoomin).popover({
+                        container: "body",
+                        animation: false,
+                        html: true,
+                        trigger: 'hover',
+                        placement: 'bottom',
+                        delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                        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.<br/><small>Help, can be disabled from the settings.</small>'
+                    });
+                    
+                    this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
+                    this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
+                    this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
+                    this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
+                        e.preventDefault();
+                        var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
+                        var before = that.view_before + dt;
+                        var after = that.view_after - dt;
+
+                        that.library.toolboxPanAndZoom(that, after, before);
+                    };
+                    if(NETDATA.options.current.show_help === true)
+                        $(this.element_legend_childs.toolbox_zoomout).popover({
+                        container: "body",
+                        animation: false,
+                        html: true,
+                        trigger: 'hover',
+                        placement: 'bottom',
+                        delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                        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.<br/><small>Help, can be disabled from the settings.</small>'
+                    });
+                    
+                    //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
+                    //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
+                    //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
+                    //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
+                    //this.element_legend_childs.toolbox_volume.onclick = function(e) {
+                        //e.preventDefault();
+                        //alert('clicked toolbox_volume on ' + that.id);
+                    //}
+                }
+                else {
+                    this.element_legend_childs.toolbox = null;
+                    this.element_legend_childs.toolbox_left = null;
+                    this.element_legend_childs.toolbox_reset = null;
+                    this.element_legend_childs.toolbox_right = null;
+                    this.element_legend_childs.toolbox_zoomin = null;
+                    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 = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
+                this.element.appendChild(this.element_legend_childs.resize_handler);
+                if(NETDATA.options.current.show_help === true)
+                    $(this.element_legend_childs.resize_handler).popover({
+                    container: "body",
+                    animation: false,
+                    html: true,
+                    trigger: 'hover',
+                    placement: 'bottom',
+                    delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+                    title: 'Chart Resize',
+                    content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
+                });
+
+                // mousedown event
+                this.element_legend_childs.resize_handler.onmousedown =
+                    function(e) {
+                        that.resizeHandler(e);
+                    };
+
+                // touchstart event
+                this.element_legend_childs.resize_handler.addEventListener('touchstart', function(e) {
+                    that.resizeHandler(e);
+                }, false);
+
+                this.element_legend_childs.title_date.className += " netdata-legend-title-date";
+                this.element_legend.appendChild(this.element_legend_childs.title_date);
+
+                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.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.element_legend.appendChild(document.createElement('br'));
+
+                this.element_legend_childs.nano.className = 'netdata-legend-series';
+                this.element_legend.appendChild(this.element_legend_childs.nano);
+
+                content.className = 'netdata-legend-series-content';
+                this.element_legend_childs.nano.appendChild(content);
+
+                if(NETDATA.options.current.show_help === true)
+                    $(content).popover({
+                    container: "body",
+                    animation: false,
+                    html: true,
+                    trigger: 'hover',
+                    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.<br/><small>Help, can be disabled from the settings.</small>'
+                });
+            }
+            else {
+                this.element_legend_childs = {
+                    content: content,
+                    resize_handler: null,
+                    toolbox: null,
+                    toolbox_left: null,
+                    toolbox_right: null,
+                    toolbox_reset: null,
+                    toolbox_zoomin: null,
+                    toolbox_zoomout: null,
+                    toolbox_volume: null,
+                    title_date: null,
+                    title_time: null,
+                    title_units: null,
+                    nano: null,
+                    nano_options: null,
+                    series: {}
+                };
+            }
+
+            if(this.data) {
+                this.element_legend_childs.series.labels_key = this.data.dimension_names.toString();
+                if(this.debug === true)
+                    this.log('labels from data: "' + this.element_legend_childs.series.labels_key + '"');
+
+                for(var i = 0, len = this.data.dimension_names.length; i < len ;i++) {
+                    genLabel(this, content, this.data.dimension_ids[i], this.data.dimension_names[i], i);
+                }
+            }
+            else {
+                var tmp = new Array();
+                for(var dim in this.chart.dimensions) {
+                    tmp.push(this.chart.dimensions[dim].name);
+                    genLabel(this, content, dim, this.chart.dimensions[dim].name, i);
+                }
+                this.element_legend_childs.series.labels_key = tmp.toString();
+                if(this.debug === true)
+                    this.log('labels from chart: "' + this.element_legend_childs.series.labels_key + '"');
+            }
+
+            // create a hidden div to be used for hidding
+            // the original legend of the chart library
+            var el = document.createElement('div');
+            if(this.element_legend !== null)
+                this.element_legend.appendChild(el);
+            el.style.display = 'none';
+
+            this.element_legend_childs.hidden = document.createElement('div');
+            el.appendChild(this.element_legend_childs.hidden);
+
+            if(this.element_legend_childs.nano !== null && this.element_legend_childs.nano_options !== null)
+                $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
+
+            this.legendShowLatestValues();
+        };
+
+        this.hasLegend = function() {
+            if(typeof this.___hasLegendCache___ !== 'undefined')
+                return this.___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;
+            }
+
+            this.___hasLegendCache___ = leg;
+            return leg;
+        };
+
+        this.legendWidth = function() {
+            return (this.hasLegend())?140:0;
+        };
+
+        this.legendHeight = function() {
+            return $(this.element).height();
+        };
+
+        this.chartWidth = function() {
+            return $(this.element).width() - this.legendWidth();
+        };
+
+        this.chartHeight = function() {
+            return $(this.element).height();
+        };
+
+        this.chartPixelsPerPoint = function() {
+            // force an options provided detail
+            var px = this.pixels_per_point;
+
+            if(this.library && px < this.library.pixels_per_point(this))
+                px = this.library.pixels_per_point(this);
+
+            if(px < NETDATA.options.current.pixels_per_point)
+                px = NETDATA.options.current.pixels_per_point;
+
+            return px;
+        };
+
+        this.needsRecreation = function() {
+            return (
+                    this.chart_created === true
+                    && this.library
+                    && this.library.autoresize() === false
+                    && this.tm.last_resized < NETDATA.options.last_resized
+                );
+        };
+
+        this.chartURL = function() {
+            var after, before, points_multiplier = 1;
+            if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
+                this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
+
+                after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
+                before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
+                this.view_after = after * 1000;
+                this.view_before = before * 1000;
+
+                this.requested_padding = null;
+                points_multiplier = 1;
+            }
+            else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
+                this.tm.pan_and_zoom_seq = 0;
+
+                before = Math.round(this.current.force_before_ms / 1000);
+                after  = Math.round(this.current.force_after_ms / 1000);
+                this.view_after = after * 1000;
+                this.view_before = before * 1000;
+
+                if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
+                    this.requested_padding = Math.round((before - after) / 2);
+                    after -= this.requested_padding;
+                    before += this.requested_padding;
+                    this.requested_padding *= 1000;
+                    points_multiplier = 2;
+                }
+
+                this.current.force_before_ms = null;
+                this.current.force_after_ms = null;
+            }
+            else {
+                this.tm.pan_and_zoom_seq = 0;
+
+                before = this.before;
+                after  = this.after;
+                this.view_after = after * 1000;
+                this.view_before = before * 1000;
+
+                this.requested_padding = null;
+                points_multiplier = 1;
+            }
+
+            this.requested_after = after * 1000;
+            this.requested_before = before * 1000;
+
+            this.data_points = this.points || Math.round(this.chartWidth() / this.chartPixelsPerPoint());
+
+            // build the data URL
+            this.data_url = this.host + this.chart.data_url;
+            this.data_url += "&format="  + this.library.format();
+            this.data_url += "&points="  + (this.data_points * points_multiplier).toString();
+            this.data_url += "&group="   + this.method;
+            this.data_url += "&options=" + this.library.options(this);
+            this.data_url += '|jsonwrap';
+
+            if(NETDATA.options.current.eliminate_zero_dimensions === true)
+                this.data_url += '|nonzero';
+
+            if(this.append_options !== null)
+                this.data_url += '|' + this.append_options.toString();
+
+            if(after)
+                this.data_url += "&after="  + after.toString();
+
+            if(before)
+                this.data_url += "&before=" + before.toString();
+
+            if(this.dimensions)
+                this.data_url += "&dimensions=" + this.dimensions;
+
+            if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
+                this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
+        };
+
+        this.redrawChart = function() {
+            if(this.data !== null)
+                this.updateChartWithData(this.data);
+        };
+
+        this.updateChartWithData = function(data) {
+            if(this.debug === true)
+                this.log('updateChartWithData() called.');
+
+            // this may force the chart to be re-created
+            resizeChart();
+
+            this.data = data;
+            this.updates_counter++;
+            this.updates_since_last_unhide++;
+            this.updates_since_last_creation++;
+
+            var started = new Date().getTime();
+
+            // if the result is JSON, find the latest update-every
+            this.data_update_every = data.view_update_every * 1000;
+            this.data_after = data.after * 1000;
+            this.data_before = data.before * 1000;
+            this.netdata_first = data.first_entry * 1000;
+            this.netdata_last = data.last_entry * 1000;
+            this.data_points = data.points;
+            data.state = this;
+
+            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);
+                    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);
+                    this.view_before = this.data_before;
+                }
+            }
+            else {
+                this.view_after = this.data_after;
+                this.view_before = this.data_before;
+            }
+
+            if(this.debug === true) {
+                this.log('UPDATE No ' + this.updates_counter + ' COMPLETED');
+
+                if(this.current.force_after_ms)
+                    this.log('STATUS: forced    : ' + (this.current.force_after_ms / 1000).toString() + ' - ' + (this.current.force_before_ms / 1000).toString());
+                else
+                    this.log('STATUS: forced    : unset');
+
+                this.log('STATUS: requested : ' + (this.requested_after / 1000).toString() + ' - ' + (this.requested_before / 1000).toString());
+                this.log('STATUS: downloaded: ' + (this.data_after / 1000).toString() + ' - ' + (this.data_before / 1000).toString());
+                this.log('STATUS: rendered  : ' + (this.view_after / 1000).toString() + ' - ' + (this.view_before / 1000).toString());
+                this.log('STATUS: points    : ' + (this.data_points).toString());
+            }
+
+            if(this.data_points === 0) {
+                noDataToShow();
+                return;
+            }
+
+            if(this.updates_since_last_creation >= this.library.max_updates_to_recreate()) {
+                if(this.debug === true)
+                    this.log('max updates of ' + this.updates_since_last_creation.toString() + ' reached. Forcing re-generation.');
+
+                this.chart_created = false;
+            }
+
+            // check and update the legend
+            this.legendUpdateDOM();
+
+            if(this.chart_created === true
+                && typeof this.library.update === 'function') {
+
+                if(this.debug === true)
+                    this.log('updating chart...');
+
+                if(callChartLibraryUpdateSafely(data) === false)
+                    return;
+            }
+            else {
+                if(this.debug === true)
+                    this.log('creating chart...');
+
+                if(callChartLibraryCreateSafely(data) === false)
+                    return;
+            }
+            hideMessage();
+            this.legendShowLatestValues();
+            if(this.selected === true)
+                NETDATA.globalSelectionSync.stop();
+
+            // update the performance counters
+            var now = new Date().getTime();
+            this.tm.last_updated = now;
+
+            // don't update last_autorefreshed if this chart is
+            // forced to be updated with global PanAndZoom
+            if(NETDATA.globalPanAndZoom.isActive())
+                this.tm.last_autorefreshed = 0;
+            else {
+                if(NETDATA.options.current.parallel_refresher === true && NETDATA.options.current.concurrent_refreshes === true)
+                    this.tm.last_autorefreshed = now - (now % this.data_update_every);
+                else
+                    this.tm.last_autorefreshed = now;
+            }
+
+            this.refresh_dt_ms = now - started;
+            NETDATA.options.auto_refresher_fast_weight += this.refresh_dt_ms;
+
+            if(this.refresh_dt_element !== null)
+                this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
+        };
+
+        this.updateChart = function(callback) {
+            if(this.debug === true)
+                this.log('updateChart() called.');
+
+            if(this._updating === true) {
+                if(this.debug === true)
+                    this.log('I am already updating...');
+
+                if(typeof callback === 'function') callback();
+                return false;
+            }
+
+            // due to late initialization of charts and libraries
+            // we need to check this too
+            if(this.enabled === false) {
+                if(this.debug === true)
+                    this.log('I am not enabled');
+
+                if(typeof callback === 'function') callback();
+                return false;
+            }
+
+            if(canBeRendered() === false) {
+                if(typeof callback === 'function') callback();
+                return false;
+            }
+
+            if(this.chart === null) {
+                this.getChart(function() { that.updateChart(callback); });
+                return false;
+            }
+
+            if(this.library.initialized === false) {
+                if(this.library.enabled === true) {
+                    this.library.initialize(function() { that.updateChart(callback); });
+                    return false;
+                }
+                else {
+                    error('chart library "' + this.library_name + '" is not available.');
+                    if(typeof callback === 'function') callback();
+                    return false;
+                }
+            }
+
+            this.clearSelection();
+            this.chartURL();
+
+            if(this.debug === true)
+                this.log('updating from ' + this.data_url);
+
+            NETDATA.statistics.refreshes_total++;
+            NETDATA.statistics.refreshes_active++;
+
+            if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
+                NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
+
+            this._updating = true;
+
+            this.xhr = $.ajax( {
+                url: this.data_url,
+                cache: false,
+                async: true,
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .success(function(data) {
+                if(that.debug === true)
+                    that.log('data received. updating chart.');
+
+                that.updateChartWithData(data);
+            })
+            .fail(function() {
+                error('data download failed for url: ' + that.data_url);
+            })
+            .always(function() {
+                NETDATA.statistics.refreshes_active--;
+                that._updating = false;
+                if(typeof callback === 'function') callback();
+            });
+
+            return true;
+        };
+
+        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___;
+
+            this.tm.last_visible_check = new Date().getTime();
+
+            var wh = window.innerHeight;
+            var x = this.element.getBoundingClientRect();
+            var ret = 0;
+            var tolerance = 0;
+
+            if(x.width === 0 || x.height === 0) {
+                hideChart();
+                this.___isVisible___ = false;
+                return this.___isVisible___;
+            }
+
+            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;
+            }
+
+            if(ret > tolerance) {
+                // the chart is too far
+
+                hideChart();
+                this.___isVisible___ = false;
+                return this.___isVisible___;
+            }
+            else {
+                // the chart is inside or very close
+
+                unhideChart();
+                this.___isVisible___ = true;
+                return this.___isVisible___;
+            }
+        };
+
+        this.isAutoRefreshable = function() {
+            return (this.current.autorefresh);
+        };
+
+        this.canBeAutoRefreshed = function() {
+            var now = new Date().getTime();
+
+            if(this.running === true) {
+                if(this.debug === true)
+                    this.log('I am already running');
+
+                return false;
+            }
+
+            if(this.enabled === false) {
+                if(this.debug === true)
+                    this.log('I am not enabled');
+
+                return false;
+            }
+
+            if(this.library === null || this.library.enabled === false) {
+                error('charting library "' + this.library_name + '" is not available');
+                if(this.debug === true)
+                    this.log('My chart library ' + this.library_name + ' is not available');
+
+                return false;
+            }
+
+            if(this.isVisible() === false) {
+                if(NETDATA.options.debug.visibility === true || this.debug === true)
+                    this.log('I am not visible');
+
+                return false;
+            }
+
+            if(this.current.force_update_at !== 0 && this.current.force_update_at < now) {
+                if(this.debug === true)
+                    this.log('timed force update detected - allowing this update');
+
+                this.current.force_update_at = 0;
+                return true;
+            }
+
+            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');
+
+                    return false;
+                }
+
+                if(this.needsRecreation() === true) {
+                    if(this.debug === true)
+                        this.log('canBeAutoRefreshed(): needs re-creation.');
+
+                    return true;
+                }
+
+                // options valid only for autoRefresh()
+                if(NETDATA.options.auto_refresher_stop_until === 0 || NETDATA.options.auto_refresher_stop_until < now) {
+                    if(NETDATA.globalPanAndZoom.isActive()) {
+                        if(NETDATA.globalPanAndZoom.shouldBeAutoRefreshed(this)) {
+                            if(this.debug === true)
+                                this.log('canBeAutoRefreshed(): global panning: I need an update.');
+
+                            return true;
+                        }
+                        else {
+                            if(this.debug === true)
+                                this.log('canBeAutoRefreshed(): global panning: I am already up to date.');
+
+                            return false;
+                        }
+                    }
+
+                    if(this.selected === true) {
+                        if(this.debug === true)
+                            this.log('canBeAutoRefreshed(): I have a selection in place.');
+
+                        return false;
+                    }
+
+                    if(this.paused === true) {
+                        if(this.debug === true)
+                            this.log('canBeAutoRefreshed(): I am paused.');
+
+                        return false;
+                    }
+
+                    if(now - this.tm.last_autorefreshed >= this.data_update_every) {
+                        if(this.debug === true)
+                            this.log('canBeAutoRefreshed(): It is time to update me.');
+
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        };
+
+        this.autoRefresh = function(callback) {
+            if(this.canBeAutoRefreshed() === true && this.running === false) {
+                var state = this;
+
+                state.running = true;
+                state.updateChart(function() {
+                    state.running = false;
+
+                    if(typeof callback !== 'undefined')
+                        callback();
+                });
+            }
+            else {
+                if(typeof callback !== 'undefined')
+                    callback();
+            }
+        };
+
+        this._defaultsFromDownloadedChart = function(chart) {
+            this.chart = chart;
+            this.chart_url = chart.url;
+            this.data_update_every = chart.update_every * 1000;
+            this.data_points = Math.round(this.chartWidth() / this.chartPixelsPerPoint());
+            this.tm.last_info_downloaded = new Date().getTime();
+
+            if(this.title === null)
+                this.title = chart.title;
+
+            if(this.units === null)
+                this.units = chart.units;
+        };
+
+        // fetch the chart description from the netdata server
+        this.getChart = function(callback) {
+            this.chart = NETDATA.chartRegistry.get(this.host, this.id);
+            if(this.chart) {
+                this._defaultsFromDownloadedChart(this.chart);
+                if(typeof callback === 'function') callback();
+            }
+            else {
+                this.chart_url = "/api/v1/chart?chart=" + this.id;
+
+                if(this.debug === true)
+                    this.log('downloading ' + this.chart_url);
+
+                $.ajax( {
+                    url:  this.host + this.chart_url,
+                    cache: false,
+                    async: true,
+                    xhrFields: { withCredentials: true } // required for the cookie
+                })
+                .done(function(chart) {
+                    chart.url = that.chart_url;
+                    that._defaultsFromDownloadedChart(chart);
+                    NETDATA.chartRegistry.add(that.host, that.id, chart);
+                })
+                .fail(function() {
+                    NETDATA.error(404, that.chart_url);
+                    error('chart not found on url "' + that.chart_url + '"');
+                })
+                .always(function() {
+                    if(typeof callback === 'function') callback();
+                });
+            }
+        };
+
+        // ============================================================================================================
+        // INITIALIZATION
+
+        init();
+    };
+
+    NETDATA.resetAllCharts = function(state) {
+        // first clear the global selection sync
+        // to make sure no chart is in selected state
+        state.globalSelectionSyncStop();
+
+        // there are 2 possibilities here
+        // a. state is the global Pan and Zoom master
+        // b. state is not the global Pan and Zoom master
+        var master = true;
+        if(NETDATA.globalPanAndZoom.isMaster(state) === false)
+            master = false;
+
+        // clear the global Pan and Zoom
+        // this will also refresh the master
+        // and unblock any charts currently mirroring the master
+        NETDATA.globalPanAndZoom.clearMaster();
+
+        // if we were not the master, reset our status too
+        // this is required because most probably the mouse
+        // is over this chart, blocking it from auto-refreshing
+        if(master === false && (state.paused === true || state.selected === true))
+            state.resetChart();
+    };
+
+    // get or create a chart state, given a DOM element
+    NETDATA.chartState = function(element) {
+        var state = $(element).data('netdata-state-object') || null;
+        if(state === null) {
+            state = new chartState(element);
+            $(element).data('netdata-state-object', state);
+        }
+        return state;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Library functions
+
+    // Load a script without jquery
+    // This is used to load jquery - after it is loaded, we use jquery
+    NETDATA._loadjQuery = function(callback) {
+        if(typeof jQuery === 'undefined') {
+            if(NETDATA.options.debug.main_loop === true)
+                console.log('loading ' + NETDATA.jQuery);
+
+            var script = document.createElement('script');
+            script.type = 'text/javascript';
+            script.async = true;
+            script.src = NETDATA.jQuery;
+
+            // script.onabort = onError;
+            script.onerror = function(err, t) { NETDATA.error(101, NETDATA.jQuery); };
+            if(typeof callback === "function")
+                script.onload = callback;
+
+            var s = document.getElementsByTagName('script')[0];
+            s.parentNode.insertBefore(script, s);
+        }
+        else if(typeof callback === "function")
+            callback();
+    };
+
+    NETDATA._loadCSS = function(filename) {
+        // don't use jQuery here
+        // styles are loaded before jQuery
+        // to eliminate showing an unstyled page to the user
+
+        var fileref = document.createElement("link");
+        fileref.setAttribute("rel", "stylesheet");
+        fileref.setAttribute("type", "text/css");
+        fileref.setAttribute("href", filename);
+
+        if (typeof fileref !== 'undefined')
+            document.getElementsByTagName("head")[0].appendChild(fileref);
+    };
+
+    NETDATA.colorHex2Rgb = function(hex) {
+        // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
+        var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+            hex = hex.replace(shorthandRegex, function(m, r, g, b) {
+            return r + r + g + g + b + b;
+        });
+
+        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+        return result ? {
+            r: parseInt(result[1], 16),
+            g: parseInt(result[2], 16),
+            b: parseInt(result[3], 16)
+        } : null;
+    };
+
+    NETDATA.colorLuminance = function(hex, lum) {
+        // validate hex string
+        hex = String(hex).replace(/[^0-9a-f]/gi, '');
+        if (hex.length < 6)
+            hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
+
+        lum = lum || 0;
+
+        // convert to decimal and change luminosity
+        var rgb = "#", c, i;
+        for (i = 0; i < 3; i++) {
+            c = parseInt(hex.substr(i*2,2), 16);
+            c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
+            rgb += ("00"+c).substr(c.length);
+        }
+
+        return rgb;
+    };
+
+    NETDATA.guid = function() {
+        function s4() {
+            return Math.floor((1 + Math.random()) * 0x10000)
+                    .toString(16)
+                    .substring(1);
+            }
+
+            return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+    };
+
+    NETDATA.zeropad = function(x) {
+        if(x > -10 && x < 10) return '0' + x.toString();
+        else return x.toString();
+    };
+
+    // user function to signal us the DOM has been
+    // updated.
+    NETDATA.updatedDom = function() {
+        NETDATA.options.updated_dom = true;
+    };
+
+    NETDATA.ready = function(callback) {
+        NETDATA.options.pauseCallback = callback;
+    };
+
+    NETDATA.pause = function(callback) {
+        if(NETDATA.options.pause === true)
+            callback();
+        else
+            NETDATA.options.pauseCallback = callback;
+    };
+
+    NETDATA.unpause = function() {
+        NETDATA.options.pauseCallback = null;
+        NETDATA.options.updated_dom = true;
+        NETDATA.options.pause = false;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+
+    // this is purely sequencial charts refresher
+    // it is meant to be autonomous
+    NETDATA.chartRefresherNoParallel = function(index) {
+        if(NETDATA.options.debug.mail_loop === true)
+            console.log('NETDATA.chartRefresherNoParallel(' + index + ')');
+
+        if(NETDATA.options.updated_dom === true) {
+            // the dom has been updated
+            // get the dom parts again
+            NETDATA.parseDom(NETDATA.chartRefresher);
+            return;
+        }
+        if(index >= NETDATA.options.targets.length) {
+            if(NETDATA.options.debug.main_loop === true)
+                console.log('waiting to restart main loop...');
+
+            NETDATA.options.auto_refresher_fast_weight = 0;
+
+            setTimeout(function() {
+                NETDATA.chartRefresher();
+            }, NETDATA.options.current.idle_between_loops);
+        }
+        else {
+            var state = NETDATA.options.targets[index];
+
+            if(NETDATA.options.auto_refresher_fast_weight < NETDATA.options.current.fast_render_timeframe) {
+                if(NETDATA.options.debug.main_loop === true)
+                    console.log('fast rendering...');
+
+                state.autoRefresh(function() {
+                    NETDATA.chartRefresherNoParallel(++index);
+                });
+            }
+            else {
+                if(NETDATA.options.debug.main_loop === true) console.log('waiting for next refresh...');
+                NETDATA.options.auto_refresher_fast_weight = 0;
+
+                setTimeout(function() {
+                    state.autoRefresh(function() {
+                        NETDATA.chartRefresherNoParallel(++index);
+                    });
+                }, NETDATA.options.current.idle_between_charts);
+            }
+        }
+    };
+
+    // 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() {
+        if(NETDATA.options.pause === true) {
+            // console.log('auto-refresher is paused');
+            setTimeout(NETDATA.chartRefresher,
+                NETDATA.chartRefresherWaitTime());
+            return;
+        }
+
+        if(typeof NETDATA.options.pauseCallback === 'function') {
+            // console.log('auto-refresher is calling pauseCallback');
+            NETDATA.options.pause = true;
+            NETDATA.options.pauseCallback();
+            NETDATA.chartRefresher();
+            return;
+        }
+
+        if(NETDATA.options.current.parallel_refresher === false) {
+            NETDATA.chartRefresherNoParallel(0);
+            return;
+        }
+
+        if(NETDATA.options.updated_dom === true) {
+            // the dom has been updated
+            // get the dom parts again
+            NETDATA.parseDom(NETDATA.chartRefresher);
+            return;
+        }
+
+        var parallel = new Array();
+        var targets = NETDATA.options.targets;
+        var len = targets.length;
+        var state;
+        while(len--) {
+            state = targets[len];
+            if(state.isVisible() === false || state.running === true)
+                continue;
+
+            if(state.library.initialized === false) {
+                if(state.library.enabled === true) {
+                    state.library.initialize(NETDATA.chartRefresher);
+                    return;
+                }
+                else {
+                    state.error('chart library "' + state.library_name + '" is not enabled.');
+                }
+            }
+
+            parallel.unshift(state);
+        }
+
+        if(parallel.length > 0) {
+            // this will execute the jobs in parallel
+            $(parallel).each(function() {
+                this.autoRefresh();
+            })
+        }
+
+        // run the next refresh iteration
+        setTimeout(NETDATA.chartRefresher,
+            NETDATA.chartRefresherWaitTime());
+    };
+
+    NETDATA.parseDom = function(callback) {
+        NETDATA.options.last_page_scroll = new Date().getTime();
+        NETDATA.options.updated_dom = false;
+
+        var targets = $('div[data-netdata]'); //.filter(':visible');
+
+        if(NETDATA.options.debug.main_loop === true)
+            console.log('DOM updated - there are ' + targets.length + ' charts on page.');
+
+        NETDATA.options.targets = new Array();
+        var len = targets.length;
+        while(len--) {
+            // the initialization will take care of sizing
+            // and the "loading..." message
+            NETDATA.options.targets.push(NETDATA.chartState(targets[len]));
+        }
+
+        if(typeof callback === 'function') callback();
+    };
+
+    // this is the main function - where everything starts
+    NETDATA.start = function() {
+        // this should be called only once
+
+        NETDATA.options.page_is_visible = true;
+
+        $(window).blur(function() {
+            if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
+                NETDATA.options.page_is_visible = false;
+                if(NETDATA.options.debug.focus === true)
+                    console.log('Lost Focus!');
+            }
+        });
+
+        $(window).focus(function() {
+            if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
+                NETDATA.options.page_is_visible = true;
+                if(NETDATA.options.debug.focus === true)
+                    console.log('Focus restored!');
+            }
+        });
+
+        if(typeof document.hasFocus === 'function' && !document.hasFocus()) {
+            if(NETDATA.options.current.stop_updates_when_focus_is_lost === true) {
+                NETDATA.options.page_is_visible = false;
+                if(NETDATA.options.debug.focus === true)
+                    console.log('Document has no focus!');
+            }
+        }
+
+        // bootstrap tab switching
+        $('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);
+
+        // bootstrap collapse switching
+        $('.collapse').on('hidden.bs.collapse', NETDATA.onscroll);
+        $('.collapse').on('shown.bs.collapse', NETDATA.onscroll);
+
+        NETDATA.parseDom(NETDATA.chartRefresher);
+
+        // Registry initialization
+        setTimeout(NETDATA.registry.init, 1000);
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // peity
+
+    NETDATA.peityInitialize = function(callback) {
+        if(typeof netdataNoPeitys === 'undefined' || !netdataNoPeitys) {
+            $.ajax({
+                url: NETDATA.peity_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function() {
+                NETDATA.registerChartLibrary('peity', NETDATA.peity_js);
+            })
+            .fail(function() {
+                NETDATA.chartLibraries.peity.enabled = false;
+                NETDATA.error(100, NETDATA.peity_js);
+            })
+            .always(function() {
+                if(typeof callback === "function")
+                    callback();
+            });
+        }
+        else {
+            NETDATA.chartLibraries.peity.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    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.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_instance).peity('line', state.peity_options);
+        return true;
+    };
+
+    NETDATA.peityChartCreate = function(state, data) {
+        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,
+            width: state.chartWidth(),
+            height: state.chartHeight(),
+            fill: NETDATA.themes.current.foreground
+        };
+
+        NETDATA.peityChartUpdate(state, data);
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // sparkline
+
+    NETDATA.sparklineInitialize = function(callback) {
+        if(typeof netdataNoSparklines === 'undefined' || !netdataNoSparklines) {
+            $.ajax({
+                url: NETDATA.sparkline_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function() {
+                NETDATA.registerChartLibrary('sparkline', NETDATA.sparkline_js);
+            })
+            .fail(function() {
+                NETDATA.chartLibraries.sparkline.enabled = false;
+                NETDATA.error(100, NETDATA.sparkline_js);
+            })
+            .always(function() {
+                if(typeof callback === "function")
+                    callback();
+            });
+        }
+        else {
+            NETDATA.chartLibraries.sparkline.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.sparklineChartUpdate = function(state, data) {
+        state.sparkline_options.width = state.chartWidth();
+        state.sparkline_options.height = state.chartHeight();
+
+        $(state.element_chart).sparkline(data.result, state.sparkline_options);
+        return true;
+    };
+
+    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;
+
+        if(spotColor === 'disable') spotColor='';
+        if(minSpotColor === 'disable') minSpotColor='';
+        if(maxSpotColor === 'disable') maxSpotColor='';
+
+        state.sparkline_options = {
+            type: type,
+            lineColor: lineColor,
+            fillColor: fillColor,
+            chartRangeMin: chartRangeMin,
+            chartRangeMax: chartRangeMax,
+            composite: composite,
+            enableTagOptions: enableTagOptions,
+            tagOptionPrefix: tagOptionPrefix,
+            tagValuesAttribute: tagValuesAttribute,
+            disableHiddenCheck: disableHiddenCheck,
+            defaultPixelsPerValue: defaultPixelsPerValue,
+            spotColor: spotColor,
+            minSpotColor: minSpotColor,
+            maxSpotColor: maxSpotColor,
+            spotRadius: spotRadius,
+            valueSpots: valueSpots,
+            highlightSpotColor: highlightSpotColor,
+            highlightLineColor: highlightLineColor,
+            lineWidth: lineWidth,
+            normalRangeMin: normalRangeMin,
+            normalRangeMax: normalRangeMax,
+            drawNormalOnTop: drawNormalOnTop,
+            xvalues: xvalues,
+            chartRangeClip: chartRangeClip,
+            chartRangeMinX: chartRangeMinX,
+            chartRangeMaxX: chartRangeMaxX,
+            disableInteraction: disableInteraction,
+            disableTooltips: disableTooltips,
+            disableHighlight: disableHighlight,
+            highlightLighten: highlightLighten,
+            highlightColor: highlightColor,
+            tooltipContainer: tooltipContainer,
+            tooltipClassname: tooltipClassname,
+            tooltipChartTitle: state.title,
+            tooltipFormat: tooltipFormat,
+            tooltipPrefix: tooltipPrefix,
+            tooltipSuffix: tooltipSuffix,
+            tooltipSkipNull: tooltipSkipNull,
+            tooltipValueLookups: tooltipValueLookups,
+            tooltipFormatFieldlist: tooltipFormatFieldlist,
+            tooltipFormatFieldlistKey: tooltipFormatFieldlistKey,
+            numberFormatter: numberFormatter,
+            numberDigitGroupSep: numberDigitGroupSep,
+            numberDecimalMark: numberDecimalMark,
+            numberDigitGroupCount: numberDigitGroupCount,
+            animatedZooms: animatedZooms,
+            width: state.chartWidth(),
+            height: state.chartHeight()
+        };
+
+        $(state.element_chart).sparkline(data.result, state.sparkline_options);
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // dygraph
+
+    NETDATA.dygraph = {
+        smooth: false
+    };
+
+    NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
+        if(after < state.netdata_first)
+            after = state.netdata_first;
+
+        if(before > state.netdata_last)
+            before = state.netdata_last;
+
+        state.setMode('zoom');
+        state.globalSelectionSyncStop();
+        state.globalSelectionSyncDelay();
+        state.dygraph_user_action = true;
+        state.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') {
+            var r = state.calculateRowForTime(t);
+            if(r !== -1)
+                state.dygraph_instance.setSelection(r);
+            else {
+                state.dygraph_instance.clearSelection();
+                state.legendShowUndefined();
+            }
+        }
+
+        return true;
+    };
+
+    NETDATA.dygraphClearSelection = function(state, t) {
+        if(typeof state.dygraph_instance !== 'undefined') {
+            state.dygraph_instance.clearSelection();
+        }
+        return true;
+    };
+
+    NETDATA.dygraphSmoothInitialize = function(callback) {
+        $.ajax({
+            url: NETDATA.dygraph_smooth_js,
+            cache: true,
+            dataType: "script",
+            xhrFields: { withCredentials: true } // required for the cookie
+        })
+        .done(function() {
+            NETDATA.dygraph.smooth = true;
+            smoothPlotter.smoothing = 0.3;
+        })
+        .fail(function() {
+            NETDATA.dygraph.smooth = false;
+        })
+        .always(function() {
+            if(typeof callback === "function")
+                callback();
+        });
+    };
+
+    NETDATA.dygraphInitialize = function(callback) {
+        if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
+            $.ajax({
+                url: NETDATA.dygraph_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function() {
+                NETDATA.registerChartLibrary('dygraph', NETDATA.dygraph_js);
+            })
+            .fail(function() {
+                NETDATA.chartLibraries.dygraph.enabled = false;
+                NETDATA.error(100, NETDATA.dygraph_js);
+            })
+            .always(function() {
+                if(NETDATA.chartLibraries.dygraph.enabled === true && NETDATA.options.current.smooth_plot === true)
+                    NETDATA.dygraphSmoothInitialize(callback);
+                else if(typeof callback === "function")
+                    callback();
+            });
+        }
+        else {
+            NETDATA.chartLibraries.dygraph.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.dygraphChartUpdate = function(state, data) {
+        var dygraph = state.dygraph_instance;
+
+        if(typeof dygraph === 'undefined')
+            return NETDATA.dygraphChartCreate(state, data);
+
+        // when the chart is not visible, and hidden
+        // if there is a window resize, dygraph detects
+        // its element size as 0x0.
+        // this will make it re-appear properly
+
+        if(state.tm.last_unhidden > state.dygraph_last_rendered)
+            dygraph.resize();
+
+        var options = {
+                file: data.result.data,
+                colors: state.chartColors(),
+                labels: data.result.labels,
+                labelsDivWidth: state.chartWidth() - 70,
+                visibility: state.dimensions_visibility.selected2BooleanArray(state.data.dimension_names)
+        };
+
+        if(state.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.valueRange = state.dygraph_options.valueRange;
+            options.isZoomedIgnoreProgrammaticZoom = true;
+            state.dygraph_force_zoom = false;
+        }
+        else if(state.current.name !== 'auto') {
+            if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                state.log('dygraphChartUpdate() loose update');
+
+            options.valueRange = state.dygraph_options.valueRange;
+        }
+        else {
+            if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                state.log('dygraphChartUpdate() strict update');
+
+            options.dateWindow = (state.requested_padding !== null)?[ state.view_after, state.view_before ]:null;
+            options.valueRange = state.dygraph_options.valueRange;
+            options.isZoomedIgnoreProgrammaticZoom = true;
+        }
+
+        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)) {
+                NETDATA.dygraphChartCreate(state, data);
+                return;
+            }
+        }
+
+        dygraph.updateOptions(options);
+
+        state.dygraph_last_rendered = new Date().getTime();
+        return true;
+    };
+
+    NETDATA.dygraphChartCreate = function(state, data) {
+        if(NETDATA.options.debug.dygraph === true || state.debug === true)
+            state.log('dygraphChartCreate()');
+
+        var self = $(state.element);
+
+        var chart_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 strokeWidth = (chart_type === 'stacked')?0.1:((smooth)?1.5:0.7)
+        var highlightCircleSize = (NETDATA.chartLibraries.dygraph.isSparkline(state))?3:4;
+
+        state.dygraph_options = {
+            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', // 'onmouseover',
+            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') || false,
+            xRangePad: self.data('dygraph-xrangepad') || 0,
+            yRangePad: self.data('dygraph-yrangepad') || 1,
+
+            valueRange: self.data('dygraph-valuerange') || 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') || 0.3,
+
+            drawGrid: self.data('dygraph-drawgrid') || true,
+            drawXGrid: self.data('dygraph-drawxgrid') || undefined,
+            drawYGrid: self.data('dygraph-drawygrid') || undefined,
+            gridLinePattern: self.data('dygraph-gridlinepattern') || null,
+            gridLineWidth: self.data('dygraph-gridlinewidth') || 0.3,
+            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),
+            axes: {
+                x: {
+                    pixelsPerLabel: 50,
+                    ticker: Dygraph.dateTicker,
+                    axisLabelFormatter: function (d, 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();
+                        // return NETDATA.zeropad(d.getHours()) + ":" + NETDATA.zeropad(d.getMinutes()) + ":" + NETDATA.zeropad(d.getSeconds());
+                    }
+                },
+                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;
+                    }
+                }
+            },
+            legendFormatter: function(data) {
+                var elements = state.element_legend_childs;
+
+                // if the hidden div is not there
+                // we are not managing the legend
+                if(elements.hidden === null) return;
+
+                if (typeof data.x !== 'undefined') {
+                    state.legendSetDate(data.x);
+                    var i = data.series.length;
+                    while(i--) {
+                        var series = data.series[i];
+                        if(!series.isVisible) continue;
+                        state.legendSetLabelValue(series.label, series.y);
+                    }
+                }
+
+                return '';
+            },
+            drawCallback: function(dygraph, is_initial) {
+                if(state.current.name !== 'auto' && state.dygraph_user_action === true) {
+                    state.dygraph_user_action = false;
+
+                    var x_range = dygraph.xAxisRange();
+                    var after = Math.round(x_range[0]);
+                    var before = Math.round(x_range[1]);
+
+                    if(NETDATA.options.debug.dygraph === true)
+                        state.log('dygraphDrawCallback(dygraph, ' + is_initial + '): ' + (after / 1000).toString() + ' - ' + (before / 1000).toString());
+
+                    if(before <= state.netdata_last && after >= state.netdata_first)
+                        state.updateChartPanOrZoom(after, before);
+                }
+            },
+            zoomCallback: function(minDate, maxDate, yRanges) {
+                if(NETDATA.options.debug.dygraph === true)
+                    state.log('dygraphZoomCallback()');
+
+                state.globalSelectionSyncStop();
+                state.globalSelectionSyncDelay();
+                state.setMode('zoom');
+
+                // refresh it to the greatest possible zoom level
+                state.dygraph_user_action = true;
+                state.dygraph_force_zoom = true;
+                state.updateChartPanOrZoom(minDate, maxDate);
+            },
+            highlightCallback: function(event, x, points, row, seriesName) {
+                if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                    state.log('dygraphHighlightCallback()');
+
+                state.pauseChart();
+
+                // there is a bug in dygraph when the chart is zoomed enough
+                // 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;
+                // 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);
+
+                // 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;
+            },
+            unhighlightCallback: function(event) {
+                if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                    state.log('dygraphUnhighlightCallback()');
+
+                state.unpauseChart();
+                state.globalSelectionSyncStop();
+            },
+            interactionModel : {
+                mousedown: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.mousedown()');
+
+                    state.dygraph_user_action = true;
+                    state.globalSelectionSyncStop();
+
+                    if(NETDATA.options.debug.dygraph === true)
+                        state.log('dygraphMouseDown()');
+
+                    // Right-click should not initiate a zoom.
+                    if(event.button && event.button === 2) return;
+
+                    context.initializeMouseDown(event, dygraph, context);
+
+                    if(event.button && event.button === 1) {
+                        if (event.altKey || event.shiftKey) {
+                            state.setMode('pan');
+                            state.globalSelectionSyncDelay();
+                            Dygraph.startPan(event, dygraph, context);
+                        }
+                        else {
+                            state.setMode('zoom');
+                            state.globalSelectionSyncDelay();
+                            Dygraph.startZoom(event, dygraph, context);
+                        }
+                    }
+                    else {
+                        if (event.altKey || event.shiftKey) {
+                            state.setMode('zoom');
+                            state.globalSelectionSyncDelay();
+                            Dygraph.startZoom(event, dygraph, context);
+                        }
+                        else {
+                            state.setMode('pan');
+                            state.globalSelectionSyncDelay();
+                            Dygraph.startPan(event, dygraph, context);
+                        }
+                    }
+                },
+                mousemove: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.mousemove()');
+
+                    if(context.isPanning) {
+                        state.dygraph_user_action = true;
+                        state.globalSelectionSyncStop();
+                        state.globalSelectionSyncDelay();
+                        state.setMode('pan');
+                        Dygraph.movePan(event, dygraph, context);
+                    }
+                    else if(context.isZooming) {
+                        state.dygraph_user_action = true;
+                        state.globalSelectionSyncStop();
+                        state.globalSelectionSyncDelay();
+                        state.setMode('zoom');
+                        Dygraph.moveZoom(event, dygraph, context);
+                    }
+                },
+                mouseup: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.mouseup()');
+
+                    if (context.isPanning) {
+                        state.dygraph_user_action = true;
+                        state.globalSelectionSyncDelay();
+                        Dygraph.endPan(event, dygraph, context);
+                    }
+                    else if (context.isZooming) {
+                        state.dygraph_user_action = true;
+                        state.globalSelectionSyncDelay();
+                        Dygraph.endZoom(event, dygraph, context);
+                    }
+                },
+                click: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.click()');
+
+                    event.preventDefault();
+                },
+                dblclick: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.dblclick()');
+                    NETDATA.resetAllCharts(state);
+                },
+                mousewheel: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.mousewheel()');
+
+                    // Take the offset of a mouse event on the dygraph canvas and
+                    // convert it to a pair of percentages from the bottom left.
+                    // (Not top left, bottom is where the lower value is.)
+                    function offsetToPercentage(g, offsetX, offsetY) {
+                        // This is calculating the pixel offset of the leftmost date.
+                        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)
+                        var yOffset = g.toDomCoords(null, yar0[1])[1];
+
+                        // x y w and h are relative to the corner of the drawing area,
+                        // so that the upper corner of the drawing area is (0, 0).
+                        var x = offsetX - xOffset;
+                        var y = offsetY - yOffset;
+
+                        // This is computing the rightmost pixel, effectively defining the
+                        // width.
+                        var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
+
+                        // This is computing the lowest pixel, effectively defining the height.
+                        var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
+
+                        // Percentage from the left.
+                        var xPct = w === 0 ? 0 : (x / w);
+                        // Percentage from the top.
+                        var yPct = h === 0 ? 0 : (y / h);
+
+                        // The (1-) part below changes it from "% distance down from the top"
+                        // to "% distance up from the bottom".
+                        return [xPct, (1-yPct)];
+                    }
+
+                    // Adjusts [x, y] toward each other by zoomInPercentage%
+                    // Split it so the left/bottom axis gets xBias/yBias of that change and
+                    // tight/top gets (1-xBias)/(1-yBias) of that change.
+                    //
+                    // If a bias is missing it splits it down the middle.
+                    function zoomRange(g, zoomInPercentage, xBias, yBias) {
+                        xBias = xBias || 0.5;
+                        yBias = yBias || 0.5;
+
+                        function adjustAxis(axis, zoomInPercentage, bias) {
+                            var delta = axis[1] - axis[0];
+                            var increment = delta * zoomInPercentage;
+                            var foo = [increment * bias, increment * (1-bias)];
+
+                            return [ axis[0] + foo[0], axis[1] - foo[1] ];
+                        }
+
+                        var yAxes = g.yAxisRanges();
+                        var newYAxes = [];
+                        for (var i = 0; i < yAxes.length; i++) {
+                            newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
+                        }
+
+                        return adjustAxis(g.xAxisRange(), zoomInPercentage, xBias);
+                    }
+
+                    if(event.altKey || event.shiftKey) {
+                        state.dygraph_user_action = true;
+
+                        state.globalSelectionSyncStop();
+                        state.globalSelectionSyncDelay();
+
+                        // http://dygraphs.com/gallery/interaction-api.js
+                        var normal = (event.detail) ? event.detail * -1 : event.wheelDelta / 40;
+                        var percentage = normal / 50;
+
+                        if (!(event.offsetX && event.offsetY)){
+                            event.offsetX = event.layerX - event.target.offsetLeft;
+                            event.offsetY = event.layerY - event.target.offsetTop;
+                        }
+
+                        var percentages = offsetToPercentage(dygraph, event.offsetX, event.offsetY);
+                        var xPct = percentages[0];
+                        var yPct = percentages[1];
+
+                        var new_x_range = zoomRange(dygraph, percentage, xPct, yPct);
+
+                        var after = new_x_range[0];
+                        var before = new_x_range[1];
+
+                        var first = state.netdata_first + state.data_update_every;
+                        var last = state.netdata_last + state.data_update_every;
+
+                        if(before > last) {
+                            after -= (before - last);
+                            before = last;
+                        }
+                        if(after < first) {
+                            after = first;
+                        }
+
+                        state.setMode('zoom');
+                        if(state.updateChartPanOrZoom(after, before) === true)
+                            dygraph.updateOptions({ dateWindow: [ after, before ] });
+
+                        event.preventDefault();
+                    }
+                },
+                touchstart: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.touchstart()');
+
+                    state.dygraph_user_action = true;
+                    state.setMode('zoom');
+                    state.pauseChart();
+
+                    Dygraph.defaultInteractionModel.touchstart(event, dygraph, context);
+
+                    // we overwrite the touch directions at the end, to overwrite
+                    // the internal default of dygraphs
+                    context.touchDirections = { x: true, y: false };
+
+                    state.dygraph_last_touch_start = new Date().getTime();
+                    state.dygraph_last_touch_move = 0;
+
+                    if(typeof event.touches[0].pageX === 'number')
+                        state.dygraph_last_touch_page_x = event.touches[0].pageX;
+                    else
+                        state.dygraph_last_touch_page_x = 0;
+                },
+                touchmove: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.touchmove()');
+
+                    state.dygraph_user_action = true;
+                    Dygraph.defaultInteractionModel.touchmove(event, dygraph, context);
+
+                    state.dygraph_last_touch_move = new Date().getTime();
+                },
+                touchend: function(event, dygraph, context) {
+                    if(NETDATA.options.debug.dygraph === true || state.debug === true)
+                        state.log('interactionModel.touchend()');
+
+                    state.dygraph_user_action = true;
+                    Dygraph.defaultInteractionModel.touchend(event, dygraph, context);
+
+                    // 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
+                        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)
+                            state.globalSelectionSync(t);
+                    }
+
+                    // if it was double tap within double click time, reset the charts
+                    var now = new Date().getTime();
+                    if(typeof state.dygraph_last_touch_end !== 'undefined') {
+                        if(state.dygraph_last_touch_move === 0) {
+                            var dt = now - state.dygraph_last_touch_end;
+                            if(dt <= NETDATA.options.current.double_click_speed)
+                                NETDATA.resetAllCharts(state);
+                        }
+                    }
+
+                    // remember the timestamp of the last touch end
+                    state.dygraph_last_touch_end = now;
+                }
+            }
+        };
+
+        if(NETDATA.chartLibraries.dygraph.isSparkline(state)) {
+            state.dygraph_options.drawGrid = false;
+            state.dygraph_options.drawAxis = false;
+            state.dygraph_options.title = undefined;
+            state.dygraph_options.units = 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;
+        }
+
+        if(smooth === true) {
+            state.dygraph_smooth_eligible = true;
+
+            if(NETDATA.options.current.smooth_plot === true)
+                state.dygraph_options.plotter = smoothPlotter;
+        }
+        else state.dygraph_smooth_eligible = false;
+
+        state.dygraph_instance = new Dygraph(state.element_chart,
+            data.result.data, state.dygraph_options);
+
+        state.dygraph_force_zoom = false;
+        state.dygraph_user_action = false;
+        state.dygraph_last_rendered = new Date().getTime();
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // morris
+
+    NETDATA.morrisInitialize = function(callback) {
+        if(typeof netdataNoMorris === 'undefined' || !netdataNoMorris) {
+
+            // morris requires raphael
+            if(!NETDATA.chartLibraries.raphael.initialized) {
+                if(NETDATA.chartLibraries.raphael.enabled) {
+                    NETDATA.raphaelInitialize(function() {
+                        NETDATA.morrisInitialize(callback);
+                    });
+                }
+                else {
+                    NETDATA.chartLibraries.morris.enabled = false;
+                    if(typeof callback === "function")
+                        callback();
+                }
+            }
+            else {
+                NETDATA._loadCSS(NETDATA.morris_css);
+
+                $.ajax({
+                    url: NETDATA.morris_js,
+                    cache: true,
+                    dataType: "script",
+                    xhrFields: { withCredentials: true } // required for the cookie
+                })
+                .done(function() {
+                    NETDATA.registerChartLibrary('morris', NETDATA.morris_js);
+                })
+                .fail(function() {
+                    NETDATA.chartLibraries.morris.enabled = false;
+                    NETDATA.error(100, NETDATA.morris_js);
+                })
+                .always(function() {
+                    if(typeof callback === "function")
+                        callback();
+                });
+            }
+        }
+        else {
+            NETDATA.chartLibraries.morris.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.morrisChartUpdate = function(state, data) {
+        state.morris_instance.setData(data.result.data);
+        return true;
+    };
+
+    NETDATA.morrisChartCreate = function(state, data) {
+
+        state.morris_options = {
+                element: state.element_chart.id,
+                data: data.result.data,
+                xkey: 'time',
+                ykeys: data.dimension_names,
+                labels: data.dimension_names,
+                lineWidth: 2,
+                pointSize: 3,
+                smooth: true,
+                hideHover: 'auto',
+                parseTime: true,
+                continuousLine: false,
+                behaveLikeLine: false
+        };
+
+        if(state.chart.chart_type === 'line')
+            state.morris_instance = new Morris.Line(state.morris_options);
+
+        else if(state.chart.chart_type === 'area') {
+            state.morris_options.behaveLikeLine = true;
+            state.morris_instance = new Morris.Area(state.morris_options);
+        }
+        else // stacked
+            state.morris_instance = new Morris.Area(state.morris_options);
+
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // raphael
+
+    NETDATA.raphaelInitialize = function(callback) {
+        if(typeof netdataStopRaphael === 'undefined' || !netdataStopRaphael) {
+            $.ajax({
+                url: NETDATA.raphael_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function() {
+                NETDATA.registerChartLibrary('raphael', NETDATA.raphael_js);
+            })
+            .fail(function() {
+                NETDATA.chartLibraries.raphael.enabled = false;
+                NETDATA.error(100, NETDATA.raphael_js);
+            })
+            .always(function() {
+                if(typeof callback === "function")
+                    callback();
+            });
+        }
+        else {
+            NETDATA.chartLibraries.raphael.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.raphaelChartUpdate = function(state, data) {
+        $(state.element_chart).raphael(data.result, {
+            width: state.chartWidth(),
+            height: state.chartHeight()
+        });
+
+        return false;
+    };
+
+    NETDATA.raphaelChartCreate = function(state, data) {
+        $(state.element_chart).raphael(data.result, {
+            width: state.chartWidth(),
+            height: state.chartHeight()
+        });
+
+        return false;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // C3
+
+    NETDATA.c3Initialize = function(callback) {
+        if(typeof netdataNoC3 === 'undefined' || !netdataNoC3) {
+
+            // C3 requires D3
+            if(!NETDATA.chartLibraries.d3.initialized) {
+                if(NETDATA.chartLibraries.d3.enabled) {
+                    NETDATA.d3Initialize(function() {
+                        NETDATA.c3Initialize(callback);
+                    });
+                }
+                else {
+                    NETDATA.chartLibraries.c3.enabled = false;
+                    if(typeof callback === "function")
+                        callback();
+                }
+            }
+            else {
+                NETDATA._loadCSS(NETDATA.c3_css);
+
+                $.ajax({
+                    url: NETDATA.c3_js,
+                    cache: true,
+                    dataType: "script",
+                    xhrFields: { withCredentials: true } // required for the cookie
+                })
+                .done(function() {
+                    NETDATA.registerChartLibrary('c3', NETDATA.c3_js);
+                })
+                .fail(function() {
+                    NETDATA.chartLibraries.c3.enabled = false;
+                    NETDATA.error(100, NETDATA.c3_js);
+                })
+                .always(function() {
+                    if(typeof callback === "function")
+                        callback();
+                });
+            }
+        }
+        else {
+            NETDATA.chartLibraries.c3.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.c3ChartUpdate = function(state, data) {
+        state.c3_instance.destroy();
+        return NETDATA.c3ChartCreate(state, data);
+
+        //state.c3_instance.load({
+        //  rows: data.result,
+        //  unload: true
+        //});
+
+        //return true;
+    };
+
+    NETDATA.c3ChartCreate = function(state, data) {
+
+        state.element_chart.id = 'c3-' + state.uuid;
+        // console.log('id = ' + state.element_chart.id);
+
+        state.c3_instance = c3.generate({
+            bindto: '#' + state.element_chart.id,
+            size: {
+                width: state.chartWidth(),
+                height: state.chartHeight()
+            },
+            color: {
+                pattern: state.chartColors()
+            },
+            data: {
+                x: 'time',
+                rows: data.result,
+                type: (state.chart.chart_type === 'line')?'spline':'area-spline'
+            },
+            axis: {
+                x: {
+                    type: 'timeseries',
+                    tick: {
+                        format: function(x) {
+                            return NETDATA.zeropad(x.getHours()) + ":" + NETDATA.zeropad(x.getMinutes()) + ":" + NETDATA.zeropad(x.getSeconds());
+                        }
+                    }
+                }
+            },
+            grid: {
+                x: {
+                    show: true
+                },
+                y: {
+                    show: true
+                }
+            },
+            point: {
+                show: false
+            },
+            line: {
+                connectNull: false
+            },
+            transition: {
+                duration: 0
+            },
+            interaction: {
+                enabled: true
+            }
+        });
+
+        // console.log(state.c3_instance);
+
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // D3
+
+    NETDATA.d3Initialize = function(callback) {
+        if(typeof netdataStopD3 === 'undefined' || !netdataStopD3) {
+            $.ajax({
+                url: NETDATA.d3_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function() {
+                NETDATA.registerChartLibrary('d3', NETDATA.d3_js);
+            })
+            .fail(function() {
+                NETDATA.chartLibraries.d3.enabled = false;
+                NETDATA.error(100, NETDATA.d3_js);
+            })
+            .always(function() {
+                if(typeof callback === "function")
+                    callback();
+            });
+        }
+        else {
+            NETDATA.chartLibraries.d3.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.d3ChartUpdate = function(state, data) {
+        return false;
+    };
+
+    NETDATA.d3ChartCreate = function(state, data) {
+        return false;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // google charts
+
+    NETDATA.googleInitialize = function(callback) {
+        if(typeof netdataNoGoogleCharts === 'undefined' || !netdataNoGoogleCharts) {
+            $.ajax({
+                url: NETDATA.google_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+            .done(function() {
+                NETDATA.registerChartLibrary('google', NETDATA.google_js);
+                google.load('visualization', '1.1', {
+                    'packages': ['corechart', 'controls'],
+                    'callback': callback
+                });
+            })
+            .fail(function() {
+                NETDATA.chartLibraries.google.enabled = false;
+                NETDATA.error(100, NETDATA.google_js);
+                if(typeof callback === "function")
+                    callback();
+            });
+        }
+        else {
+            NETDATA.chartLibraries.google.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.googleChartUpdate = function(state, data) {
+        var datatable = new google.visualization.DataTable(data.result);
+        state.google_instance.draw(datatable, state.google_options);
+        return true;
+    };
+
+    NETDATA.googleChartCreate = function(state, data) {
+        var datatable = new google.visualization.DataTable(data.result);
+
+        state.google_options = {
+            colors: state.chartColors(),
+
+            // do not set width, height - the chart resizes itself
+            //width: state.chartWidth(),
+            //height: state.chartHeight(),
+            lineWidth: 1,
+            title: state.title,
+            fontSize: 11,
+            hAxis: {
+            //  title: "Time of Day",
+            //  format:'HH:mm:ss',
+                viewWindowMode: 'maximized',
+                slantedText: false,
+                format:'HH:mm:ss',
+                textStyle: {
+                    fontSize: 9
+                },
+                gridlines: {
+                    color: '#EEE'
+                }
+            },
+            vAxis: {
+                title: state.units,
+                viewWindowMode: 'pretty',
+                minValue: -0.1,
+                maxValue: 0.1,
+                direction: 1,
+                textStyle: {
+                    fontSize: 9
+                },
+                gridlines: {
+                    color: '#EEE'
+                }
+            },
+            chartArea: {
+                width: '65%',
+                height: '80%'
+            },
+            focusTarget: 'category',
+            annotation: {
+                '1': {
+                    style: 'line'
+                }
+            },
+            pointsVisible: 0,
+            titlePosition: 'out',
+            titleTextStyle: {
+                fontSize: 11
+            },
+            tooltip: {
+                isHtml: false,
+                ignoreBounds: true,
+                textStyle: {
+                    fontSize: 9
+                }
+            },
+            curveType: 'function',
+            areaOpacity: 0.3,
+            isStacked: false
+        };
+
+        switch(state.chart.chart_type) {
+            case "area":
+                state.google_options.vAxis.viewWindowMode = 'maximized';
+                state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_area;
+                state.google_instance = new google.visualization.AreaChart(state.element_chart);
+                break;
+
+            case "stacked":
+                state.google_options.isStacked = true;
+                state.google_options.areaOpacity = NETDATA.options.current.color_fill_opacity_stacked;
+                state.google_options.vAxis.viewWindowMode = 'maximized';
+                state.google_options.vAxis.minValue = null;
+                state.google_options.vAxis.maxValue = null;
+                state.google_instance = new google.visualization.AreaChart(state.element_chart);
+                break;
+
+            default:
+            case "line":
+                state.google_options.lineWidth = 2;
+                state.google_instance = new google.visualization.LineChart(state.element_chart);
+                break;
+        }
+
+        state.google_instance.draw(datatable, state.google_options);
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+
+    NETDATA.percentFromValueMax = function(value, max) {
+        if(value === null) value = 0;
+        if(max < value) max = value;
+
+        var pcent = 0;
+        if(max !== 0) {
+            pcent = Math.round(value * 100 / max);
+            if(pcent === 0 && value > 0) pcent = 1;
+        }
+
+        return pcent;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // easy-pie-chart
+
+    NETDATA.easypiechartInitialize = function(callback) {
+        if(typeof netdataNoEasyPieChart === 'undefined' || !netdataNoEasyPieChart) {
+            $.ajax({
+                url: NETDATA.easypiechart_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+                .done(function() {
+                    NETDATA.registerChartLibrary('easypiechart', NETDATA.easypiechart_js);
+                })
+                .fail(function() {
+                    NETDATA.chartLibraries.easypiechart.enabled = false;
+                    NETDATA.error(100, NETDATA.easypiechart_js);
+                })
+                .always(function() {
+                    if(typeof callback === "function")
+                        callback();
+                })
+        }
+        else {
+            NETDATA.chartLibraries.easypiechart.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.easypiechartClearSelection = function(state) {
+        if(typeof state.easyPieChartEvent !== 'undefined') {
+            if(state.easyPieChartEvent.timer !== null)
+                clearTimeout(state.easyPieChartEvent.timer);
+
+            state.easyPieChartEvent.timer = null;
+        }
+
+        if(state.isAutoRefreshable() === true && state.data !== null) {
+            NETDATA.easypiechartChartUpdate(state, state.data);
+        }
+        else {
+            state.easyPieChartLabel.innerHTML = state.legendFormatValue(null);
+            state.easyPieChart_instance.update(0);
+        }
+        state.easyPieChart_instance.enableAnimation();
+
+        return true;
+    };
+
+    NETDATA.easypiechartSetSelection = function(state, t) {
+        if(state.timeIsVisible(t) !== true)
+            return NETDATA.easypiechartClearSelection(state);
+
+        var slot = state.calculateRowForTime(t);
+        if(slot < 0 || slot >= state.data.result.length)
+            return NETDATA.easypiechartClearSelection(state);
+
+        if(typeof state.easyPieChartEvent === 'undefined') {
+            state.easyPieChartEvent = {
+                timer: null,
+                value: 0,
+                pcent: 0
+            };
+        }
+
+        var value = state.data.result[state.data.result.length - 1 - slot];
+        var max = (state.easyPieChartMax === null)?state.data.max:state.easyPieChartMax;
+        var pcent = NETDATA.percentFromValueMax(value, max);
+
+        state.easyPieChartEvent.value = value;
+        state.easyPieChartEvent.pcent = pcent;
+        state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
+
+        if(state.easyPieChartEvent.timer === null) {
+            state.easyPieChart_instance.disableAnimation();
+
+            state.easyPieChartEvent.timer = setTimeout(function() {
+                state.easyPieChartEvent.timer = null;
+                state.easyPieChart_instance.update(state.easyPieChartEvent.pcent);
+            }, NETDATA.options.current.charts_selection_animation_delay);
+        }
+
+        return true;
+    };
+
+    NETDATA.easypiechartChartUpdate = function(state, data) {
+        var value, max, pcent;
+
+        if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
+            value = null;
+            max = 0;
+            pcent = 0;
+        }
+        else {
+            value = data.result[0];
+            max = (state.easyPieChartMax === null)?data.max:state.easyPieChartMax;
+            pcent = NETDATA.percentFromValueMax(value, max);
+        }
+
+        state.easyPieChartLabel.innerHTML = state.legendFormatValue(value);
+        state.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 max = self.data('easypiechart-max-value') || null;
+        var adjust = self.data('easypiechart-adjust') || null;
+
+        if(max === null) {
+            max = data.max;
+            state.easyPieChartMax = null;
+        }
+        else
+            state.easyPieChartMax = max;
+
+        var pcent = NETDATA.percentFromValueMax(value, 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 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.innerHTML = state.legendFormatValue(value);
+        state.easyPieChartLabel.style.fontSize = valuefontsize + 'px';
+        state.easyPieChartLabel.style.top = valuetop.toString() + 'px';
+        state.element_chart.appendChild(state.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.innerHTML = 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);
+
+        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.innerHTML = state.units;
+        state.easyPieChartUnits.style.fontSize = unitfontsize + 'px';
+        state.easyPieChartUnits.style.top = unittop.toString() + 'px';
+        state.element_chart.appendChild(state.easyPieChartUnits);
+
+        chart.easyPieChart({
+            barColor: self.data('easypiechart-barcolor') || state.chartColors()[0], //'#ef1e25',
+            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-rotate') || {duration: 500, enabled: true},
+            easing: self.data('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')
+            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();
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // gauge.js
+
+    NETDATA.gaugeInitialize = function(callback) {
+        if(typeof netdataNoGauge === 'undefined' || !netdataNoGauge) {
+            $.ajax({
+                url: NETDATA.gauge_js,
+                cache: true,
+                dataType: "script",
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+                .done(function() {
+                    NETDATA.registerChartLibrary('gauge', NETDATA.gauge_js);
+                })
+                .fail(function() {
+                    NETDATA.chartLibraries.gauge.enabled = false;
+                    NETDATA.error(100, NETDATA.gauge_js);
+                })
+                .always(function() {
+                    if(typeof callback === "function")
+                        callback();
+                })
+        }
+        else {
+            NETDATA.chartLibraries.gauge.enabled = false;
+            if(typeof callback === "function")
+                callback();
+        }
+    };
+
+    NETDATA.gaugeAnimation = function(state, status) {
+        var speed = 32;
+
+        if(typeof status === 'boolean' && status === false)
+            speed = 1000000000;
+        else if(typeof status === 'number')
+            speed = status;
+
+        state.gauge_instance.animationSpeed = speed;
+        state.___gaugeOld__.speed = speed;
+    };
+
+    NETDATA.gaugeSet = function(state, value, min, max) {
+        if(typeof value !== 'number') value = 0;
+        if(typeof min !== 'number') min = 0;
+        if(typeof max !== 'number') max = 0;
+        if(value > max) max = value;
+        if(value < min) min = value;
+        if(min > max) {
+            var t = min;
+            min = max;
+            max = t;
+        }
+        else if(min == max)
+            max = min + 1;
+
+        // gauge.js has an issue if the needle
+        // is smaller than min or larger than max
+        // when we set the new values
+        // the needle will go crazy
+
+        // to prevent it, we always feed it
+        // with a percentage, so that the needle
+        // 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;
+
+        state.gauge_instance.set(pcent);
+
+        state.___gaugeOld__.value = value;
+        state.___gaugeOld__.min = min;
+        state.___gaugeOld__.max = max;
+    };
+
+    NETDATA.gaugeSetLabels = function(state, value, min, max) {
+        if(state.___gaugeOld__.valueLabel !== value) {
+            state.___gaugeOld__.valueLabel = value;
+            state.gaugeChartLabel.innerHTML = state.legendFormatValue(value);
+        }
+        if(state.___gaugeOld__.minLabel !== min) {
+            state.___gaugeOld__.minLabel = min;
+            state.gaugeChartMin.innerHTML = state.legendFormatValue(min);
+        }
+        if(state.___gaugeOld__.maxLabel !== max) {
+            state.___gaugeOld__.maxLabel = max;
+            state.gaugeChartMax.innerHTML = state.legendFormatValue(max);
+        }
+    };
+
+    NETDATA.gaugeClearSelection = function(state) {
+        if(typeof state.gaugeEvent !== 'undefined') {
+            if(state.gaugeEvent.timer !== null)
+                clearTimeout(state.gaugeEvent.timer);
+
+            state.gaugeEvent.timer = null;
+        }
+
+        if(state.isAutoRefreshable() === true && state.data !== null) {
+            NETDATA.gaugeChartUpdate(state, state.data);
+        }
+        else {
+            NETDATA.gaugeAnimation(state, false);
+            NETDATA.gaugeSet(state, null, null, null);
+            NETDATA.gaugeSetLabels(state, null, null, null);
+        }
+
+        NETDATA.gaugeAnimation(state, true);
+        return true;
+    };
+
+    NETDATA.gaugeSetSelection = function(state, t) {
+        if(state.timeIsVisible(t) !== true)
+            return NETDATA.gaugeClearSelection(state);
+
+        var slot = state.calculateRowForTime(t);
+        if(slot < 0 || slot >= state.data.result.length)
+            return NETDATA.gaugeClearSelection(state);
+
+        if(typeof state.gaugeEvent === 'undefined') {
+            state.gaugeEvent = {
+                timer: null,
+                value: 0,
+                min: 0,
+                max: 0
+            };
+        }
+
+        var value = state.data.result[state.data.result.length - 1 - slot];
+        var max = (state.gaugeMax === null)?state.data.max:state.gaugeMax;
+        var min = 0;
+
+        state.gaugeEvent.value = value;
+        state.gaugeEvent.max = max;
+        state.gaugeEvent.min = min;
+        NETDATA.gaugeSetLabels(state, value, min, max);
+
+        if(state.gaugeEvent.timer === null) {
+            NETDATA.gaugeAnimation(state, false);
+
+            state.gaugeEvent.timer = setTimeout(function() {
+                state.gaugeEvent.timer = null;
+                NETDATA.gaugeSet(state, state.gaugeEvent.value, state.gaugeEvent.min, state.gaugeEvent.max);
+            }, NETDATA.options.current.charts_selection_animation_delay);
+        }
+
+        return true;
+    };
+
+    NETDATA.gaugeChartUpdate = function(state, data) {
+        var value, min, max;
+
+        if(NETDATA.globalPanAndZoom.isActive() === true || state.isAutoRefreshable() === false) {
+            value = 0;
+            min = 0;
+            max = 1;
+            NETDATA.gaugeSetLabels(state, null, null, null);
+        }
+        else {
+            value = data.result[0];
+            min = 0;
+            max = (state.gaugeMax === null)?data.max:state.gaugeMax;
+            if(value > max) max = value;
+            NETDATA.gaugeSetLabels(state, value, min, max);
+        }
+
+        NETDATA.gaugeSet(state, value, min, max);
+        return true;
+    };
+
+    NETDATA.gaugeChartCreate = function(state, data) {
+        var self = $(state.element);
+        // var chart = $(state.element_chart);
+
+        var value = data.result[0];
+        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;
+
+        if(max === null) {
+            max = data.max;
+            state.gaugeMax = null;
+        }
+        else
+            state.gaugeMax = max;
+
+        var width = state.chartWidth(), height = state.chartHeight(); //, ratio = 1.5;
+        //switch(adjust) {
+        //  case 'width': width = height * ratio; break;
+        //  case 'height':
+        //  default: height = width / ratio; break;
+        //}
+        //state.element.style.width = width.toString() + 'px';
+        //state.element.style.height = height.toString() + 'px';
+
+        var lum_d = 0.05;
+
+        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
+            pointer: {
+                length: 0.8,            // 0.9 The radius of the inner circle
+                strokeWidth: 0.035,     // The rotation offset
+                color: pointerColor     // Fill color
+            },
+            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
+        };
+
+        if (generateGradient.constructor === Array) {
+            // example options:
+            // data-gauge-generate-gradient="[0, 50, 100]"
+            // data-gauge-gradient-percent-color-0="#FFFFFF"
+            // data-gauge-gradient-percent-color-50="#999900"
+            // data-gauge-gradient-percent-color-100="#000000"
+
+            options.percentColors = new Array();
+            var len = generateGradient.length;
+            while(len--) {
+                var pcent = generateGradient[len];
+                var color = self.data('gauge-gradient-percent-color-' + pcent.toString()) || false;
+                if(color !== false) {
+                    var a = new Array();
+                    a[0] = pcent / 100;
+                    a[1] = color;
+                    options.percentColors.unshift(a);
+                }
+            }
+            if(options.percentColors.length === 0)
+                delete options.percentColors;
+        }
+        else if(generateGradient === false && NETDATA.themes.current.gauge_gradient === true) {
+            options.percentColors = [
+                [0.0, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 0))],
+                [0.1, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 1))],
+                [0.2, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 2))],
+                [0.3, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 3))],
+                [0.4, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 4))],
+                [0.5, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 5))],
+                [0.6, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 6))],
+                [0.7, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 7))],
+                [0.8, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 8))],
+                [0.9, NETDATA.colorLuminance(startColor, (lum_d * 10) - (lum_d * 9))],
+                [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);
+
+        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 titlefontsize = Math.round(valuefontsize / 2);
+        var titletop = 0;
+        state.gaugeChartTitle = document.createElement('span');
+        state.gaugeChartTitle.className = 'gaugeChartTitle';
+        state.gaugeChartTitle.innerHTML = 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);
+
+        var unitfontsize = Math.round(titlefontsize * 0.9);
+        state.gaugeChartUnits = document.createElement('span');
+        state.gaugeChartUnits.className = 'gaugeChartUnits';
+        state.gaugeChartUnits.innerHTML = 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);
+
+        // when we just re-create the chart
+        // do not animate the first update
+        var animate = true;
+        if(typeof state.gauge_instance !== 'undefined')
+            animate = false;
+
+        state.gauge_instance = new Gauge(state.gauge_canvas).setOptions(options); // create sexy gauge!
+
+        state.___gaugeOld__ = {
+            value: value,
+            min: 0,
+            max: max,
+            valueLabel: null,
+            minLabel: null,
+            maxLabel: null
+        };
+
+        // we will always feed a percentage
+        state.gauge_instance.minValue = 0;
+        state.gauge_instance.maxValue = 100;
+
+        NETDATA.gaugeAnimation(state, animate);
+        NETDATA.gaugeSet(state, value, 0, max);
+        NETDATA.gaugeSetLabels(state, value, 0, max);
+        NETDATA.gaugeAnimation(state, true);
+        return true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Charts Libraries Registration
+
+    NETDATA.chartLibraries = {
+        "dygraph": {
+            initialize: NETDATA.dygraphInitialize,
+            create: NETDATA.dygraphChartCreate,
+            update: NETDATA.dygraphChartUpdate,
+            resize: function(state) {
+                if(typeof state.dygraph_instance.resize === 'function')
+                    state.dygraph_instance.resize();
+            },
+            setSelection: NETDATA.dygraphSetSelection,
+            clearSelection:  NETDATA.dygraphClearSelection,
+            toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
+            initialized: false,
+            enabled: true,
+            format: function(state) { return 'json'; },
+            options: function(state) { return 'ms|flip'; },
+            legend: function(state) {
+                if(this.isSparkline(state) === false)
+                    return 'right-side';
+                else
+                    return null;
+            },
+            autoresize: function(state) { return true; },
+            max_updates_to_recreate: function(state) { return 5000; },
+            track_colors: function(state) { return true; },
+            pixels_per_point: function(state) {
+                if(this.isSparkline(state) === false)
+                    return 3;
+                else
+                    return 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;
+                }
+                return state.dygraph_sparkline;
+            }
+        },
+        "sparkline": {
+            initialize: NETDATA.sparklineInitialize,
+            create: NETDATA.sparklineChartCreate,
+            update: NETDATA.sparklineChartUpdate,
+            resize: null,
+            setSelection: undefined, // function(state, t) { return true; },
+            clearSelection: undefined, // function(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; }
+        },
+        "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; },
+            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; }
+        },
+        "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; },
+            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; }
+        },
+        "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; },
+            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; }
+        },
+        "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; },
+            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; }
+        },
+        "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; },
+            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; }
+        },
+        "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; },
+            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; }
+        },
+        "easypiechart": {
+            initialize: NETDATA.easypiechartInitialize,
+            create: NETDATA.easypiechartChartCreate,
+            update: NETDATA.easypiechartChartUpdate,
+            resize: null,
+            setSelection: NETDATA.easypiechartSetSelection,
+            clearSelection: NETDATA.easypiechartClearSelection,
+            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; },
+            aspect_ratio: 100
+        },
+        "gauge": {
+            initialize: NETDATA.gaugeInitialize,
+            create: NETDATA.gaugeChartCreate,
+            update: NETDATA.gaugeChartUpdate,
+            resize: null,
+            setSelection: NETDATA.gaugeSetSelection,
+            clearSelection: NETDATA.gaugeClearSelection,
+            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; },
+            aspect_ratio: 70
+        }
+    };
+
+    NETDATA.registerChartLibrary = function(library, url) {
+        if(NETDATA.options.debug.libraries === true)
+            console.log("registering chart library: " + library);
+
+        NETDATA.chartLibraries[library].url = url;
+        NETDATA.chartLibraries[library].initialized = true;
+        NETDATA.chartLibraries[library].enabled = true;
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Load required JS libraries and CSS
+
+    NETDATA.requiredJs = [
+        {
+            url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
+            isAlreadyLoaded: function() {
+                // check if bootstrap is loaded
+                if(typeof $().emulateTransitionEnd == 'function')
+                    return true;
+                else {
+                    if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
+                        return true;
+                    else
+                        return false;
+                }
+            }
+        },
+        {
+            url: NETDATA.serverDefault + 'lib/jquery.nanoscroller.min.js',
+            isAlreadyLoaded: function() { return false; }
+        },
+        {
+            url: NETDATA.serverDefault + 'lib/bootstrap-toggle.min.js',
+            isAlreadyLoaded: function() { return false; }
+        }
+    ];
+
+    NETDATA.requiredCSS = [
+        {
+            url: NETDATA.themes.current.bootstrap_css,
+            isAlreadyLoaded: function() {
+                if(typeof netdataNoBootstrap !== 'undefined' && netdataNoBootstrap)
+                    return true;
+                else
+                    return false;
+            }
+        },
+        {
+            url: NETDATA.serverDefault + 'css/font-awesome.min.css',
+            isAlreadyLoaded: function() { return false; }
+        },
+        {
+            url: NETDATA.themes.current.dashboard_css,
+            isAlreadyLoaded: function() { return false; }
+        },
+        {
+            url: NETDATA.serverDefault + 'css/bootstrap-toggle.min.css',
+            isAlreadyLoaded: function() { return false; }
+        }
+    ];
+
+    NETDATA.loadRequiredJs = function(index, callback) {
+        if(index >= NETDATA.requiredJs.length)  {
+            if(typeof callback === 'function')
+                callback();
+            return;
+        }
+
+        if(NETDATA.requiredJs[index].isAlreadyLoaded()) {
+            NETDATA.loadRequiredJs(++index, callback);
+            return;
+        }
+
+        if(NETDATA.options.debug.main_loop === true)
+            console.log('loading ' + NETDATA.requiredJs[index].url);
+
+        $.ajax({
+            url: NETDATA.requiredJs[index].url,
+            cache: true,
+            dataType: "script",
+            xhrFields: { withCredentials: true } // required for the cookie
+        })
+        .success(function() {
+            if(NETDATA.options.debug.main_loop === true)
+                console.log('loaded ' + NETDATA.requiredJs[index].url);
+
+            NETDATA.loadRequiredJs(++index, callback);
+        })
+        .fail(function() {
+            alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
+        })
+    };
+
+    NETDATA.loadRequiredCSS = function(index) {
+        if(index >= NETDATA.requiredCSS.length)
+            return;
+
+        if(NETDATA.requiredCSS[index].isAlreadyLoaded()) {
+            NETDATA.loadRequiredCSS(++index);
+            return;
+        }
+
+        if(NETDATA.options.debug.main_loop === true)
+            console.log('loading ' + NETDATA.requiredCSS[index].url);
+
+        NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
+        NETDATA.loadRequiredCSS(++index);
+    };
+
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // 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
+        person_urls: null,
+
+        parsePersonUrls: function(person_urls) {
+            // console.log(person_urls);
+            NETDATA.registry.person_urls = person_urls;
+
+            if(person_urls) {
+                NETDATA.registry.machines = {};
+                NETDATA.registry.machines_array = new Array();
+
+                var now = new Date().getTime();
+                var apu = person_urls;
+                var i = apu.length;
+                while(i--) {
+                    if(typeof NETDATA.registry.machines[apu[i][0]] === 'undefined') {
+                        // console.log('adding: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
+
+                        var obj = {
+                            guid: apu[i][0],
+                            url: apu[i][1],
+                            last_t: apu[i][2],
+                            accesses: apu[i][3],
+                            name: apu[i][4],
+                            alternate_urls: new Array()
+                        };
+                        obj.alternate_urls.push(apu[i][1]);
+
+                        NETDATA.registry.machines[apu[i][0]] = obj;
+                        NETDATA.registry.machines_array.push(obj);
+                    }
+                    else {
+                        // console.log('appending: ' + apu[i][4] + ', ' + ((now - apu[i][2]) / 1000).toString());
+
+                        var pu = NETDATA.registry.machines[apu[i][0]];
+                        if(pu.last_t < apu[i][2]) {
+                            pu.url = apu[i][1];
+                            pu.last_t = apu[i][2];
+                            pu.name = apu[i][4];
+                        }
+                        pu.accesses += apu[i][3];
+                        pu.alternate_urls.push(apu[i][1]);
+                    }
+                }
+            }
+
+            if(typeof netdataRegistryCallback === 'function')
+                netdataRegistryCallback(NETDATA.registry.machines_array);
+        },
+
+        init: function() {
+            if(typeof netdataNoRegistry !== 'undefined' && netdataNoRegistry)
+                return;
+
+            NETDATA.registry.hello(NETDATA.serverDefault, function(data) {
+                if(data) {
+                    NETDATA.registry.server = data.registry;
+                    NETDATA.registry.machine_guid = data.machine_guid;
+                    NETDATA.registry.hostname = data.hostname;
+
+                    NETDATA.registry.access(2, function (person_urls) {
+                        NETDATA.registry.parsePersonUrls(person_urls);
+
+                    });
+                }
+            });
+        },
+
+        hello: function(host, callback) {
+            while(host.slice(-1) === '/')
+                host = host.substring(0, host.length - 1);
+
+            // send HELLO to a netdata server:
+            // 1. verifies the server is reachable
+            // 2. responds with the registry URL, the machine GUID of this netdata server and its hostname
+            $.ajax({
+                    url: host + '/api/v1/registry?action=hello',
+                    async: true,
+                    cache: false,
+                    xhrFields: { withCredentials: true } // required for the cookie
+                })
+                .done(function(data) {
+                    if(typeof data.status !== 'string' || data.status !== 'ok') {
+                        NETDATA.error(408, host + ' response: ' + JSON.stringify(data));
+                        data = null;
+                    }
+
+                    if(typeof callback === 'function')
+                        callback(data);
+                })
+                .fail(function() {
+                    NETDATA.error(407, host);
+
+                    if(typeof callback === 'function')
+                        callback(null);
+                });
+        },
+
+        access: function(max_redirects, callback) {
+            // send ACCESS to a netdata registry:
+            // 1. it lets it know we are accessing a netdata server (its machine GUID and its URL)
+            // 2. it responds with a list of netdata servers we know
+            // the registry identifies us using a cookie it sets the first time we access it
+            // the registry may respond with a redirect URL to send us to another registry
+            $.ajax({
+                    url: NETDATA.registry.server + '/api/v1/registry?action=access&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault), // + '&visible_url=' + encodeURIComponent(document.location),
+                    async: true,
+                    cache: false,
+                    xhrFields: { withCredentials: true } // required for the cookie
+                })
+                .done(function(data) {
+                    var redirect = null;
+                    if(typeof data.registry === 'string')
+                        redirect = data.registry;
+
+                    if(typeof data.status !== 'string' || data.status !== 'ok') {
+                        NETDATA.error(409, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
+                        data = null;
+                    }
+
+                    if(data === null) {
+                        if(redirect !== null && max_redirects > 0) {
+                            NETDATA.registry.server = redirect;
+                            NETDATA.registry.access(max_redirects - 1, callback);
+                        }
+                        else {
+                            if(typeof callback === 'function')
+                                callback(null);
+                        }
+                    }
+                    else {
+                        if(typeof data.person_guid === 'string')
+                            NETDATA.registry.person_guid = data.person_guid;
+
+                        if(typeof callback === 'function')
+                            callback(data.urls);
+                    }
+                })
+                .fail(function() {
+                    NETDATA.error(410, NETDATA.registry.server);
+
+                    if(typeof callback === 'function')
+                        callback(null);
+                });
+        },
+
+        delete: function(delete_url, callback) {
+            // send DELETE to a netdata registry:
+            $.ajax({
+                url: NETDATA.registry.server + '/api/v1/registry?action=delete&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&delete_url=' + encodeURIComponent(delete_url),
+                async: true,
+                cache: false,
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+                .done(function(data) {
+                    if(typeof data.status !== 'string' || data.status !== 'ok') {
+                        NETDATA.error(411, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
+                        data = null;
+                    }
+
+                    if(typeof callback === 'function')
+                        callback(data);
+                })
+                .fail(function() {
+                    NETDATA.error(412, NETDATA.registry.server);
+
+                    if(typeof callback === 'function')
+                        callback(null);
+                });
+        },
+
+        switch: function(new_person_guid, callback) {
+            // impersonate
+            $.ajax({
+                url: NETDATA.registry.server + '/api/v1/registry?action=switch&machine=' + NETDATA.registry.machine_guid + '&name=' + encodeURIComponent(NETDATA.registry.hostname) + '&url=' + encodeURIComponent(NETDATA.serverDefault) + '&to=' + new_person_guid,
+                async: true,
+                cache: false,
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+                .done(function(data) {
+                    if(typeof data.status !== 'string' || data.status !== 'ok') {
+                        NETDATA.error(413, NETDATA.registry.server + ' responded with: ' + JSON.stringify(data));
+                        data = null;
+                    }
+
+                    if(typeof callback === 'function')
+                        callback(data);
+                })
+                .fail(function() {
+                    NETDATA.error(414, NETDATA.registry.server);
+
+                    if(typeof callback === 'function')
+                        callback(null);
+                });
+        }
+    };
+
+    // ----------------------------------------------------------------------------------------------------------------
+    // Boot it!
+
+    NETDATA.errorReset();
+    NETDATA.loadRequiredCSS(0);
+
+    NETDATA._loadjQuery(function() {
+        NETDATA.loadRequiredJs(0, function() {
+            if(typeof $().emulateTransitionEnd !== 'function') {
+                // bootstrap is not available
+                NETDATA.options.current.show_help = false;
+            }
+
+            if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
+                if(NETDATA.options.debug.main_loop === true)
+                    console.log('starting chart refresh thread');
+
+                NETDATA.start();
+            }
+        });
+    });
+
+    // window.NETDATA = NETDATA;
 // })(window, document);
index fc1f9254209acd69301a069bfa876e273348e768..aa1f8c967862b161877a6c59a0a68025f8e4c67d 100644 (file)
@@ -1,51 +1,51 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>NetData Dashboard</title>
-       <meta name="application-name" content="netdata">
+    <title>NetData Dashboard</title>
+    <meta name="application-name" content="netdata">
 
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-       <meta name="author" content="costa@tsaousis.gr">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+    <meta name="author" content="costa@tsaousis.gr">
 
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
-       
-       <script type="text/javascript" src="dashboard.js?v39"></script>
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+    
+    <script type="text/javascript" src="dashboard.js?v39"></script>
 </head>
 <body>
 
 <div style="width: 100%; text-align: center;">
-       <div data-netdata="netdata.server_cpu"
-                       data-dimensions="user"
-                       data-chart-library="gauge"
-                       data-width="150px"
-                       data-after="-60"
-                       data-points="60"
-                       data-title="Yes! Realtime!"
-                       data-units="I am alive!"
-                       data-colors="#FF5555"
-                       ></div>
-       <br/>
-       <div data-netdata="netdata.server_cpu"
-                       data-dimensions="user"
-                       data-chart-library="dygraph"
-                       data-dygraph-theme="sparkline"
-                       data-width="200px"
-                       data-height="30px"
-                       data-after="-60"
-                       data-points="60"
-                       data-colors="#FF5555"
-                       ></div>
+    <div data-netdata="netdata.server_cpu"
+            data-dimensions="user"
+            data-chart-library="gauge"
+            data-width="150px"
+            data-after="-60"
+            data-points="60"
+            data-title="Yes! Realtime!"
+            data-units="I am alive!"
+            data-colors="#FF5555"
+            ></div>
+    <br/>
+    <div data-netdata="netdata.server_cpu"
+            data-dimensions="user"
+            data-chart-library="dygraph"
+            data-dygraph-theme="sparkline"
+            data-width="200px"
+            data-height="30px"
+            data-after="-60"
+            data-points="60"
+            data-colors="#FF5555"
+            ></div>
 </div>
 </body>
 </html>
index f184321c0a3c657146e5cbcde8cda5a8370154fc..dbb97cd7ff88f8106b80ff3c8e47bba02af812ba 100644 (file)
 <!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>NetData Dashboard</title>
-       <meta name="application-name" content="netdata">
+    <title>NetData Dashboard</title>
+    <meta name="application-name" content="netdata">
 
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-       <meta name="author" content="costa@tsaousis.gr">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+    <meta name="author" content="costa@tsaousis.gr">
 
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
 
-       <script>var netdataTheme = 'slate';</script>
-       <script type="text/javascript" src="http://my-netdata.io/dashboard.js?v40"></script>
+    <script>var netdataTheme = 'slate';</script>
+    <script type="text/javascript" src="http://my-netdata.io/dashboard.js?v40"></script>
 </head>
 <body>
 
 <div class="container" style="width: 90%; padding-top: 10px; text-align: center; color: #AAA">
-       <div style="font-size: 7vw;">why netdata?</div>
-       <br/>
-       <div style="font-size: 2vw; color: white;">These charts visualize the same data...</div>
+    <div style="font-size: 7vw;">why netdata?</div>
+    <br/>
+    <div style="font-size: 2vw; color: white;">These charts visualize the same data...</div>
 
 
-       <!-- Nav tabs -->
-       <ul class="nav nav-tabs" role="tablist">
-               <li role="presentation" class="active"><a href="#gauge" aria-controls="gauge" role="tab" data-toggle="tab">Gauge.js</a></li>
-               <li role="presentation"><a href="#easypiechart" aria-controls="easypiechart" role="tab" data-toggle="tab">Easy Pie Chart</a></li>
-       </ul>
+    <!-- Nav tabs -->
+    <ul class="nav nav-tabs" role="tablist">
+        <li role="presentation" class="active"><a href="#gauge" aria-controls="gauge" role="tab" data-toggle="tab">Gauge.js</a></li>
+        <li role="presentation"><a href="#easypiechart" aria-controls="easypiechart" role="tab" data-toggle="tab">Easy Pie Chart</a></li>
+    </ul>
 
-       <!-- Tab panes -->
-       <div class="tab-content">
-               <div role="tabpanel" class="tab-pane active" id="gauge">
+    <!-- Tab panes -->
+    <div class="tab-content">
+        <div role="tabpanel" class="tab-pane active" id="gauge">
 
-                       <div style="display: inline-block; width: 35.8%">
-                               <div style="font-size: 1.2vw; color: #666; padding-top: 10px;"><i class="fa fa-comment"></i> I can trace an issue like this</div>
-                               <br/>
-                               <div data-netdata="example.random2"
-                                               data-dimensions="random"
-                                               data-chart-library="gauge"
-                                               data-gauge-max-value="32767"
-                                               data-width="100%"
-                                               data-after="-600"
-                                               data-points="600"
-                                               data-title="1/second (netdata&nbsp;default)"
-                                               data-units="important metric"
-                                               data-colors="#5A5"
-                                               ></div>
-                       </div>
-                       <div style="display: inline-block; width: 50%">
-                               <div style="font-size: 1.2vw; color: #666;"><i class="fa fa-comment"></i> Can you trace an issue like these?<br/>&nbsp;<br/></div>
-                               <div data-netdata="example.random2"
-                                               data-dimensions="random"
-                                               data-chart-library="gauge"
-                                               data-gauge-max-value="32767"
-                                               data-width="45%"
-                                               data-after="-600"
-                                               data-points="60"
-                                               data-title="Updates Every 10&nbsp;Sec"
-                                               data-units="important metric"
-                                               data-colors="#C55"
-                                               ></div>
-                               <div data-netdata="example.random2"
-                                               data-dimensions="random"
-                                               data-chart-library="gauge"
-                                               data-gauge-max-value="32767"
-                                               data-width="45%"
-                                               data-after="-600"
-                                               data-points="2"
-                                               data-title="Updates Every 5&nbsp;Mins"
-                                               data-units="important metric"
-                                               data-colors="#C55"
-                                               ></div>
-                       </div>
-               </div>
-               <div role="tabpanel" class="tab-pane" id="easypiechart">
+            <div style="display: inline-block; width: 35.8%">
+                <div style="font-size: 1.2vw; color: #666; padding-top: 10px;"><i class="fa fa-comment"></i> I can trace an issue like this</div>
+                <br/>
+                <div data-netdata="example.random2"
+                        data-dimensions="random"
+                        data-chart-library="gauge"
+                        data-gauge-max-value="32767"
+                        data-width="100%"
+                        data-after="-600"
+                        data-points="600"
+                        data-title="1/second (netdata&nbsp;default)"
+                        data-units="important metric"
+                        data-colors="#5A5"
+                        ></div>
+            </div>
+            <div style="display: inline-block; width: 50%">
+                <div style="font-size: 1.2vw; color: #666;"><i class="fa fa-comment"></i> Can you trace an issue like these?<br/>&nbsp;<br/></div>
+                <div data-netdata="example.random2"
+                        data-dimensions="random"
+                        data-chart-library="gauge"
+                        data-gauge-max-value="32767"
+                        data-width="45%"
+                        data-after="-600"
+                        data-points="60"
+                        data-title="Updates Every 10&nbsp;Sec"
+                        data-units="important metric"
+                        data-colors="#C55"
+                        ></div>
+                <div data-netdata="example.random2"
+                        data-dimensions="random"
+                        data-chart-library="gauge"
+                        data-gauge-max-value="32767"
+                        data-width="45%"
+                        data-after="-600"
+                        data-points="2"
+                        data-title="Updates Every 5&nbsp;Mins"
+                        data-units="important metric"
+                        data-colors="#C55"
+                        ></div>
+            </div>
+        </div>
+        <div role="tabpanel" class="tab-pane" id="easypiechart">
 
-                       <div style="display: inline-block; width: 25%">
-                               <div style="font-size: 1.2vw; color: #666; padding-top: 10px;"><i class="fa fa-comment"></i> I can trace an issue like this</div>
-                               <br/>
-                               <div data-netdata="example.random2"
-                                               data-dimensions="random"
-                                               data-chart-library="easypiechart"
-                                               data-easypiechart-max-value="32767"
-                                               data-width="100%"
-                                               data-after="-600"
-                                               data-points="600"
-                                               data-title="1/second (netdata&nbsp;default)"
-                                               data-units="important metric"
-                                               data-colors="#5A5"
-                                               ></div>
-                       </div>
-                       <div style="display: inline-block; width: 40%">
-                               <div style="font-size: 1.2vw; color: #666;"><i class="fa fa-comment"></i> Can you trace an issue like these?<br/>&nbsp;<br/></div>
-                               <div data-netdata="example.random2"
-                                               data-dimensions="random"
-                                               data-chart-library="easypiechart"
-                                               data-easypiechart-max-value="32767"
-                                               data-width="45%"
-                                               data-after="-600"
-                                               data-points="60"
-                                               data-title="Updates Every 10&nbsp;Sec"
-                                               data-units="important metric"
-                                               data-colors="#C55"
-                                               ></div>
-                               <div data-netdata="example.random2"
-                                               data-dimensions="random"
-                                               data-chart-library="easypiechart"
-                                               data-easypiechart-max-value="32767"
-                                               data-width="45%"
-                                               data-after="-600"
-                                               data-points="2"
-                                               data-title="Updates Every 5&nbsp;Mins"
-                                               data-units="important metric"
-                                               data-colors="#C55"
-                                               ></div>
-                       </div>
-               </div>
-       </div>
-       <div style="font-size: 1.5vw;">Hover on the chart below, to see the selected value on the charts above!</div>
-       <div data-netdata="example.random2"
-                       data-dimensions="random"
-                       data-dygraph-theme="sparkline"
-                       data-width="100%"
-                       data-height="20vh"
-                       data-after="-600"
-                       data-points="600"
-                       data-title="1/second (netdata&nbsp;default)"
-                       data-units="something"
-                       data-colors="#888"
-                       ></div>
+            <div style="display: inline-block; width: 25%">
+                <div style="font-size: 1.2vw; color: #666; padding-top: 10px;"><i class="fa fa-comment"></i> I can trace an issue like this</div>
+                <br/>
+                <div data-netdata="example.random2"
+                        data-dimensions="random"
+                        data-chart-library="easypiechart"
+                        data-easypiechart-max-value="32767"
+                        data-width="100%"
+                        data-after="-600"
+                        data-points="600"
+                        data-title="1/second (netdata&nbsp;default)"
+                        data-units="important metric"
+                        data-colors="#5A5"
+                        ></div>
+            </div>
+            <div style="display: inline-block; width: 40%">
+                <div style="font-size: 1.2vw; color: #666;"><i class="fa fa-comment"></i> Can you trace an issue like these?<br/>&nbsp;<br/></div>
+                <div data-netdata="example.random2"
+                        data-dimensions="random"
+                        data-chart-library="easypiechart"
+                        data-easypiechart-max-value="32767"
+                        data-width="45%"
+                        data-after="-600"
+                        data-points="60"
+                        data-title="Updates Every 10&nbsp;Sec"
+                        data-units="important metric"
+                        data-colors="#C55"
+                        ></div>
+                <div data-netdata="example.random2"
+                        data-dimensions="random"
+                        data-chart-library="easypiechart"
+                        data-easypiechart-max-value="32767"
+                        data-width="45%"
+                        data-after="-600"
+                        data-points="2"
+                        data-title="Updates Every 5&nbsp;Mins"
+                        data-units="important metric"
+                        data-colors="#C55"
+                        ></div>
+            </div>
+        </div>
+    </div>
+    <div style="font-size: 1.5vw;">Hover on the chart below, to see the selected value on the charts above!</div>
+    <div data-netdata="example.random2"
+            data-dimensions="random"
+            data-dygraph-theme="sparkline"
+            data-width="100%"
+            data-height="20vh"
+            data-after="-600"
+            data-points="600"
+            data-title="1/second (netdata&nbsp;default)"
+            data-units="something"
+            data-colors="#888"
+            ></div>
 </div>
 </body>
 </html>
index 78c47034d4540b56a4448f660e36527d13ef1696..859740471a521f7f4751a913489a8c002dd8c94a 100644 (file)
 <!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>NetData - Real-time performance monitoring, done right!</title>
-       <meta name="application-name" content="netdata">
+    <title>NetData - Real-time performance monitoring, done right!</title>
+    <meta name="application-name" content="netdata">
 
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
 
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
 
-       <script>
-       // --- OPTIONS FOR THE DASHBOARD --
+    <script>
+    // --- OPTIONS FOR THE DASHBOARD --
 
-       // this section has to appear before loading dashboard.js
+    // this section has to appear before loading dashboard.js
 
-       // Select a theme.
-       // uncomment on of the two themes:
+    // Select a theme.
+    // uncomment on of the two themes:
 
-       // var netdataTheme = 'default'; // this is white
-       var netdataTheme = 'slate'; // this is dark
+    // var netdataTheme = 'default'; // this is white
+    var netdataTheme = 'slate'; // this is dark
 
 
-       // Set the default netdata server.
-       // on charts without a 'data-host', this one will be used.
-       // the default is the server that dashboard.js is downloaded from.
+    // Set the default netdata server.
+    // on charts without a 'data-host', this one will be used.
+    // the default is the server that dashboard.js is downloaded from.
 
-       // var netdataServer = 'http://my.server:19999/';
-       </script>
+    // var netdataServer = 'http://my.server:19999/';
+    </script>
 
-       <!--
-               --- LOAD dashboard.js ---
+    <!--
+        --- LOAD dashboard.js ---
 
-               to host this HTML file on your web server,
-               you have to load dashboard.js from the netdata server.
+        to host this HTML file on your web server,
+        you have to load dashboard.js from the netdata server.
 
-               So, pick one the two below
-               If you pick the first, set the server name/IP.
+        So, pick one the two below
+        If you pick the first, set the server name/IP.
 
-               The second assumes you host this file on /usr/share/netdata/web
-               and that you have chown it to be owned by netdata:netdata
-       -->
-       <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-       <script type="text/javascript" src="dashboard.js?v41"></script>
+        The second assumes you host this file on /usr/share/netdata/web
+        and that you have chown it to be owned by netdata:netdata
+    -->
+    <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
+    <script type="text/javascript" src="dashboard.js?v41"></script>
 
-       <script>
-       // --- OPTIONS FOR THE CHARTS --
+    <script>
+    // --- OPTIONS FOR THE CHARTS --
 
-       // destroy charts not shown (lowers memory on the browsers)
-       // set this to 'true' to destroy, 'false' to hide the charts
-       NETDATA.options.current.destroy_on_hide = true;
-       
-       // set this to false, to always show all dimensions
-       NETDATA.options.current.eliminate_zero_dimensions = true;
-       
-       // set this to false, to lower the pressure on the browser
-       NETDATA.options.current.concurrent_refreshes = false;
+    // destroy charts not shown (lowers memory on the browsers)
+    // set this to 'true' to destroy, 'false' to hide the charts
+    NETDATA.options.current.destroy_on_hide = true;
+    
+    // set this to false, to always show all dimensions
+    NETDATA.options.current.eliminate_zero_dimensions = true;
+    
+    // set this to false, to lower the pressure on the browser
+    NETDATA.options.current.concurrent_refreshes = false;
 
-       // if you need to support slow mobile phones, set this to false
-       NETDATA.options.current.parallel_refresher = true;
+    // if you need to support slow mobile phones, set this to false
+    NETDATA.options.current.parallel_refresher = true;
 
-       // set this to false, to always update the charts, even if focus is lost
-       NETDATA.options.current.stop_updates_when_focus_is_lost = true;
-       </script>
+    // set this to false, to always update the charts, even if focus is lost
+    NETDATA.options.current.stop_updates_when_focus_is_lost = true;
+    </script>
 
-       <style>
+    <style>
 
 body {
-       font-size: 1vw;
+    font-size: 1vw;
 }
 
 .mysparkline {
-       position: relative;
-       display: inline-block;
-       min-height: 50px;
-       width: 100%;
-       height: 8vmax;
-       text-align: left;
+    position: relative;
+    display: inline-block;
+    min-height: 50px;
+    width: 100%;
+    height: 8vmax;
+    text-align: left;
 }
 
 .mysparkline-overchart-label {
-       position: absolute;
-       display: block;
-       top: 0;
-       left: 10px;
-       bottom: 0;
-       right: 0;
-       font-size: 1vmax;
-       z-index: 1;
+    position: absolute;
+    display: block;
+    top: 0;
+    left: 10px;
+    bottom: 0;
+    right: 0;
+    font-size: 1vmax;
+    z-index: 1;
 }
 
 .mysparkline-overchart-value {
-       position: absolute;
-       display: block;
-       top: 1.1vmax;
-       left: 10px;
-       bottom: 0;
-       right: 0;
-       font-size: 5vmax;
-       z-index: 2;
-       text-shadow: #333 0px 0px 2px;
+    position: absolute;
+    display: block;
+    top: 1.1vmax;
+    left: 10px;
+    bottom: 0;
+    right: 0;
+    font-size: 5vmax;
+    z-index: 2;
+    text-shadow: #333 0px 0px 2px;
 }
 
 .myfullchart {
-       position: relative;
-       display: inline-block;
-       width: 100%;
-       height: 14vmax;
-       min-height: 150px;
-       text-align: left;
+    position: relative;
+    display: inline-block;
+    width: 100%;
+    height: 14vmax;
+    min-height: 150px;
+    text-align: left;
 }
 
 .mygauge-combo {
-       display: inline-block;
+    display: inline-block;
 }
 
 .mygauge {
-       position: relative;
-       display: block;
-       width: 18vw;
-       height: 11vw;
+    position: relative;
+    display: block;
+    width: 18vw;
+    height: 11vw;
 }
 
 .mygauge-button {
-       display: block;
+    display: block;
 }
 
 .mytitle {
-       padding-top: 6vw;
-       padding-bottom: 1vw;
-       text-align: center;
-       font-size: 2.4vw;
+    padding-top: 6vw;
+    padding-bottom: 1vw;
+    text-align: center;
+    font-size: 2.4vw;
 }
 
 .mysubtitle {
-       padding-top: 2vw;
-       padding-bottom: 1vw;
-       text-align: center;
-       font-size: 1.8vw;
+    padding-top: 2vw;
+    padding-bottom: 1vw;
+    text-align: center;
+    font-size: 1.8vw;
 }
 
 .mycontent {
-       text-align: center;
-       font-size: 1.5vw;
+    text-align: center;
+    font-size: 1.5vw;
 }
 
 @media only screen and (min-width : 992px) {
-       .container {
-               width: 90%;
-       }
+    .container {
+        width: 90%;
+    }
 }
 @media only screen and (max-width : 992px) {
-       .container {
-               width: 100%;
-       }
+    .container {
+        width: 100%;
+    }
 }
-       </style>
+    </style>
 
 </head>
 <body style="text-align: center;">
 
 <div class="container">
 
-       <div style="text-align: center; font-size: 13vw; height: 14vw;">
-               <b>netdata</b>
-       </div>
-       <div style="text-align: center; font-size: 2vw; height: 2.5vw;">
-               real-time performance monitoring
-       </div>
-       <div style="width:80%; text-align: right; font-size: 2.7vw;">
-               <strong>scaled out</strong>!
-       </div>
-       <div class="mytitle">
-               pick a <b>netdata</b> demo server
-       </div>
-       <div class="mycontent">
-               these demo servers show what you will get by installing <b>netdata</b>
-       </div>
-
-       <div style="width: 100%; text-align: center; padding-top: 2vw;">
-               <div style="width: 100%; text-align: center;">
-
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//london.my-netdata.io"
-                                                       data-title="EU - London"
-                                                       data-chart-library="gauge"
-                                                       data-width="100%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#558855"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//london.my-netdata.io/default.html'" style="font-size: 1.0vw;">Enter London!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//atlanta.my-netdata.io"
-                                                       data-title="US - Atlanta"
-                                                       data-chart-library="gauge"
-                                                       data-width="100%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#AA5555"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//atlanta.my-netdata.io/default.html'" style="font-size: 1.0vw;">Enter Atlanta!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by CDN77.com
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//sanfrancisco.netdata.rocks"
-                                                       data-title="US - California"
-                                                       data-chart-library="gauge"
-                                                       data-width="100%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#5555AA"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//sanfrancisco.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter California!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//toronto.netdata.rocks"
-                                                       data-title="Canada"
-                                                       data-chart-library="gauge"
-                                                       data-width="100%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#885588"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//toronto.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Canada!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-                       <br/>&nbsp;<br/>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//frankfurt.netdata.rocks"
-                                                       data-title="EU - Germany"
-                                                       data-chart-library="easypiechart"
-                                                       data-width="75%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#AAAA55"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//frankfurt.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Germany!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//newyork.netdata.rocks"
-                                                       data-title="US - New York"
-                                                       data-chart-library="easypiechart"
-                                                       data-width="75%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#BB5533"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//newyork.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter New York!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//singapore.netdata.rocks"
-                                                       data-title="Signapore"
-                                                       data-chart-library="easypiechart"
-                                                       data-width="75%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#5588BB"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//singapore.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Singapore!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="mygauge-combo">
-                               <div class="mygauge">
-                                       <div data-netdata="netdata.requests"
-                                                       data-host="//bangalore.netdata.rocks"
-                                                       data-title="India"
-                                                       data-chart-library="easypiechart"
-                                                       data-width="75%"
-                                                       data-after="-300"
-                                                       data-points="300"
-                                                       data-colors="#BB55BB"
-                                                       ></div>
-                               </div>
-                               <div class="mygauge-button">
-                                       <br/>&nbsp;<br/>
-                                       <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//bangalore.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter India!</button>
-                                       <div style="font-size: 0.8vw;">
-                                               Donated by DigitalOcean.com
-                                       </div>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="mytitle">
-               this page is a custom <b>netdata</b> dashboard
-       </div>
-       <div class="mycontent">
-               charts are coming from 8 servers, in parallel
-               <br/>
-               the servers are not aware of this multi-server dashboard,
-               <br/>
-               each server is not aware of the other servers,
-               <br/>
-               but on this dashboard <b>they are one</b>!
-       </div>
-       <div style="padding-top: 1vw; width: 100%; text-align: center; font-size: 1.5vw;">
-               <i class="fa fa-comment" aria-hidden="true"></i>
-               hover on a chart below, or drag it to show the past - <b>the others will follow</b>!
-               <br/>
-               double click on a chart to reset them all
-       </div>
-
-       <div class="mytitle">
-               our <code>nginx</code> performance
-       </div>
-       <div class="mycontent">
-               (we proxy netdata through nginx, on the demo sites)
-       </div>
-
-       <!-- Nav tabs -->
-       <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;">
-               <li role="presentation" class="active"><a href="#nginx_requests" aria-controls="nginx_requests" role="tab" data-toggle="tab">Requests</a></li>
-               <li role="presentation"><a href="#nginx_connections" aria-controls="nginx_connections" role="tab" data-toggle="tab">Connections</a></li>
-       </ul>
-
-       <!-- Tab panes -->
-       <div class="tab-content">
-               <div role="tabpanel" class="tab-pane active" id="nginx_requests">
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>EU - London</b> web requests/s
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.requests.netdata" >
-                               </div>
-                               <div data-netdata="nginx.requests"
-                                               data-dimensions="requests"
-                                               data-host="//london.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#558855"
-                                               data-show-value-of-requests-at="nginx.requests.netdata"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - Atlanta</b> web requests/s
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.requests.netdata2" >
-                               </div>
-                               <div data-netdata="nginx.requests"
-                                               data-dimensions="requests"
-                                               data-host="//atlanta.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#AA5555"
-                                               data-show-value-of-requests-at="nginx.requests.netdata2"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - California</b> web requests/s
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.requests.netdata3" >
-                               </div>
-                               <div data-netdata="nginx.requests"
-                                               data-dimensions="requests"
-                                               data-host="//sanfrancisco.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#5555AA"
-                                               data-show-value-of-requests-at="nginx.requests.netdata3"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>Canada</b> web requests/s
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.requests.netdata4" >
-                               </div>
-                               <div data-netdata="nginx.requests"
-                                               data-dimensions="requests"
-                                               data-host="//toronto.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#885588"
-                                               data-show-value-of-requests-at="nginx.requests.netdata4"
-                                               ></div>
-                       </div>
-               </div>
-
-               <div role="tabpanel" class="tab-pane" id="nginx_connections">
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>EU - London</b> active connections
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.connections.netdata1" >
-                               </div>
-                               <div data-netdata="nginx.connections"
-                                               data-dimensions="active"
-                                               data-host="//london.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#558855"
-                                               data-show-value-of-active-at="nginx.connections.netdata1"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - Atlanta</b> active connections
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.connections.netdata2" >
-                               </div>
-                               <div data-netdata="nginx.connections"
-                                               data-dimensions="active"
-                                               data-host="//atlanta.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#AA5555"
-                                               data-show-value-of-active-at="nginx.connections.netdata2"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - California</b> active connections
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.connections.netdata3" >
-                               </div>
-                               <div data-netdata="nginx.connections"
-                                               data-dimensions="active"
-                                               data-host="//sanfrancisco.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#5555AA"
-                                               data-show-value-of-active-at="nginx.connections.netdata3"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>Canada</b> active connections
-                               </div>
-                               <div class="mysparkline-overchart-value" id="nginx.connections.netdata4" >
-                               </div>
-                               <div data-netdata="nginx.connections"
-                                               data-dimensions="active"
-                                               data-host="//toronto.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#885588"
-                                               data-show-value-of-active-at="nginx.connections.netdata4"
-                                               ></div>
-                       </div>
-               </div>
-       </div>
-
-       <div style="width: 100%; text-align: right; font-size: 1vw;">
-               <i class="fa fa-comment" aria-hidden="true"></i> these charts are draggable and touchable, double click them to reset them
-       </div>
-
-
-       <div class="mytitle">
-               bandwidth consumption on the demo sites
-       </div>
-       <div class="mycontent">
-               Linux QoS is configured by <a href="https://github.com/firehol/netdata/wiki/You-should-install-QoS-on-all-your-servers">FireQOS</a>
-       </div>
-
-       <!-- Nav tabs -->
-       <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;">
-               <li role="presentation" class="active"><a href="#outbout" aria-controls="outbout" role="tab" data-toggle="tab">Outbound</a></li>
-               <li role="presentation"><a href="#inbound" aria-controls="inbound" role="tab" data-toggle="tab">Inbound</a></li>
-       </ul>
-
-       <!-- Tab panes -->
-       <div class="tab-content">
-               <div role="tabpanel" class="tab-pane active" id="outbout">
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_out"
-                                       data-host="//london.my-netdata.io"
-                                       data-chart-library="dygraph"
-                                       data-title="EU - London, traffic we send per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       </div>
-
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_out"
-                                       data-host="//atlanta.my-netdata.io"
-                                       data-chart-library="dygraph"
-                                       data-title="US - Atlanta, traffic we send per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-
-                       </div>
-
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_out"
-                                       data-host="//sanfrancisco.netdata.rocks"
-                                       data-chart-library="dygraph"
-                                       data-title="US - California, traffic we send per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       </div>
-
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_out"
-                                       data-host="//toronto.netdata.rocks"
-                                       data-chart-library="dygraph"
-                                       data-title="Canada, traffic we send per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       </div>
-               </div>
-
-               <div role="tabpanel" class="tab-pane" id="inbound">
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_in"
-                                       data-host="//london.my-netdata.io"
-                                       data-chart-library="dygraph"
-                                       data-title="EU - London, traffic we receive per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-
-                       </div>
-
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_in"
-                                       data-host="//atlanta.my-netdata.io"
-                                       data-chart-library="dygraph"
-                                       data-title="US - Atlanta, traffic we receive per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-
-                       </div>
-
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_in"
-                                       data-host="//sanfrancisco.netdata.rocks"
-                                       data-chart-library="dygraph"
-                                       data-title="US - California, traffic we receive per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       </div>
-
-                       <div class="myfullchart">
-                               <div data-netdata="tc.world_in"
-                                       data-host="//toronto.netdata.rocks"
-                                       data-chart-library="dygraph"
-                                       data-title="Canada, traffic we receive per service"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       </div>
-               </div>
-       </div>
-       <div style="width: 100%; text-align: right; font-size: 1vw;">
-               <i class="fa fa-comment" aria-hidden="true"></i> <i>these legends are interactive and the charts are resizable here ^^^</i>
-       </div>
-
-       <div class="mytitle">
-               DDoS protection performance on the demo sites
-       </div>
-       <div class="mycontent">
-               iptables SYNPROXY configured by <a href="https://github.com/firehol/netdata/wiki/Monitoring-SYNPROXY">FireHOL</a>
-       </div>
-
-       <div style="padding-top: 4vw; width: 100%; text-align: center; font-size: 1.5vw;">
-
-               <div class="mysparkline">
-                       <div class="mysparkline-overchart-label">
-                               <b>EU - London</b>, TCP SYN packets/s received
-                       </div>
-                       <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata1" >
-                       </div>
-                       <div data-netdata="netfilter.synproxy_syn_received"
-                                       data-dimensions="received"
-                                       data-host="//london.my-netdata.io"
-                                       data-chart-library="dygraph"
-                                       data-dygraph-theme="sparkline"
-                                       data-dygraph-type="area"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       data-colors="#558855"
-                                       data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata1"
-                                       ></div>
-               </div>
-
-               <div class="mysparkline">
-                       <div class="mysparkline-overchart-label">
-                               <b>US - Atlanta</b>, TCP SYN packets/s received
-                       </div>
-                       <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata2" >
-                       </div>
-                       <div data-netdata="netfilter.synproxy_syn_received"
-                                       data-dimensions="received"
-                                       data-host="//atlanta.my-netdata.io"
-                                       data-chart-library="dygraph"
-                                       data-dygraph-theme="sparkline"
-                                       data-dygraph-type="area"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       data-colors="#885555"
-                                       data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata2"
-                                       ></div>
-               </div>
-
-               <div class="mysparkline">
-                       <div class="mysparkline-overchart-label">
-                               <b>US - California</b>, TCP SYN packets/s received
-                       </div>
-                       <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata3" >
-                       </div>
-                       <div data-netdata="netfilter.synproxy_syn_received"
-                                       data-dimensions="received"
-                                       data-host="//sanfrancisco.netdata.rocks"
-                                       data-chart-library="dygraph"
-                                       data-dygraph-theme="sparkline"
-                                       data-dygraph-type="area"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       data-colors="#555588"
-                                       data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata3"
-                                       ></div>
-               </div>
-
-               <div class="mysparkline">
-                       <div class="mysparkline-overchart-label">
-                               <b>Canada</b>, TCP SYN packets/s received
-                       </div>
-                       <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata4" >
-                       </div>
-                       <div data-netdata="netfilter.synproxy_syn_received"
-                                       data-dimensions="received"
-                                       data-host="//toronto.netdata.rocks"
-                                       data-chart-library="dygraph"
-                                       data-dygraph-theme="sparkline"
-                                       data-dygraph-type="area"
-                                       data-width="100%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       data-colors="#885588"
-                                       data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata4"
-                                       ></div>
-               </div>
-       </div>
-       <div style="width: 100%; text-align: right; font-size: 1vw;">
-               <i class="fa fa-comment" aria-hidden="true"></i> <i>did you notice the decimal numbers?
-               <br/>netdata interpolates collected values at second boundaries, with nanosecond detail!</i>
-       </div>
-
-
-       <div class="mytitle">
-               CPU Utilization of the demo sites
-       </div>
-
-       <div style="padding-top: 1vw;">
-               <div class="myfullchart">
-                       <div data-netdata="system.cpu"
-                               data-host="//london.my-netdata.io"
-                               data-chart-library="dygraph"
-                               data-title="EU - London, CPU Usage"
-                               data-width="100%"
-                               data-height="100%"
-                               data-after="-300"
-                               data-dygraph-valuerange="[0, 100]"
-                               ></div>
-               </div>
-
-               <div class="myfullchart">
-                       <div data-netdata="system.cpu"
-                               data-host="//atlanta.my-netdata.io"
-                               data-chart-library="dygraph"
-                               data-title="US - Atlanta, CPU Usage"
-                               data-width="100%"
-                               data-height="100%"
-                               data-after="-300"
-                               data-dygraph-valuerange="[0, 100]"
-                               ></div>
-               </div>
-
-               <div class="myfullchart">
-                       <div data-netdata="system.cpu"
-                               data-host="//sanfrancisco.netdata.rocks"
-                               data-chart-library="dygraph"
-                               data-title="US - California, CPU Usage"
-                               data-width="100%"
-                               data-height="100%"
-                               data-after="-300"
-                               data-dygraph-valuerange="[0, 100]"
-                               ></div>
-               </div>
-
-               <div class="myfullchart">
-                       <div data-netdata="system.cpu"
-                               data-host="//toronto.netdata.rocks"
-                               data-chart-library="dygraph"
-                               data-title="Canada, CPU Usage"
-                               data-width="100%"
-                               data-height="100%"
-                               data-after="-300"
-                               data-dygraph-valuerange="[0, 100]"
-                               ></div>
-               </div>
-       </div>
-       <div style="width: 100%; text-align: right; font-size: 1vw;">
-               <i class="fa fa-comment" aria-hidden="true"></i> <i>what is using so much CPU?
-               <br/>The site <a href="//iplists.firehol.org/">iplists.firehol.org</a> is maintained by FireHOL - the CPU is used for comparing security IP Lists.</i>
-       </div>
-
-       <div class="mytitle">
-               Netdata performance
-       </div>
-       <div class="mycontent">
-               netdata monitors <b>users</b>, <b>user groups</b>, <b>applications (process trees)</b>
-               <br/>
-               <b>containers</b> (<code>lxc</code>, <code>docker</code>, etc.) and SNMP devices.
-       </div>
-
-       <!-- Nav tabs -->
-       <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;">
-               <li role="presentation" class="active"><a href="#netdata_cpu" aria-controls="netdata_cpu" role="tab" data-toggle="tab">CPU</a></li>
-               <li role="presentation"><a href="#netdata_avgtime" aria-controls="netdata_avgtime" role="tab" data-toggle="tab">Average Response Time</a></li>
-       </ul>
-
-       <!-- Tab panes -->
-       <div class="tab-content">
-               <div role="tabpanel" class="tab-pane active" id="netdata_cpu">
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>EU - London</b>, CPU % of a single core
-                               </div>
-                               <div class="mysparkline-overchart-value" id="users.cpu.netdata1" >
-                               </div>
-                               <div data-netdata="apps.cpu"
-                                               data-dimensions="netdata"
-                                               data-host="//london.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#558855"
-                                               data-show-value-of-netdata-at="users.cpu.netdata1"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - Atlanta</b>, CPU % of a single core
-                               </div>
-                               <div class="mysparkline-overchart-value" id="users.cpu.netdata2" >
-                               </div>
-                               <div data-netdata="apps.cpu"
-                                               data-dimensions="netdata"
-                                               data-host="//atlanta.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#885555"
-                                               data-show-value-of-netdata-at="users.cpu.netdata2"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - California</b>, CPU % of a single core
-                               </div>
-                               <div class="mysparkline-overchart-value" id="users.cpu.netdata3" >
-                               </div>
-                               <div data-netdata="apps.cpu"
-                                               data-dimensions="netdata"
-                                               data-host="//sanfrancisco.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#555588"
-                                               data-show-value-of-netdata-at="users.cpu.netdata3"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>Toronto</b>, CPU % of a single core
-                               </div>
-                               <div class="mysparkline-overchart-value" id="users.cpu.netdata4" >
-                               </div>
-                               <div data-netdata="apps.cpu"
-                                               data-dimensions="netdata"
-                                               data-host="//toronto.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#885588"
-                                               data-show-value-of-netdata-at="users.cpu.netdata4"
-                                               ></div>
-                       </div>
-
-                       <div style="width: 100%; text-align: right; font-size: 1vw;">
-                               <i class="fa fa-comment" aria-hidden="true"></i> <i>this utilization is about the whole netdata process tree and the percentage is of <b>a single core</b>!
-                               <br/>including <b>BASH</b> plugins (it monitors <code>mysql</code> on the demo sites), <b>node.js</b> plugins (it monitors <code>bind9</code> on the demo sites), etc.
-                               <br/>and including the chart refreshes for the dashboards of all viewers.</i>
-                       </div>
-               </div>
-
-               <div role="tabpanel" class="tab-pane" id="netdata_avgtime">
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>EU - London</b>, API average response time in milliseconds
-                               </div>
-                               <div class="mysparkline-overchart-value" id="netdata.response_time1" >
-                               </div>
-                               <div data-netdata="netdata.response_time"
-                                               data-host="//london.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#558855"
-                                               data-show-value-of-response_time-at="netdata.response_time1"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - Atlanta</b>, API average response time in milliseconds
-                               </div>
-                               <div class="mysparkline-overchart-value" id="netdata.response_time2" >
-                               </div>
-                               <div data-netdata="netdata.response_time"
-                                               data-host="//atlanta.my-netdata.io"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#885555"
-                                               data-show-value-of-response_time-at="netdata.response_time2"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>US - California</b>, API average response time in milliseconds
-                               </div>
-                               <div class="mysparkline-overchart-value" id="netdata.response_time3" >
-                               </div>
-                               <div data-netdata="netdata.response_time"
-                                               data-host="//sanfrancisco.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#555588"
-                                               data-show-value-of-response_time-at="netdata.response_time3"
-                                               ></div>
-                       </div>
-
-                       <div class="mysparkline">
-                               <div class="mysparkline-overchart-label">
-                                       <b>Canada</b>, API average response time in milliseconds
-                               </div>
-                               <div class="mysparkline-overchart-value" id="netdata.response_time4" >
-                               </div>
-                               <div data-netdata="netdata.response_time"
-                                               data-host="//toronto.netdata.rocks"
-                                               data-chart-library="dygraph"
-                                               data-dygraph-theme="sparkline"
-                                               data-dygraph-type="area"
-                                               data-width="100%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-colors="#885588"
-                                               data-show-value-of-response_time-at="netdata.response_time4"
-                                               ></div>
-                       </div>
-
-                       <div style="width: 100%; text-align: right; font-size: 1vw;">
-                               <i class="fa fa-comment" aria-hidden="true"></i> <i>netdata is really <b>fast</b> (the values are milliseconds!)
-                               <br/>
-                               These values include everything, from the reception of the first byte to the dispatch of the last, including gzip compression.
-                               <br/>
-                               Values above 2-3ms are usually chart refreshes of charts with several dimensions, charts with very long durations (zoomed out), or file transfers.
-                               </i>
-                       </div>
-               </div>
-       </div>
-
-       <div style="padding-top: 6vw; width: 100%; text-align: center; font-size: 2vw;">
-               want to know more?
-               <br/>
-               jump to <a href="https://github.com/firehol/netdata/">the netdata page at github</a>
-               <br/>
-               it needs just 3 mins to be installed on your servers!
-               <br/>
-               &nbsp;
-       </div>
+    <div style="text-align: center; font-size: 13vw; height: 14vw;">
+        <b>netdata</b>
+    </div>
+    <div style="text-align: center; font-size: 2vw; height: 2.5vw;">
+        real-time performance monitoring
+    </div>
+    <div style="width:80%; text-align: right; font-size: 2.7vw;">
+        <strong>scaled out</strong>!
+    </div>
+    <div class="mytitle">
+        pick a <b>netdata</b> demo server
+    </div>
+    <div class="mycontent">
+        these demo servers show what you will get by installing <b>netdata</b>
+    </div>
+
+    <div style="width: 100%; text-align: center; padding-top: 2vw;">
+        <div style="width: 100%; text-align: center;">
+
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//london.my-netdata.io"
+                            data-title="EU - London"
+                            data-chart-library="gauge"
+                            data-width="100%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#558855"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//london.my-netdata.io/default.html'" style="font-size: 1.0vw;">Enter London!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//atlanta.my-netdata.io"
+                            data-title="US - Atlanta"
+                            data-chart-library="gauge"
+                            data-width="100%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#AA5555"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//atlanta.my-netdata.io/default.html'" style="font-size: 1.0vw;">Enter Atlanta!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by CDN77.com
+                    </div>
+                </div>
+            </div>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//sanfrancisco.netdata.rocks"
+                            data-title="US - California"
+                            data-chart-library="gauge"
+                            data-width="100%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#5555AA"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//sanfrancisco.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter California!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//toronto.netdata.rocks"
+                            data-title="Canada"
+                            data-chart-library="gauge"
+                            data-width="100%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#885588"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//toronto.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Canada!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+            <br/>&nbsp;<br/>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//frankfurt.netdata.rocks"
+                            data-title="EU - Germany"
+                            data-chart-library="easypiechart"
+                            data-width="75%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#AAAA55"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//frankfurt.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Germany!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//newyork.netdata.rocks"
+                            data-title="US - New York"
+                            data-chart-library="easypiechart"
+                            data-width="75%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#BB5533"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//newyork.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter New York!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//singapore.netdata.rocks"
+                            data-title="Signapore"
+                            data-chart-library="easypiechart"
+                            data-width="75%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#5588BB"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//singapore.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter Singapore!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+            <div class="mygauge-combo">
+                <div class="mygauge">
+                    <div data-netdata="netdata.requests"
+                            data-host="//bangalore.netdata.rocks"
+                            data-title="India"
+                            data-chart-library="easypiechart"
+                            data-width="75%"
+                            data-after="-300"
+                            data-points="300"
+                            data-colors="#BB55BB"
+                            ></div>
+                </div>
+                <div class="mygauge-button">
+                    <br/>&nbsp;<br/>
+                    <button type="button" class="btn btn-default" data-toggle="button" aria-pressed="false" autocomplete="off" onclick="window.location='//bangalore.netdata.rocks/default.html'" style="font-size: 1.0vw;">Enter India!</button>
+                    <div style="font-size: 0.8vw;">
+                        Donated by DigitalOcean.com
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="mytitle">
+        this page is a custom <b>netdata</b> dashboard
+    </div>
+    <div class="mycontent">
+        charts are coming from 8 servers, in parallel
+        <br/>
+        the servers are not aware of this multi-server dashboard,
+        <br/>
+        each server is not aware of the other servers,
+        <br/>
+        but on this dashboard <b>they are one</b>!
+    </div>
+    <div style="padding-top: 1vw; width: 100%; text-align: center; font-size: 1.5vw;">
+        <i class="fa fa-comment" aria-hidden="true"></i>
+        hover on a chart below, or drag it to show the past - <b>the others will follow</b>!
+        <br/>
+        double click on a chart to reset them all
+    </div>
+
+    <div class="mytitle">
+        our <code>nginx</code> performance
+    </div>
+    <div class="mycontent">
+        (we proxy netdata through nginx, on the demo sites)
+    </div>
+
+    <!-- Nav tabs -->
+    <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;">
+        <li role="presentation" class="active"><a href="#nginx_requests" aria-controls="nginx_requests" role="tab" data-toggle="tab">Requests</a></li>
+        <li role="presentation"><a href="#nginx_connections" aria-controls="nginx_connections" role="tab" data-toggle="tab">Connections</a></li>
+    </ul>
+
+    <!-- Tab panes -->
+    <div class="tab-content">
+        <div role="tabpanel" class="tab-pane active" id="nginx_requests">
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>EU - London</b> web requests/s
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.requests.netdata" >
+                </div>
+                <div data-netdata="nginx.requests"
+                        data-dimensions="requests"
+                        data-host="//london.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#558855"
+                        data-show-value-of-requests-at="nginx.requests.netdata"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - Atlanta</b> web requests/s
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.requests.netdata2" >
+                </div>
+                <div data-netdata="nginx.requests"
+                        data-dimensions="requests"
+                        data-host="//atlanta.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#AA5555"
+                        data-show-value-of-requests-at="nginx.requests.netdata2"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - California</b> web requests/s
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.requests.netdata3" >
+                </div>
+                <div data-netdata="nginx.requests"
+                        data-dimensions="requests"
+                        data-host="//sanfrancisco.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#5555AA"
+                        data-show-value-of-requests-at="nginx.requests.netdata3"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>Canada</b> web requests/s
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.requests.netdata4" >
+                </div>
+                <div data-netdata="nginx.requests"
+                        data-dimensions="requests"
+                        data-host="//toronto.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#885588"
+                        data-show-value-of-requests-at="nginx.requests.netdata4"
+                        ></div>
+            </div>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="nginx_connections">
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>EU - London</b> active connections
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.connections.netdata1" >
+                </div>
+                <div data-netdata="nginx.connections"
+                        data-dimensions="active"
+                        data-host="//london.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#558855"
+                        data-show-value-of-active-at="nginx.connections.netdata1"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - Atlanta</b> active connections
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.connections.netdata2" >
+                </div>
+                <div data-netdata="nginx.connections"
+                        data-dimensions="active"
+                        data-host="//atlanta.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#AA5555"
+                        data-show-value-of-active-at="nginx.connections.netdata2"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - California</b> active connections
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.connections.netdata3" >
+                </div>
+                <div data-netdata="nginx.connections"
+                        data-dimensions="active"
+                        data-host="//sanfrancisco.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#5555AA"
+                        data-show-value-of-active-at="nginx.connections.netdata3"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>Canada</b> active connections
+                </div>
+                <div class="mysparkline-overchart-value" id="nginx.connections.netdata4" >
+                </div>
+                <div data-netdata="nginx.connections"
+                        data-dimensions="active"
+                        data-host="//toronto.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#885588"
+                        data-show-value-of-active-at="nginx.connections.netdata4"
+                        ></div>
+            </div>
+        </div>
+    </div>
+
+    <div style="width: 100%; text-align: right; font-size: 1vw;">
+        <i class="fa fa-comment" aria-hidden="true"></i> these charts are draggable and touchable, double click them to reset them
+    </div>
+
+
+    <div class="mytitle">
+        bandwidth consumption on the demo sites
+    </div>
+    <div class="mycontent">
+        Linux QoS is configured by <a href="https://github.com/firehol/netdata/wiki/You-should-install-QoS-on-all-your-servers">FireQOS</a>
+    </div>
+
+    <!-- Nav tabs -->
+    <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;">
+        <li role="presentation" class="active"><a href="#outbout" aria-controls="outbout" role="tab" data-toggle="tab">Outbound</a></li>
+        <li role="presentation"><a href="#inbound" aria-controls="inbound" role="tab" data-toggle="tab">Inbound</a></li>
+    </ul>
+
+    <!-- Tab panes -->
+    <div class="tab-content">
+        <div role="tabpanel" class="tab-pane active" id="outbout">
+            <div class="myfullchart">
+                <div data-netdata="tc.world_out"
+                    data-host="//london.my-netdata.io"
+                    data-chart-library="dygraph"
+                    data-title="EU - London, traffic we send per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            </div>
+
+            <div class="myfullchart">
+                <div data-netdata="tc.world_out"
+                    data-host="//atlanta.my-netdata.io"
+                    data-chart-library="dygraph"
+                    data-title="US - Atlanta, traffic we send per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+
+            </div>
+
+            <div class="myfullchart">
+                <div data-netdata="tc.world_out"
+                    data-host="//sanfrancisco.netdata.rocks"
+                    data-chart-library="dygraph"
+                    data-title="US - California, traffic we send per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            </div>
+
+            <div class="myfullchart">
+                <div data-netdata="tc.world_out"
+                    data-host="//toronto.netdata.rocks"
+                    data-chart-library="dygraph"
+                    data-title="Canada, traffic we send per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            </div>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="inbound">
+            <div class="myfullchart">
+                <div data-netdata="tc.world_in"
+                    data-host="//london.my-netdata.io"
+                    data-chart-library="dygraph"
+                    data-title="EU - London, traffic we receive per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+
+            </div>
+
+            <div class="myfullchart">
+                <div data-netdata="tc.world_in"
+                    data-host="//atlanta.my-netdata.io"
+                    data-chart-library="dygraph"
+                    data-title="US - Atlanta, traffic we receive per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+
+            </div>
+
+            <div class="myfullchart">
+                <div data-netdata="tc.world_in"
+                    data-host="//sanfrancisco.netdata.rocks"
+                    data-chart-library="dygraph"
+                    data-title="US - California, traffic we receive per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            </div>
+
+            <div class="myfullchart">
+                <div data-netdata="tc.world_in"
+                    data-host="//toronto.netdata.rocks"
+                    data-chart-library="dygraph"
+                    data-title="Canada, traffic we receive per service"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            </div>
+        </div>
+    </div>
+    <div style="width: 100%; text-align: right; font-size: 1vw;">
+        <i class="fa fa-comment" aria-hidden="true"></i> <i>these legends are interactive and the charts are resizable here ^^^</i>
+    </div>
+
+    <div class="mytitle">
+        DDoS protection performance on the demo sites
+    </div>
+    <div class="mycontent">
+        iptables SYNPROXY configured by <a href="https://github.com/firehol/netdata/wiki/Monitoring-SYNPROXY">FireHOL</a>
+    </div>
+
+    <div style="padding-top: 4vw; width: 100%; text-align: center; font-size: 1.5vw;">
+
+        <div class="mysparkline">
+            <div class="mysparkline-overchart-label">
+                <b>EU - London</b>, TCP SYN packets/s received
+            </div>
+            <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata1" >
+            </div>
+            <div data-netdata="netfilter.synproxy_syn_received"
+                    data-dimensions="received"
+                    data-host="//london.my-netdata.io"
+                    data-chart-library="dygraph"
+                    data-dygraph-theme="sparkline"
+                    data-dygraph-type="area"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    data-colors="#558855"
+                    data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata1"
+                    ></div>
+        </div>
+
+        <div class="mysparkline">
+            <div class="mysparkline-overchart-label">
+                <b>US - Atlanta</b>, TCP SYN packets/s received
+            </div>
+            <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata2" >
+            </div>
+            <div data-netdata="netfilter.synproxy_syn_received"
+                    data-dimensions="received"
+                    data-host="//atlanta.my-netdata.io"
+                    data-chart-library="dygraph"
+                    data-dygraph-theme="sparkline"
+                    data-dygraph-type="area"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    data-colors="#885555"
+                    data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata2"
+                    ></div>
+        </div>
+
+        <div class="mysparkline">
+            <div class="mysparkline-overchart-label">
+                <b>US - California</b>, TCP SYN packets/s received
+            </div>
+            <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata3" >
+            </div>
+            <div data-netdata="netfilter.synproxy_syn_received"
+                    data-dimensions="received"
+                    data-host="//sanfrancisco.netdata.rocks"
+                    data-chart-library="dygraph"
+                    data-dygraph-theme="sparkline"
+                    data-dygraph-type="area"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    data-colors="#555588"
+                    data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata3"
+                    ></div>
+        </div>
+
+        <div class="mysparkline">
+            <div class="mysparkline-overchart-label">
+                <b>Canada</b>, TCP SYN packets/s received
+            </div>
+            <div class="mysparkline-overchart-value" id="netfilter.synproxy_syn_received.netdata4" >
+            </div>
+            <div data-netdata="netfilter.synproxy_syn_received"
+                    data-dimensions="received"
+                    data-host="//toronto.netdata.rocks"
+                    data-chart-library="dygraph"
+                    data-dygraph-theme="sparkline"
+                    data-dygraph-type="area"
+                    data-width="100%"
+                    data-height="100%"
+                    data-after="-300"
+                    data-colors="#885588"
+                    data-show-value-of-received-at="netfilter.synproxy_syn_received.netdata4"
+                    ></div>
+        </div>
+    </div>
+    <div style="width: 100%; text-align: right; font-size: 1vw;">
+        <i class="fa fa-comment" aria-hidden="true"></i> <i>did you notice the decimal numbers?
+        <br/>netdata interpolates collected values at second boundaries, with nanosecond detail!</i>
+    </div>
+
+
+    <div class="mytitle">
+        CPU Utilization of the demo sites
+    </div>
+
+    <div style="padding-top: 1vw;">
+        <div class="myfullchart">
+            <div data-netdata="system.cpu"
+                data-host="//london.my-netdata.io"
+                data-chart-library="dygraph"
+                data-title="EU - London, CPU Usage"
+                data-width="100%"
+                data-height="100%"
+                data-after="-300"
+                data-dygraph-valuerange="[0, 100]"
+                ></div>
+        </div>
+
+        <div class="myfullchart">
+            <div data-netdata="system.cpu"
+                data-host="//atlanta.my-netdata.io"
+                data-chart-library="dygraph"
+                data-title="US - Atlanta, CPU Usage"
+                data-width="100%"
+                data-height="100%"
+                data-after="-300"
+                data-dygraph-valuerange="[0, 100]"
+                ></div>
+        </div>
+
+        <div class="myfullchart">
+            <div data-netdata="system.cpu"
+                data-host="//sanfrancisco.netdata.rocks"
+                data-chart-library="dygraph"
+                data-title="US - California, CPU Usage"
+                data-width="100%"
+                data-height="100%"
+                data-after="-300"
+                data-dygraph-valuerange="[0, 100]"
+                ></div>
+        </div>
+
+        <div class="myfullchart">
+            <div data-netdata="system.cpu"
+                data-host="//toronto.netdata.rocks"
+                data-chart-library="dygraph"
+                data-title="Canada, CPU Usage"
+                data-width="100%"
+                data-height="100%"
+                data-after="-300"
+                data-dygraph-valuerange="[0, 100]"
+                ></div>
+        </div>
+    </div>
+    <div style="width: 100%; text-align: right; font-size: 1vw;">
+        <i class="fa fa-comment" aria-hidden="true"></i> <i>what is using so much CPU?
+        <br/>The site <a href="//iplists.firehol.org/">iplists.firehol.org</a> is maintained by FireHOL - the CPU is used for comparing security IP Lists.</i>
+    </div>
+
+    <div class="mytitle">
+        Netdata performance
+    </div>
+    <div class="mycontent">
+        netdata monitors <b>users</b>, <b>user groups</b>, <b>applications (process trees)</b>
+        <br/>
+        <b>containers</b> (<code>lxc</code>, <code>docker</code>, etc.) and SNMP devices.
+    </div>
+
+    <!-- Nav tabs -->
+    <ul class="nav nav-tabs" role="tablist" style="padding-top: 1vw;">
+        <li role="presentation" class="active"><a href="#netdata_cpu" aria-controls="netdata_cpu" role="tab" data-toggle="tab">CPU</a></li>
+        <li role="presentation"><a href="#netdata_avgtime" aria-controls="netdata_avgtime" role="tab" data-toggle="tab">Average Response Time</a></li>
+    </ul>
+
+    <!-- Tab panes -->
+    <div class="tab-content">
+        <div role="tabpanel" class="tab-pane active" id="netdata_cpu">
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>EU - London</b>, CPU % of a single core
+                </div>
+                <div class="mysparkline-overchart-value" id="users.cpu.netdata1" >
+                </div>
+                <div data-netdata="apps.cpu"
+                        data-dimensions="netdata"
+                        data-host="//london.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#558855"
+                        data-show-value-of-netdata-at="users.cpu.netdata1"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - Atlanta</b>, CPU % of a single core
+                </div>
+                <div class="mysparkline-overchart-value" id="users.cpu.netdata2" >
+                </div>
+                <div data-netdata="apps.cpu"
+                        data-dimensions="netdata"
+                        data-host="//atlanta.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#885555"
+                        data-show-value-of-netdata-at="users.cpu.netdata2"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - California</b>, CPU % of a single core
+                </div>
+                <div class="mysparkline-overchart-value" id="users.cpu.netdata3" >
+                </div>
+                <div data-netdata="apps.cpu"
+                        data-dimensions="netdata"
+                        data-host="//sanfrancisco.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#555588"
+                        data-show-value-of-netdata-at="users.cpu.netdata3"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>Toronto</b>, CPU % of a single core
+                </div>
+                <div class="mysparkline-overchart-value" id="users.cpu.netdata4" >
+                </div>
+                <div data-netdata="apps.cpu"
+                        data-dimensions="netdata"
+                        data-host="//toronto.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#885588"
+                        data-show-value-of-netdata-at="users.cpu.netdata4"
+                        ></div>
+            </div>
+
+            <div style="width: 100%; text-align: right; font-size: 1vw;">
+                <i class="fa fa-comment" aria-hidden="true"></i> <i>this utilization is about the whole netdata process tree and the percentage is of <b>a single core</b>!
+                <br/>including <b>BASH</b> plugins (it monitors <code>mysql</code> on the demo sites), <b>node.js</b> plugins (it monitors <code>bind9</code> on the demo sites), etc.
+                <br/>and including the chart refreshes for the dashboards of all viewers.</i>
+            </div>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="netdata_avgtime">
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>EU - London</b>, API average response time in milliseconds
+                </div>
+                <div class="mysparkline-overchart-value" id="netdata.response_time1" >
+                </div>
+                <div data-netdata="netdata.response_time"
+                        data-host="//london.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#558855 #356835"
+                        data-show-value-of-average-at="netdata.response_time1"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - Atlanta</b>, API average response time in milliseconds
+                </div>
+                <div class="mysparkline-overchart-value" id="netdata.response_time2" >
+                </div>
+                <div data-netdata="netdata.response_time"
+                        data-host="//atlanta.my-netdata.io"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#885555 #683535"
+                        data-show-value-of-average-at="netdata.response_time2"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>US - California</b>, API average response time in milliseconds
+                </div>
+                <div class="mysparkline-overchart-value" id="netdata.response_time3" >
+                </div>
+                <div data-netdata="netdata.response_time"
+                        data-host="//sanfrancisco.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#555588 #353568"
+                        data-show-value-of-average-at="netdata.response_time3"
+                        ></div>
+            </div>
+
+            <div class="mysparkline">
+                <div class="mysparkline-overchart-label">
+                    <b>Canada</b>, API average response time in milliseconds
+                </div>
+                <div class="mysparkline-overchart-value" id="netdata.response_time4" >
+                </div>
+                <div data-netdata="netdata.response_time"
+                        data-host="//toronto.netdata.rocks"
+                        data-chart-library="dygraph"
+                        data-dygraph-theme="sparkline"
+                        data-dygraph-type="area"
+                        data-width="100%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-colors="#885588 #683568"
+                        data-show-value-of-average-at="netdata.response_time4"
+                        ></div>
+            </div>
+
+            <div style="width: 100%; text-align: right; font-size: 1vw;">
+                <i class="fa fa-comment" aria-hidden="true"></i> <i>netdata is really <b>fast</b> (the values are milliseconds!)
+                <br/>
+                These values include everything, from the reception of the first byte to the dispatch of the last, including gzip compression.
+                <br/>
+                Values above 2-3ms are usually chart refreshes of charts with several dimensions, charts with very long durations (zoomed out), or file transfers.
+                </i>
+            </div>
+        </div>
+    </div>
+
+    <div style="padding-top: 6vw; width: 100%; text-align: center; font-size: 2vw;">
+        want to know more?
+        <br/>
+        jump to <a href="https://github.com/firehol/netdata/">the netdata page at github</a>
+        <br/>
+        it needs just 3 mins to be installed on your servers!
+        <br/>
+        &nbsp;
+    </div>
 </div>
 </body>
 <script>
-       // google analytics when this is used for the home page of the demo sites
-       // you don't need this if you customize this dashboard for your needs
-       setTimeout(function() {
-               (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-               (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-               m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-               })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
-
-               ga('create', 'UA-64295674-3', 'auto');
-               ga('send', 'pageview');
-       }, 2000);
+    // google analytics when this is used for the home page of the demo sites
+    // you don't need this if you customize this dashboard for your needs
+    setTimeout(function() {
+        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+        })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
+
+        ga('create', 'UA-64295674-3', 'auto');
+        ga('send', 'pageview');
+    }, 2000);
 </script>
 </html>
index 1efb9760fe92bbf6e0b5d91901b632bbae16ce9b..d9a4bf2f2ea51de085630ce4e45c8f6ddf305446 100644 (file)
 ï»¿<!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>netdata dashboard</title>
-       <meta name="application-name" content="netdata">
-
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-       <meta name="author" content="costa@tsaousis.gr">
-
-       <link rel="shortcut icon" href="images/seo-performance-multi-size.ico">
-
-       <link rel="apple-touch-icon" href="images/seo-performance-72.png">
-       <link rel="apple-touch-icon" sizes="72x72" href="images/seo-performance-72.png">
-       <link rel="apple-touch-icon" sizes="114x114" href="images/seo-performance-114.png">
-
-       <link rel="icon" type="image/png" sizes="512x512" href="images/seo-performance-512.png">
-       <link rel="icon" type="image/png" sizes="256x256" href="images/seo-performance-256.png">
-       <link rel="icon" type="image/png" sizes="128x128" href="images/seo-performance-128.png">
-       <link rel="icon" type="image/png" sizes="64x64" href="images/seo-performance-64.png">
-       <link rel="icon" type="image/png" sizes="48x48" href="images/seo-performance-48.png">
-       <link rel="icon" type="image/png" sizes="32x32" href="images/seo-performance-32.png">
-       <link rel="icon" type="image/png" sizes="24x24" href="images/seo-performance-24.png">
-       <link rel="icon" type="image/png" sizes="16x16" href="images/seo-performance-16.png">
-
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="http://my-netdata.io/images/post.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
-
-       <style>
-
-       /* prevent body from hiding under the navbar */
-       body {
-               padding-top: 50px;
-       }
-
-       .loadOverlay {
-               position: absolute;
-               top: 0px;
-               left: 0px;
-               width: 100%;
-               height:100%;
-               z-index: 2000;
-               font-size: 10vh;
-               font-family: sans-serif;
-               padding: 40vh 0 40vh 0;
-               font-weight: bold;
-               text-align: center;
-       }
-
-       .modal-wide .modal-dialog {
-               width: 80%;
-       }
-
-       /* fix # anchors scrolling under the navbar
-          https://github.com/twbs/bootstrap/issues/1768#issuecomment-46519033
-        */
-       h1 {
-               position: relative;
-               z-index: -1;
-       }
-       h2 {
-               position: relative;
-               z-index: -2;
-       }
-       h1:before, h2:before {
-               display: block;
-               content: " ";
-               margin-top: -70px;
-               height: 70px;
-               visibility: hidden;
-       }
-
-       .p {
-               display: block;
-               margin-top: 15px;
-       }
-
-       .option-row,
-       .option-control {
-               vertical-align: top;
-               padding: 10px;
-               padding-top: 30px;
-               padding-left: 30px;
-       }
-
-       .option-info {
-               padding: 10px;
-       }
-
-       .chart-message {
-               display: block;
-               margin-top: 10px;
-       }
-
-       .container {
-               width: 90% !important;
-       }
-
-       #masthead h1 {
-               /*font-size: 30px;*/
-               line-height: 1;
-               padding-top: 30px;
-       }
-
-       #masthead .well {
-               margin-top:4%;
-       }
-
-       /* fix the navbar shifting when a modal is open */
-       /* https://github.com/twbs/bootstrap/issues/14040#issuecomment-159891033 */
-       body.modal-open{
-               width: 100% !important;
-               padding-right: 0 !important;
-/*             overflow-y: scroll !important; */
-/*             position: fixed !important;*/
-               overflow: visible;
-       }
-
-       /*
-        * Side navigation
-        *
-        * Scrollspy and affixed enhanced navigation to highlight sections and secondary
-        * sections of docs content.
-        */
-
-       .affix {
-               position: static;
-               top: 70px !important;
-               /*width: 220px;*/
-       }
-
-       .affix-top {
-               /*width: 220px;*/
-       }
-
-       .dashboard-sidebar {
-               max-height: calc(100% - 70px) !important;
-               overflow-y: auto;
-               /*width: 220px !important;*/
-       }
-
-       /* By default it's not affixed in mobile views, so undo that */
-       .dashboard-sidebar.affix {
-               position: static;
-       }
-
-       @media (min-width: 768px) {
-               .dashboard-sidebar {
-                       padding-left: 20px;
-               }
-       }
-
-       /* First level of nav */
-       .dashboard-sidenav {
-               margin-top: 20px;
-               margin-bottom: 20px;
-       }
-
-       /* All levels of nav */
-       .dashboard-sidebar .nav > li > a {
-               display: block;
-               padding: 4px 20px;
-               font-size: 13px;
-               font-weight: 500;
-               color: #767676;
-       }
-       .dashboard-sidebar .nav > li > a:hover,
-       .dashboard-sidebar .nav > li > a:focus {
-               padding-left: 19px;
-               color: #563d7c;
-               text-decoration: none;
-               background-color: transparent;
-               border-left: 1px solid #563d7c;
-       }
-       .dashboard-sidebar .nav > .active > a,
-       .dashboard-sidebar .nav > .active:hover > a,
-       .dashboard-sidebar .nav > .active:focus > a {
-               padding-left: 18px;
-               font-weight: bold;
-               color: #563d7c;
-               background-color: transparent;
-               border-left: 2px solid #563d7c;
-       }
-
-       /* Nav: second level (shown on .active) */
-       .dashboard-sidebar .nav .nav {
-               display: none; /* Hide by default, but at >768px, show it */
-               padding-bottom: 10px;
-       }
-       .dashboard-sidebar .nav .nav > li > a {
-               padding-top: 1px;
-               padding-bottom: 1px;
-               padding-left: 30px;
-               font-size: 12px;
-               font-weight: normal;
-       }
-       .dashboard-sidebar .nav .nav > li > a:hover,
-       .dashboard-sidebar .nav .nav > li > a:focus {
-               padding-left: 29px;
-       }
-       .dashboard-sidebar .nav .nav > .active > a,
-       .dashboard-sidebar .nav .nav > .active:hover > a,
-       .dashboard-sidebar .nav .nav > .active:focus > a {
-               padding-left: 28px;
-               font-weight: 500;
-       }
-
-       .dropdown-menu {
-               min-width: 200px;
-       }
-       .dropdown-menu.columns-2 {
-               margin: 0;
-               padding: 0;
-               width: 400px;
-       }
-       .dropdown-menu li a {
-               padding: 5px 15px;
-               font-weight: 300;
-       }
-       .dropdown-menu.multi-column {
-               overflow-x: hidden;
-       }
-       .multi-column-dropdown {
-               list-style: none;
-               padding: 0;
-       }
-       .multi-column-dropdown li a {
-               display: block;
-               clear: both;
-               line-height: 1.428571429;
-               white-space: normal;
-       }
-       .multi-column-dropdown li a:hover {
-               text-decoration: none;
-               color: #f5f5f5;
-               background-color: #262626;
-       }
-       .scrollable-menu {
-               height: auto;
-               max-height: 80vh;
-               overflow-x: hidden;
-       }
-
-       /* Back to top (hidden on mobile) */
-       .back-to-top,
-       .dashboard-theme-toggle {
-               display: none;
-               padding: 4px 10px;
-               margin-top: 10px;
-               margin-left: 10px;
-               font-size: 12px;
-               font-weight: 500;
-               color: #999;
-       }
-       .back-to-top:hover,
-       .dashboard-theme-toggle:hover {
-               color: #563d7c;
-               text-decoration: none;
-       }
-       .dashboard-theme-toggle {
-               margin-top: 0;
-       }
-
-       @media (min-width: 768px) {
-               .back-to-top,
-               .dashboard-theme-toggle {
-                       display: block;
-               }
-
-               /* Widen the fixed sidebar */
-               .dashboard-sidebar.affix,
-               .dashboard-sidebar.affix-top,
-               .dashboard-sidebar.affix-bottom {
-                       width: 200px !important;
-               }
-
-               .dashboard-sidebar.affix {
-                       position: fixed; /* Undo the static from mobile first approach */
-                       top: 20px;
-               }
-
-               .dashboard-sidebar.affix-bottom {
-                       position: absolute; /* Undo the static from mobile first approach */
-               }
-
-               .dashboard-sidebar.affix-bottom .dashboard-sidenav,
-               .dashboard-sidebar.affix .dashboard-sidenav {
-                       margin-top: 0;
-                       margin-bottom: 0;
-               }
-       }
-
-       /* Show and affix the side nav when space allows it */
-       @media (min-width: 992px) {
-               .dashboard-sidebar .nav > .active > ul {
-                       display: block;
-               }
-
-               /* Widen the fixed sidebar */
-               .dashboard-sidebar.affix,
-               .dashboard-sidebar.affix-top,
-               .dashboard-sidebar.affix-bottom {
-                       width: 200px !important;
-               }
-               .dashboard-sidebar.affix {
-                       position: fixed; /* Undo the static from mobile first approach */
-                       top: 20px;
-               }
-               .dashboard-sidebar.affix-bottom {
-                       position: absolute; /* Undo the static from mobile first approach */
-               }
-               .dashboard-sidebar.affix-bottom .dashboard-sidenav,
-               .dashboard-sidebar.affix .dashboard-sidenav {
-                       margin-top: 0;
-                       margin-bottom: 0;
-               }
-       }
-
-       @media (min-width: 1200px) {
-               /* Widen the fixed sidebar again */
-               .dashboard-sidebar.affix-bottom,
-               .dashboard-sidebar.affix-top,
-               .dashboard-sidebar.affix {
-                       width: 263px;
-               }
-       }
-       </style>
-
-       <!-- check which theme to use -->
-       <script type="text/javascript">
-               // --------------------------------------------------------------------
-               // urlOptions
-
-               var urlOptions = {
-                       hash: '#',
-                       theme: null,
-                       help: null,
-                       pan_and_zoom: false,
-                       after: 0,
-                       before: 0,
-                       nowelcome: 0,
-                       hasProperty: function(property) {
-                               // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
-                               return typeof this[property] !== 'undefined';
-                       }
-               };
-
-               function netdataPanAndZoomCallback(status, after, before) {
-                       urlOptions.pan_and_zoom = status;
-                       urlOptions.after = after;
-                       urlOptions.before = before;
-                       netdataHashUpdate();
-               }
-
-               function netdataHashUpdate() {
-                       history.replaceState(null, document.title, netdataHash());
-               }
-
-               function netdataHash() {
-                       var hash = urlOptions.hash;
-
-                       if(urlOptions.pan_and_zoom === true) {
-                               hash += ';after='  + urlOptions.after.toString() +
-                                       ';before=' + urlOptions.before.toString();
-                       }
-
-                       if(urlOptions.theme !== null)
-                               hash += ';theme=' + urlOptions.theme.toString();
-
-                       if(urlOptions.help !== null)
-                               hash += ';help=' + urlOptions.help.toString();
-
-                       return hash;
-               }
-
-               function netdataHashParse() {
-                       var variables = document.location.hash.split(';');
-                       var len = variables.length;
-                       while(len--) {
-                               if(len !== 0) {
-                                       var p = variables[len].split('=');
-                                       if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
-                                               urlOptions[p[0]] = p[1];
-                               }
-                               else {
-                                       if(variables[len].length > 0)
-                                               urlOptions.hash = variables[len];
-                               }
-                       }
-
-                       if(urlOptions.before > 0 && urlOptions.after > 0)
-                               urlOptions.pan_and_zoom = true;
-
-                       // console.log(urlOptions);
-               }
-
-               netdataHashParse();
-
-               // --------------------------------------------------------------------
-               // check options that should be processed before loading netdata.js
-               
-               function loadLocalStorage(name) {
-                       var ret = null;
-
-                       try {
-                               if(typeof Storage !== "undefined" && typeof localStorage === 'object')
-                                       ret = localStorage.getItem(name);
-                       }
-                       catch(error) {
-                               ;
-                       }
-
-                       if(typeof ret === 'undefined' || ret === null)
-                               return null;
-
-                       // console.log('loaded: ' + name.toString() + ' = ' + ret.toString());
-
-                       return ret;
-               }
-
-               function saveLocalStorage(name, value) {
-                       // console.log('saving: ' + name.toString() + ' = ' + value.toString());
-                       try {
-                               if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
-                                       localStorage.setItem(name, value.toString());
-                                       return true;
-                               }
-                       }
-                       catch(error) {
-                               ;
-                       }
-
-                       return false;
-               }
-
-               function getTheme(def) {
-                       var ret = loadLocalStorage('netdataTheme');
-                       if(typeof ret === 'undefined' || ret === null || ret === 'undefined')
-                               return def;
-                       else
-                               return ret;
-               }
-
-               function setTheme(theme) {
-                       if(theme === netdataTheme) return false;
-                       return saveLocalStorage('netdataTheme', theme);
-               }
-
-               var netdataTheme = getTheme('slate');
-               var netdataShowHelp = true;
-
-               if(urlOptions.theme !== null) {
-                       setTheme(urlOptions.theme);
-                       netdataTheme = urlOptions.theme;
-               }
-               else
-                       urlOptions.theme = netdataTheme;
-
-               if(urlOptions.help !== null) {
-                       saveLocalStorage('options.show_help', urlOptions.help);
-                       netdataShowHelp = urlOptions.help;
-               }
-               else {
-                       urlOptions.help = loadLocalStorage('options.show_help');
-               }
-
-               // --------------------------------------------------------------------
-               // registry call back to render my-netdata menu
-
-               var netdataRegistryCallback = function(machines_array) {
-                       var el = '';
-                       var a1 = '';
-                       var found = 0;
-
-                       if(machines_array) {
-                               function name_comparator_desc(a, b) {
-                                       if (a.name > b.name) return -1;
-                                       if (a.name < b.name) return 1;
-                                       return 0;
-                               }
-
-                               var machines = machines_array.sort(name_comparator_desc);
-                               var len = machines.length;
-                               while(len--) {
-                                       var u = machines[len];
-                                       found++;
-                                       el += '<li id="registry_server_' + u.guid + '"><a class="registry_link" href="' + u.url + '" onClick="return gotoServerModalHandler(\'' + u.guid + '\');">' + u.name + '</a></li>';
-                                       a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>';
-                               }
-                       }
-
-                       if(!found) {
-                               if(machines)
-                                       el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
-                               else
-                                       el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
-
-                               a1 += '<li><a href="#" onClick="return false;">&nbsp;</a></li>';
-
-                               el += '<li role="separator" class="divider"></li>' +
-                                               '<li><a href="//london.netdata.rocks/default.html">EU - London (DigitalOcean.com)</a></li>' +
-                                               '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
-                                               '<li><a href="//athens.netdata.rocks/default.html">EU - Athens</a></li>';
-                               a1 += '<li role="separator" class="divider"></li>' +
-                                               '<li><a href="#">&nbsp;</a></li>' +
-                                               '<li><a href="#">&nbsp;</a></li>'+
-                                               '<li><a href="#">&nbsp;</a></li>';
-                       }
-
-                       el += '<li role="separator" class="divider"></li>';
-                       a1 += '<li role="separator" class="divider"></li>';
-
-                       el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>';
-                       a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-cog" aria-hidden="true" style="color: #999;"></i></a></li>'
-
-                       document.getElementById('mynetdata_servers').innerHTML = el;
-                       document.getElementById('mynetdata_servers2').innerHTML = el;
-                       document.getElementById('mynetdata_actions1').innerHTML = a1;
-
-                       gotoServerInit();
-               };
-
-       </script>
-
-       <!-- load the dashboard manager - it will do the rest -->
-       <script type="text/javascript" src="dashboard.js?v41"></script>
+    <title>netdata dashboard</title>
+    <meta name="application-name" content="netdata">
+
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+    <meta name="author" content="costa@tsaousis.gr">
+
+    <link rel="shortcut icon" href="images/seo-performance-multi-size.ico">
+
+    <link rel="apple-touch-icon" href="images/seo-performance-72.png">
+    <link rel="apple-touch-icon" sizes="72x72" href="images/seo-performance-72.png">
+    <link rel="apple-touch-icon" sizes="114x114" href="images/seo-performance-114.png">
+
+    <link rel="icon" type="image/png" sizes="512x512" href="images/seo-performance-512.png">
+    <link rel="icon" type="image/png" sizes="256x256" href="images/seo-performance-256.png">
+    <link rel="icon" type="image/png" sizes="128x128" href="images/seo-performance-128.png">
+    <link rel="icon" type="image/png" sizes="64x64" href="images/seo-performance-64.png">
+    <link rel="icon" type="image/png" sizes="48x48" href="images/seo-performance-48.png">
+    <link rel="icon" type="image/png" sizes="32x32" href="images/seo-performance-32.png">
+    <link rel="icon" type="image/png" sizes="24x24" href="images/seo-performance-24.png">
+    <link rel="icon" type="image/png" sizes="16x16" href="images/seo-performance-16.png">
+
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="http://my-netdata.io/images/post.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+
+    <style>
+
+    /* prevent body from hiding under the navbar */
+    body {
+        padding-top: 50px;
+    }
+
+    .loadOverlay {
+        position: absolute;
+        top: 0px;
+        left: 0px;
+        width: 100%;
+        height:100%;
+        z-index: 2000;
+        font-size: 10vh;
+        font-family: sans-serif;
+        padding: 40vh 0 40vh 0;
+        font-weight: bold;
+        text-align: center;
+    }
+
+    .modal-wide .modal-dialog {
+        width: 80%;
+    }
+
+    /* fix # anchors scrolling under the navbar
+       https://github.com/twbs/bootstrap/issues/1768#issuecomment-46519033
+     */
+    h1 {
+        position: relative;
+        z-index: -1;
+    }
+    h2 {
+        position: relative;
+        z-index: -2;
+    }
+    h1:before, h2:before {
+        display: block;
+        content: " ";
+        margin-top: -70px;
+        height: 70px;
+        visibility: hidden;
+    }
+
+    .p {
+        display: block;
+        margin-top: 15px;
+    }
+
+    .option-row,
+    .option-control {
+        vertical-align: top;
+        padding: 10px;
+        padding-top: 30px;
+        padding-left: 30px;
+    }
+
+    .option-info {
+        padding: 10px;
+    }
+
+    .chart-message {
+        display: block;
+        margin-top: 10px;
+    }
+
+    .container {
+        width: 90% !important;
+    }
+
+    #masthead h1 {
+        /*font-size: 30px;*/
+        line-height: 1;
+        padding-top: 30px;
+    }
+
+    #masthead .well {
+        margin-top:4%;
+    }
+
+    /* fix the navbar shifting when a modal is open */
+    /* https://github.com/twbs/bootstrap/issues/14040#issuecomment-159891033 */
+    body.modal-open{
+        width: 100% !important;
+        padding-right: 0 !important;
+/*      overflow-y: scroll !important; */
+/*      position: fixed !important;*/
+        overflow: visible;
+    }
+
+    /*
+     * Side navigation
+     *
+     * Scrollspy and affixed enhanced navigation to highlight sections and secondary
+     * sections of docs content.
+     */
+
+    .affix {
+        position: static;
+        top: 70px !important;
+        /*width: 220px;*/
+    }
+
+    .affix-top {
+        /*width: 220px;*/
+    }
+
+    .dashboard-sidebar {
+        max-height: calc(100% - 70px) !important;
+        overflow-y: auto;
+        /*width: 220px !important;*/
+    }
+
+    /* By default it's not affixed in mobile views, so undo that */
+    .dashboard-sidebar.affix {
+        position: static;
+    }
+
+    @media (min-width: 768px) {
+        .dashboard-sidebar {
+            padding-left: 20px;
+        }
+    }
+
+    /* First level of nav */
+    .dashboard-sidenav {
+        margin-top: 20px;
+        margin-bottom: 20px;
+    }
+
+    /* All levels of nav */
+    .dashboard-sidebar .nav > li > a {
+        display: block;
+        padding: 4px 20px;
+        font-size: 13px;
+        font-weight: 500;
+        color: #767676;
+    }
+    .dashboard-sidebar .nav > li > a:hover,
+    .dashboard-sidebar .nav > li > a:focus {
+        padding-left: 19px;
+        color: #563d7c;
+        text-decoration: none;
+        background-color: transparent;
+        border-left: 1px solid #563d7c;
+    }
+    .dashboard-sidebar .nav > .active > a,
+    .dashboard-sidebar .nav > .active:hover > a,
+    .dashboard-sidebar .nav > .active:focus > a {
+        padding-left: 18px;
+        font-weight: bold;
+        color: #563d7c;
+        background-color: transparent;
+        border-left: 2px solid #563d7c;
+    }
+
+    /* Nav: second level (shown on .active) */
+    .dashboard-sidebar .nav .nav {
+        display: none; /* Hide by default, but at >768px, show it */
+        padding-bottom: 10px;
+    }
+    .dashboard-sidebar .nav .nav > li > a {
+        padding-top: 1px;
+        padding-bottom: 1px;
+        padding-left: 30px;
+        font-size: 12px;
+        font-weight: normal;
+    }
+    .dashboard-sidebar .nav .nav > li > a:hover,
+    .dashboard-sidebar .nav .nav > li > a:focus {
+        padding-left: 29px;
+    }
+    .dashboard-sidebar .nav .nav > .active > a,
+    .dashboard-sidebar .nav .nav > .active:hover > a,
+    .dashboard-sidebar .nav .nav > .active:focus > a {
+        padding-left: 28px;
+        font-weight: 500;
+    }
+
+    .dropdown-menu {
+        min-width: 200px;
+    }
+    .dropdown-menu.columns-2 {
+        margin: 0;
+        padding: 0;
+        width: 400px;
+    }
+    .dropdown-menu li a {
+        padding: 5px 15px;
+        font-weight: 300;
+    }
+    .dropdown-menu.multi-column {
+        overflow-x: hidden;
+    }
+    .multi-column-dropdown {
+        list-style: none;
+        padding: 0;
+    }
+    .multi-column-dropdown li a {
+        display: block;
+        clear: both;
+        line-height: 1.428571429;
+        white-space: normal;
+    }
+    .multi-column-dropdown li a:hover {
+        text-decoration: none;
+        color: #f5f5f5;
+        background-color: #262626;
+    }
+    .scrollable-menu {
+        height: auto;
+        max-height: 80vh;
+        overflow-x: hidden;
+    }
+
+    /* Back to top (hidden on mobile) */
+    .back-to-top,
+    .dashboard-theme-toggle {
+        display: none;
+        padding: 4px 10px;
+        margin-top: 10px;
+        margin-left: 10px;
+        font-size: 12px;
+        font-weight: 500;
+        color: #999;
+    }
+    .back-to-top:hover,
+    .dashboard-theme-toggle:hover {
+        color: #563d7c;
+        text-decoration: none;
+    }
+    .dashboard-theme-toggle {
+        margin-top: 0;
+    }
+
+    @media (min-width: 768px) {
+        .back-to-top,
+        .dashboard-theme-toggle {
+            display: block;
+        }
+
+        /* Widen the fixed sidebar */
+        .dashboard-sidebar.affix,
+        .dashboard-sidebar.affix-top,
+        .dashboard-sidebar.affix-bottom {
+            width: 200px !important;
+        }
+
+        .dashboard-sidebar.affix {
+            position: fixed; /* Undo the static from mobile first approach */
+            top: 20px;
+        }
+
+        .dashboard-sidebar.affix-bottom {
+            position: absolute; /* Undo the static from mobile first approach */
+        }
+
+        .dashboard-sidebar.affix-bottom .dashboard-sidenav,
+        .dashboard-sidebar.affix .dashboard-sidenav {
+            margin-top: 0;
+            margin-bottom: 0;
+        }
+    }
+
+    /* Show and affix the side nav when space allows it */
+    @media (min-width: 992px) {
+        .dashboard-sidebar .nav > .active > ul {
+            display: block;
+        }
+
+        /* Widen the fixed sidebar */
+        .dashboard-sidebar.affix,
+        .dashboard-sidebar.affix-top,
+        .dashboard-sidebar.affix-bottom {
+            width: 200px !important;
+        }
+        .dashboard-sidebar.affix {
+            position: fixed; /* Undo the static from mobile first approach */
+            top: 20px;
+        }
+        .dashboard-sidebar.affix-bottom {
+            position: absolute; /* Undo the static from mobile first approach */
+        }
+        .dashboard-sidebar.affix-bottom .dashboard-sidenav,
+        .dashboard-sidebar.affix .dashboard-sidenav {
+            margin-top: 0;
+            margin-bottom: 0;
+        }
+    }
+
+    @media (min-width: 1200px) {
+        /* Widen the fixed sidebar again */
+        .dashboard-sidebar.affix-bottom,
+        .dashboard-sidebar.affix-top,
+        .dashboard-sidebar.affix {
+            width: 263px;
+        }
+    }
+    </style>
+
+    <!-- check which theme to use -->
+    <script type="text/javascript">
+        // --------------------------------------------------------------------
+        // urlOptions
+
+        var urlOptions = {
+            hash: '#',
+            theme: null,
+            help: null,
+            pan_and_zoom: false,
+            after: 0,
+            before: 0,
+            nowelcome: 0,
+            hasProperty: function(property) {
+                // console.log('checking property ' + property + ' of type ' + typeof(this[property]));
+                return typeof this[property] !== 'undefined';
+            }
+        };
+
+        function netdataPanAndZoomCallback(status, after, before) {
+            urlOptions.pan_and_zoom = status;
+            urlOptions.after = after;
+            urlOptions.before = before;
+            netdataHashUpdate();
+        }
+
+        function netdataHashUpdate() {
+            history.replaceState(null, '', netdataHash());
+        }
+
+        function netdataHash() {
+            var hash = urlOptions.hash;
+
+            if(urlOptions.pan_and_zoom === true) {
+                hash += ';after='  + urlOptions.after.toString() +
+                        ';before=' + urlOptions.before.toString();
+            }
+
+            if(urlOptions.theme !== null)
+                hash += ';theme=' + urlOptions.theme.toString();
+
+            if(urlOptions.help !== null)
+                hash += ';help=' + urlOptions.help.toString();
+
+            return hash;
+        }
+
+        function netdataHashParse() {
+            var variables = document.location.hash.split(';');
+            var len = variables.length;
+            while(len--) {
+                if(len !== 0) {
+                    var p = variables[len].split('=');
+                    if(urlOptions.hasProperty(p[0]) && typeof p[1] !== 'undefined')
+                        urlOptions[p[0]] = p[1];
+                }
+                else {
+                    if(variables[len].length > 0)
+                        urlOptions.hash = variables[len];
+                }
+            }
+
+            if(urlOptions.before > 0 && urlOptions.after > 0)
+                urlOptions.pan_and_zoom = true;
+
+            // console.log(urlOptions);
+        }
+
+        netdataHashParse();
+
+        // --------------------------------------------------------------------
+        // check options that should be processed before loading netdata.js
+        
+        function loadLocalStorage(name) {
+            var ret = null;
+
+            try {
+                if(typeof Storage !== "undefined" && typeof localStorage === 'object')
+                    ret = localStorage.getItem(name);
+            }
+            catch(error) {
+                ;
+            }
+
+            if(typeof ret === 'undefined' || ret === null)
+                return null;
+
+            // console.log('loaded: ' + name.toString() + ' = ' + ret.toString());
+
+            return ret;
+        }
+
+        function saveLocalStorage(name, value) {
+            // console.log('saving: ' + name.toString() + ' = ' + value.toString());
+            try {
+                if(typeof Storage !== "undefined" && typeof localStorage === 'object') {
+                    localStorage.setItem(name, value.toString());
+                    return true;
+                }
+            }
+            catch(error) {
+                ;
+            }
+
+            return false;
+        }
+
+        function getTheme(def) {
+            var ret = loadLocalStorage('netdataTheme');
+            if(typeof ret === 'undefined' || ret === null || ret === 'undefined')
+                return def;
+            else
+                return ret;
+        }
+
+        function setTheme(theme) {
+            if(theme === netdataTheme) return false;
+            return saveLocalStorage('netdataTheme', theme);
+        }
+
+        var netdataTheme = getTheme('slate');
+        var netdataShowHelp = true;
+
+        if(urlOptions.theme !== null) {
+            setTheme(urlOptions.theme);
+            netdataTheme = urlOptions.theme;
+        }
+        else
+            urlOptions.theme = netdataTheme;
+
+        if(urlOptions.help !== null) {
+            saveLocalStorage('options.show_help', urlOptions.help);
+            netdataShowHelp = urlOptions.help;
+        }
+        else {
+            urlOptions.help = loadLocalStorage('options.show_help');
+        }
+
+        // --------------------------------------------------------------------
+        // registry call back to render my-netdata menu
+
+        var netdataRegistryCallback = function(machines_array) {
+            var el = '';
+            var a1 = '';
+            var found = 0;
+
+            if(machines_array) {
+                function name_comparator_desc(a, b) {
+                    if (a.name > b.name) return -1;
+                    if (a.name < b.name) return 1;
+                    return 0;
+                }
+
+                var machines = machines_array.sort(name_comparator_desc);
+                var len = machines.length;
+                while(len--) {
+                    var u = machines[len];
+                    found++;
+                    el += '<li id="registry_server_' + u.guid + '"><a class="registry_link" href="' + u.url + '" onClick="return gotoServerModalHandler(\'' + u.guid + '\');">' + u.name + '</a></li>';
+                    a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>';
+                }
+            }
+
+            if(!found) {
+                if(machines)
+                    el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
+                else
+                    el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
+
+                a1 += '<li><a href="#" onClick="return false;">&nbsp;</a></li>';
+
+                el += '<li role="separator" class="divider"></li>' +
+                        '<li><a href="//london.netdata.rocks/default.html">EU - London (DigitalOcean.com)</a></li>' +
+                        '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
+                        '<li><a href="//athens.netdata.rocks/default.html">EU - Athens</a></li>';
+                a1 += '<li role="separator" class="divider"></li>' +
+                        '<li><a href="#">&nbsp;</a></li>' +
+                        '<li><a href="#">&nbsp;</a></li>'+
+                        '<li><a href="#">&nbsp;</a></li>';
+            }
+
+            el += '<li role="separator" class="divider"></li>';
+            a1 += '<li role="separator" class="divider"></li>';
+
+            el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>';
+            a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-cog" aria-hidden="true" style="color: #999;"></i></a></li>'
+
+            document.getElementById('mynetdata_servers').innerHTML = el;
+            document.getElementById('mynetdata_servers2').innerHTML = el;
+            document.getElementById('mynetdata_actions1').innerHTML = a1;
+
+            gotoServerInit();
+        };
+
+    </script>
+
+    <!-- load the dashboard manager - it will do the rest -->
+    <script type="text/javascript" src="dashboard.js?v41"></script>
 </head>
 <body data-spy="scroll" data-target="#sidebar">
-       <div id="loadOverlay" class="loadOverlay" style="background-color: #888; color: #888;">
-               netdata<br/><div style="font-size: 3vh;">Real-time performance monitoring, done right!</div>
-       </div>
-       <script type="text/javascript">
-               // change the loadOverlay colors ASAP to match the theme
-               document.getElementById('loadOverlay').style = (urlOptions.theme === 'slate')?"background-color:#272b30; color: #373b40;":"background-color:#fff; color: #ddd;";
-       </script>
-       <nav class="navbar navbar-default navbar-fixed-top" role="banner">
-               <div class="container">
-                       <nav id="mynetdata_nav" class="collapse navbar-collapse navbar-left hidden-sm hidden-xs" role="navigation" style="padding-right: 20px;">
-                               <ul class="nav navbar-nav">
-                                       <li class="dropdown">
-                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a>
-                                               <ul class="dropdown-menu scrollable-menu inpagemenu multi-column columns-2" role="menu">
-                                                       <div class="row">
-                                                               <div class="col-sm-6" style="width: 85%; padding-right: 0;">
-                                                                       <ul id="mynetdata_servers" class="multi-column-dropdown">
-                                                                               <li><a href="#" onclick="return false;" style="color: #999;">loading...</a></li>
-                                                                       </ul>
-                                                               </div>
-                                                               <div class="col-sm-3 hidden-xs" style="width: 15%; padding-left: 0;">
-                                                                       <ul id="mynetdata_actions1" class="multi-column-dropdown">
-                                                                       <li style="color: #999;">&nbsp;</li>
-                                                                       </ul>
-                                                               </div>
-                                                       </div>
-                                               </ul>
-                                       </li>
-                               </ul>
-                       </nav>
-                       <div class="navbar-header">
-                               <button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".navbar-collapse">
-                                       <span class="sr-only">Toggle navigation</span>
-                                       <span class="icon-bar"></span>
-                                       <span class="icon-bar"></span>
-                                       <span class="icon-bar"></span>
-                               </button>
-                               <a href="/" class="navbar-brand" id="hostname">netdata</a>
-                       </div>
-                       <nav class="collapse navbar-collapse navbar-right" role="navigation">
-                               <ul class="nav navbar-nav">
-                                       <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
-                                       <li class="hidden-sm"><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
-                                       <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fa fa-cloud-download"></i> update</a></li>
-                                       <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
-                                       <li class="dropdown hidden-md hidden-lg hidden-xs">
-                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">Menu <strong class="caret"></strong></a>
-                                               <ul class="dropdown-menu scrollable-menu inpagemenu" role="menu">
-                                                       <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
-                                                       <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
-                                                       <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
-                                               </ul>
-                                       </li>
-                                       <li class="dropdown hidden-sm hidden-md hidden-lg">
-                                               <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a>
-                                               <ul id="mynetdata_servers2" class="dropdown-menu scrollable-menu inpagemenu" role="menu">
-                                                       <li><a href="#" onclick="return false;" style="color: #999;">loading...</a></li>
-                                               </ul>
-                                       </li>
-                               </ul>
-                       </nav>
-       </nav>
-               </div>
-       </nav>
-
-       <div id="masthead" style="display: none;">
-               <div class="container">
-                       <div class="row">
-                               <div class="col-md-7">
-                                       <h1>Netdata
-                                               <p class="lead">Real-time performance monitoring, in the greatest possible detail</p>
-                                       </h1>
-                               </div>
-                               <div class="col-md-5">
-                                       <div class="well well-lg">
-                                               <div class="row">
-                                               <div class="col-md-6">
-                                                       <b>Drag</b> charts to pan.
-                                                       <b>Shift + wheel</b> on them, to zoom in and out.
-                                                       <b>Double-click</b> on them, to reset.
-                                                       <b>Hover</b> on them too!
-                                                       </div>
-                                               <div class="col-md-6">
-                                                       <div data-netdata="system.intr" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dygraph-type="line" data-dygraph-strokewidth="3" data-dygraph-smooth="true" data-dygraph-highlightcirclesize="6" data-after="-90" data-height="60px" data-colors="#C66"></div>
-                                                       </div>
-                                               </div>
-                                       </div>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="container">
-               <div class="row">
-                       <div class="col-md-10" role="main">
-                               <div id="charts_div"></div>
-                       </div>
-                       <div class="col-md-2" role="complementary">
-                               <nav class="dashboard-sidebar hidden-print hidden-xs hidden-sm" id="sidebar" role="menu"></nav>
-                       </div>
-               </div>
-       </div>
-
-       <div id="footer" class="container" style="display: none;">
-               <div class="row">
-                       <div class="col-md-10" role="main">
-                               <div class="p">
-                                       <big><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></big><br/>
-                                       <i class="fa fa-copyright"></i> Copyright 2016, Costa Tsaousis.<br/>
-                                       Released under <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GPL v3 or later</a>.<br/>
-                               </div>
-                               <div class="p">
-                                       <small>
-                                               <a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a> re-distributes these software tools:
-
-                                               <i class="fa fa-circle"></i> The excellent <a href="http://dygraphs.com/" target="_blank">Dygraphs.com</a> web chart library,
-                                               <i class="fa fa-copyright"></i> Copyright 2009, Dan Vanderkam, <a href="http://dygraphs.com/legal.html" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://omnipotent.net/jquery.sparkline/" target="_blank">jQuery Sparklines</a> web chart library,
-                                               <i class="fa fa-copyright"></i> Copyright 2009-2012, Splunk Inc., <a href="http://opensource.org/licenses/BSD-3-Clause" target="_blank">New BSD License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://benpickles.github.io/peity/" target="_blank">Peity</a> web chart library,
-                                               <i class="fa fa-copyright"></i> Copyright 2009-2015, Ben Pickles, <a href="https://github.com/benpickles/peity/blob/master/MIT-LICENCE" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="https://rendro.github.io/easy-pie-chart/" target="_blank">Easy Pie Chart</a> web chart library,
-                                               <i class="fa fa-copyright"></i> Copyright 2013, Robert Fleischmann, <a href="https://github.com/rendro/easy-pie-chart/blob/master/LICENSE" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://bernii.github.io/gauge.js/" target="_blank">Gauge.js</a> web chart library,
-                                               <i class="fa fa-copyright"></i> Copyright, Bernard Kobos, <a href="http://bernii.github.io/gauge.js/" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="https://jquery.org/" target="_blank">jQuery</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2015, jQuery Foundation, <a href="https://jquery.org/license/" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://getbootstrap.com/getting-started/" target="_blank">Bootstrap</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2015, Twitter, <a href="http://getbootstrap.com/getting-started/#license-faqs" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://www.bootstraptoggle.com/" target="_blank">Bootstrap Toggle</a>,
-                                               <i class="fa fa-copyright"></i> Copyright (c) 2011-2014 Min Hur, The New York Times Company, <a href="https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="https://jamesflorentino.github.io/nanoScrollerJS/" target="_blank">NanoScroller</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2012, James Florentino, <a href="https://github.com/jamesflorentino/nanoScrollerJS/blob/master/LICENSE" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="https://github.com/marcj/css-element-queries" target="_blank">CSS Element Queries</a>,
-                                               <i class="fa fa-copyright"></i> Copyright Marc J. Schmidt, <a href="https://github.com/marcj/css-element-queries/blob/master/LICENSE" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="https://fortawesome.github.io/Font-Awesome/" target="_blank">FontAwesome</a>,
-                                               <i class="fa fa-copyright"></i> Created by Dave Gandy, Font: <a href="http://scripts.sil.org/OFL" target="_blank">SIL OFL 1.1 License</a>, CSS: <a href="http://opensource.org/licenses/mit-license.html" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://www.iconsdb.com/soylent-red-icons/seo-performance-icon.html" target="_blank">IconsDB.com Icons</a>, Icons provided as CC0 1.0 Universal (CC0 1.0) Public Domain Dedication.
-
-                                               <i class="fa fa-circle"></i> <a href="http://morrisjs.github.io/morris.js/" target="_blank">morris.js</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2013, Olly Smith, <a href="http://morrisjs.github.io/morris.js/" target="_blank">Simplified BSD License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://raphaeljs.com/" target="_blank">Raphaël</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2008, Dmitry Baranovskiy, <a href="http://raphaeljs.com/license.html" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://C3js.org/" target="_blank">C3</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2013, Masayuki Tanaka, <a href="https://github.com/masayuki0812/c3/blob/master/LICENSE" target="_blank">MIT License</a>
-
-                                               <i class="fa fa-circle"></i> <a href="http://D3js.org/" target="_blank">D3</a>,
-                                               <i class="fa fa-copyright"></i> Copyright 2015, Mike Bostock, <a href="http://opensource.org/licenses/BSD-3-Clause" target="_blank">BSD License</a>
-
-                                       </small>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="modal fade" id="welcomeModal" tabindex="-1" role="dialog" aria-labelledby="welcomeModalLabel">
-               <div class="modal-dialog modal-lg" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="welcomeModalLabel">Welcome!</h4>
-                               </div>
-                               <div class="modal-body">
-                                               <div class="p">
-                                               <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any type of real-time time series data, from CPU usage, disk activity, SQL queries, API calls, web site visitors, etc.
-                                               </div>
-                                               <div class="p">
-                                               <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> tries to visualize the truth of <b>now</b>, in its <b>greatest detail</b>, so that you can get insights of what is happening now and what just happened, on your systems and applications.
-                                               </div>
-                                               <div class="p">
-                                               To make a chart in <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b>, you just need a <b>number</b>. Just a number you can read somehow. <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> will turn this number to a real time, interactive, web chart. For collecting these numbers, it supports <a href="https://github.com/firehol/netdata/wiki/External-Plugins" target="_blank">external plugins</a>, even <a href="https://github.com/firehol/netdata/wiki/General-Info---charts.d" target="_blank">shell</a> or <a href="https://github.com/firehol/netdata/wiki/General-Info---charts.d" target="_blank">node.js</a> plugins. Any computer program, in any language, that can print a few lines of text on its standard output, can be a netdata data collector.
-                                               </div>
-                                               <div class="p">
-                                               <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> can embed charts everywhere, like this one <div data-netdata="system.cpu" data-dimensions="system" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-show-value-of-system-at="system.cpu.system.modal.1"></div> (my CPU system usage which is <span id="system.cpu.system.modal.1" style="display: inline-block; width: 40px; text-align: right;"></span>%),
-                                               or this one <div data-netdata="ipv4.tcppackets" data-dimensions="received" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-show-value-of-received-at="ipv4.tcppackets.received.modal.1"></div> (my IPv4 received TCP packets, which are <span id="ipv4.tcppackets.received.modal.1" style="display: inline-block; width: 60px; text-align: right;"></span>/second).
-                                               </div>
-                                               <div class="p">
-                                               You can have <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> charts on your site too. Just give it a <code>div</code> and a real time chart, zoomable and draggable will appear (try it even on these tiny ones - <b>drag</b> them to pan horizontally, <b>shift + drag</b> to zoom in, on <b>chrome shift + mouse wheel</b> to zoom in/out, <b>double click</b> on them to reset them - don't be afraid of <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> performance - <a href="https://github.com/firehol/netdata/wiki/Performance" target="_blank">a raspberry pi 2 can sustain 300 charts updates per second</a>!).
-                                               </div>
-                                               <div class="p">
-                                               For more information please refer to the <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata wiki</a></b>.
-                                               </div>
-                               </div>
-                               <div class="modal-footer">
-                                       <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel">
-               <div class="modal-dialog modal-lg" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="helpModalLabel">Dashboard Help</h4>
-                               </div>
-                               <div class="modal-body">
-
-                                       <h4>Dygraphs (line, area and stacked area charts)</h4>
-
-                                       <!-- Nav tabs -->
-                                       <ul class="nav nav-tabs" role="tablist">
-                                               <li role="presentation" class="active"><a href="#help_mouse" aria-controls="help_mouse" role="tab" data-toggle="tab">Mouse Interface</a></li>
-                                               <li role="presentation"><a href="#help_touch" aria-controls="help_touch" role="tab" data-toggle="tab">Touch Interface</a></li>
-                                       </ul>
-
-                                       <!-- Tab panes -->
-                                       <div class="tab-content">
-                                               <div role="tabpanel" class="tab-pane active" id="help_mouse">
-                                                       <div class="p">
-                                                               <h4>Mouse Over / Hover</h4>
-                                                               Mouse over on a chart to show, at its legend, the values for the timestamp under the mouse (the chart will also highlight the point at the chart).
-                                                               <br/>
-                                                               All the other visible charts will also show and highlight their values for the same timestamp.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Drag Chart Contents</h4>
-                                                               Drag the contents of a chart to pan it horizontally.
-                                                               <br/>
-                                                               All the charts will follow soon after you let the chart alone (this little delay is by design: it speeds up your browser and lets you focus on what you are exploring).
-                                                               <br/>
-                                                               Once a chart is panned, auto refreshing stops for all charts. To enable it again, <b>double click</b> a panned chart.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Double Click</h4>
-                                                               Double Click a chart to reset all the charts to their default auto-refreshing state.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>SHIFT + Drag</h4>
-                                                               While pressing the shift key, click on the contents of a chart and move the mouse to select an area, to zoom in. The other charts will follow too. Zooming is performed in two phases:
-                                                               <ul>
-                                                                       <li>The already loaded chart contents are zoomed (low resolution)</li>
-                                                                       <li>New data are transferred from the netdata server, to refresh the chart with possibly more detail.</li>
-                                                               </ul>
-                                                               Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>SHIFT + Mouse Wheel <small>(does not work on firefox and IE/Edge)</small></h4>
-                                                               While pressing the shift key and the mouse pointer is over the contents of a chart, scroll the mouse wheel to zoom in or out. This kind of zooming is aligned to center below the mouse pointer. The other charts will follow too.
-                                                               <br/>
-                                                               Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Legend Operations</h4>
-                                                               Click on the label or value of a dimension, will select / un-select this dimension.
-                                                               <br/>
-                                                               You can press any of the SHIFT or CONTROL keys and then click on legend labels or values, to select / un-select multiple dimensions.
-                                                       </div>
-                                               </div>
-                                               <div role="tabpanel" class="tab-pane" id="help_touch">
-                                                       <div class="p">
-                                                               <h4>Single Tap</h4>
-                                                               Single Tap on the contents of a chart to show, at its legend, the values for the timestamp tapped (the chart will also highlight the point at the chart).
-                                                               <br/>
-                                                               All the other visible charts will also show and highlight their values for the same timestamp.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Drag Chart Contents</h4>
-                                                               Touch and Drag the contents of a chart to pan it horizontally.
-                                                               <br/>
-                                                               All the charts will follow soon after you let the chart alone (this little delay is by design: it speeds up your browser and lets you focus on what you are exploring).
-                                                               <br/>
-                                                               Once a chart is panned, auto refreshing stops for all charts. To enable it again, <b>double tap</b> a panned chart.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Double Tap</h4>
-                                                               Double tap a chart to reset all the charts to their default auto-refreshing state.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Zoom <small>(does not work on firefox and IE/Edge)</small></h4>
-                                                               With two fingers, zoom in or out.
-                                                               <br/>
-                                                               Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
-                                                       </div>
-                                                       <hr/>
-                                                       <div class="p">
-                                                               <h4>Legend Operations</h4>
-                                                               Tap on the label or value of a dimension, will select / un-select this dimension.
-                                                       </div>
-                                               </div>
-                                       </div>
-                               </div>
-                               <div class="modal-footer">
-                                       <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="modal fade" id="optionsModal" tabindex="-1" role="dialog" aria-labelledby="optionsModalLabel">
-               <div class="modal-dialog modal-lg" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="optionsModalLabel">netdata dashboard options</h4>
-                               </div>
-                               <div class="modal-body">
-                                       <center>
-                                               <small style="color: #BBBBBB;">These are browser settings. Each viewer has its own. They do not affect the operation of your netdata server.
-                                               <br/>
-                                               Settings take effect immediately and are saved permanently to browser local storage (except the refresh on focus / always option).
-                                               <br/>
-                                               To reset all options (including charts sizes) to their defaults, click <a href="#" onclick="resetDashboardOptions(); return false;">here</a>.</small>
-                                       </center>
-                                       <div style="padding: 10px;"></div>
-
-                                       <!-- Nav tabs -->
-                                       <ul class="nav nav-tabs" role="tablist">
-                                               <li role="presentation" class="active"><a href="#settings_performance" aria-controls="settings_performance" role="tab" data-toggle="tab">Performance</a></li>
-                                               <li role="presentation"><a href="#settings_sync" aria-controls="settings_sync" role="tab" data-toggle="tab">Synchronization</a></li>
-                                               <li role="presentation"><a href="#settings_visual" aria-controls="settings_visual" role="tab" data-toggle="tab">Visual</a></li>
-                                       </ul>
-
-                                       <!-- Tab panes -->
-                                       <div class="tab-content">
-                                               <div role="tabpanel" class="tab-pane active" id="settings_performance">
-                                                       <form id="optionsForm1" method="get" class="form-horizontal">
-                                                               <div class="form-group">
-                                                                       <table>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="stop_updates_when_focus_is_lost" type="checkbox" checked data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="On Focus" data-off="Always" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>When to refresh the charts?</strong><br/>
-                                                                                       <small>When set to <b>On Focus</b>, the charts will stop being updated if the page / tab does not have the focus of the user. When set to <b>Always</b>, the charts will always be refreshed. Set it to <b>On Focus</b> it to lower the CPU requirements of the browser (and extend the battery of laptops and tablets) when this page does not have your focus. Set to <b>Always</b> to work on another window (i.e. change the settings of something) and have the charts auto-refresh in this window.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control">
-                                                                               <input id="eliminate_zero_dimensions" type="checkbox" checked data-toggle="toggle" data-on="Non Zero" data-off="All" data-width="110px">
-                                                                               </td>
-                                                                               <td class="option-info"><strong>Which dimensions to show?</strong><br/>
-                                                                                       <small>When set to <b>Non Zero</b>, dimensions that have all their values (within the current view) set to zero will not be transferred from the netdata server (except if all dimensions of the chart are zero, in which case this setting does nothing - all dimensions are transferred and shown). When set to <b>All</b>, all dimensions will always be shown. Set it to <b>Non Zero</b> to lower the data transferred between netdata and your browser, lower the CPU requirements of your browser (fewer lines to draw) and increase the focus on the legends (fewer entries at the legends).</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="destroy_on_hide" type="checkbox" data-toggle="toggle" data-on="Destroy" data-off="Hide" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>How to handle hidden charts?</strong><br/>
-                                                                                       <small>When set to <b>Destroy</b>, charts that are not in the current viewport of the browser (are above, or below the visible area of the page), will be destroyed and re-created if and when they become visible again. When set to <b>Hide</b>, the not-visible charts will be just hidden, to simplify the DOM and speed up your browser. Set it to <b>Destroy</b>, to lower the memory requirements of your browser. Set it to <b>Hide</b> for smoother page scrolling.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       </table>
-                                                               </div>
-                                                       </form>
-                                               </div>
-                                               <div role="tabpanel" class="tab-pane" id="settings_sync">
-                                                       <form id="optionsForm2" method="get" class="form-horizontal">
-                                                               <div class="form-group">
-                                                                       <table>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="parallel_refresher" type="checkbox" checked data-toggle="toggle" data-on="Parallel" data-off="Sequential" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Which chart refresh policy to use?</strong><br/>
-                                                                                       <small>When set to <b>parallel</b>, visible charts are refreshed in parallel (all queries are sent to netdata server in parallel) and are rendered asynchronously. When set to <b>sequential</b> charts are refreshed one after another. Set it to parallel if your browser can cope with it (most modern browsers do), set it to sequential if you work on an older/slower computer.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row" id="concurrent_refreshes_row">
-                                                                               <td class="option-control"></td>
-                                                                               <td class="option-info">
-                                                                                       <table>
-                                                                                       <tr class="option-row">
-                                                                                       <td class="option-control">
-                                                                                       <input id="concurrent_refreshes" type="checkbox" checked data-toggle="toggle" data-on="Resync" data-off="Best Effort" data-width="110px">
-                                                                                       </td>
-                                                                                       <td class="option-info">
-                                                                                       <strong>Shall we re-sync chart refreshes?</strong><br/>
-                                                                                       <small>When set to <b>Resync</b>, the dashboard will attempt to re-synchronize all the charts so that they are refreshed concurrently. When set to <b>Best Effort</b>, each chart may be refreshed with a little time difference to the others. Normally, the dashboard starts refreshing them in parallel, but depending on the speed of your computer and the network latencies, charts start having a slight time difference. Setting this to <b>Resync</b> will attempt to re-synchronize the charts on every update. Setting it to <b>Best Effort</b> may lower the pressure on your browser and the network.</small>
-                                                                                       </td>
-                                                                                       </tr>
-                                                                                       </table>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="sync_selection" type="checkbox" checked data-toggle="toggle" data-on="Sync" data-off="Don't Sync" data-onstyle="success" data-offstyle="danger" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Sync hover selection on all charts?</strong><br/>
-                                                                                       <small>When enabled, a selection on one chart will automatically select the same time on all other visible charts and the legends of all visible charts will be updated to show the selected values. When disabled, only the chart getting the user's attention will be selected. Enable it to get better insights of the data. Disable it if you are on a very slow computer that cannot actually do it.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="sync_pan_and_zoom" type="checkbox" checked data-toggle="toggle"  data-on="Sync" data-off="Don't Sync" data-onstyle="success" data-offstyle="danger" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Sync pan and zoom on all charts?</strong><br/>
-                                                                                       <small>When enabled, pan and zoom operations on a chart will be replicated to all charts (even the ones that are not currently visible - of course only when they will become visible). When disabled, pan and zoom operations will not be propagated to other charts.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       </table>
-                                                               </div>
-                                                       </form>
-                                               </div>
-                                               <div role="tabpanel" class="tab-pane" id="settings_visual">
-                                                       <form id="optionsForm3" method="get" class="form-horizontal">
-                                                               <div class="form-group">
-                                                                       <table>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="netdata_theme_control" type="checkbox" checked data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="Dark" data-off="White" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Which theme to use?</strong><br/>
-                                                                                       <small>Netdata comes with two themes: <b>Dark</b> (the default) and <b>White</b>.
-                                                                                       <br/>
-                                                                                       <b>Switching this will reload the dashboard</b>.
-                                                                                       </small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="show_help" type="checkbox" checked data-toggle="toggle" data-on="Help Me" data-off="No Help" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Do you need help?</strong><br/>
-                                                                                       <small>Netdata can show some help in some areas to help you use the dashboard. If all these balloons bother you, disable them using this switch.
-                                                                                       <br/>
-                                                                                       <b>Switching this will reload the dashboard</b>.
-                                                                                       </small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="pan_and_zoom_data_padding" type="checkbox" checked data-toggle="toggle"  data-on="Pad" data-off="Don't Pad" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Enable data padding when panning and zooming?</strong><br/>
-                                                                                       <small>When set to <b>Pad</b> the charts will be padded with more data, both before and after the visible area, thus giving the impression the whole database is loaded. This padding will happen only after the first pan or zoom operation on the chart (initially all charts have only the visible data). When set to <b>Don't Pad</b> only the visible data will be transfered from the netdata server, even after the first pan and zoom operation.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       <tr class="option-row">
-                                                                               <td class="option-control"><input id="smooth_plot" type="checkbox" checked data-toggle="toggle"  data-on="Smooth" data-off="Rough" data-width="110px"></td>
-                                                                               <td class="option-info"><strong>Enable Bézier lines on charts?</strong><br/>
-                                                                                       <small>When set to <b>Smooth</b> the charts libraries that support it, will plot smooth curves instead of simple straight lines to connect the points.
-                                                                                       <br/>
-                                                                                       Keep in mind <a href="http://dygraphs.com" target="_blank">dygraphs</a>, the main charting library in netdata dashboards, can only smooth line charts. It cannot smooth area or stacked charts. When set to <b>Rough</b>, this setting can lower the CPU resources consumed by your browser.</small>
-                                                                               </td>
-                                                                               </tr>
-                                                                       </table>
-                                                               </div>
-                                                       </form>
-                                               </div>
-                                       </div>
-                               </div>
-                               <div class="modal-footer">
-                                       <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-
-       <div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-labelledby="updateModalLabel">
-               <div class="modal-dialog" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="updateModalLabel">Update Check</h4>
-                               </div>
-                               <div class="modal-body">
-                                       Your netdata version: <b><code><span id="netdataVersion">Unknown</span></code></b>
-                                       <br/>
-                                       <div style="padding: 10px;"></div>
-                                       <div id="versionCheckLog">Not checked yet. Please press the Check Now button.</div>
-                               </div>
-                               <div class="modal-footer">
-                                       <a href="#" onclick="notifyForUpdate(true); return false;" type="button" class="btn btn-default">Check Now</a>
-                                       <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="modal fade" id="deleteRegistryModal" tabindex="-1" role="dialog" aria-labelledby="deleteRegistryModalLabel">
-               <div class="modal-dialog" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="deleteRegistryModalLabel">Delete <span id="deleteRegistryServerName"></span>?</h4>
-                               </div>
-                               <div class="modal-body">
-                                       You are about to delete, from your personal list of netdata servers, the following server:
-                                       <p style="text-align: center; padding-top: 10px; padding-bottom: 10px; line-height: 2;">
-                                       <b><span id="deleteRegistryServerName2"></span></b>
-                                       <br/>
-                                       <b><span id="deleteRegistryServerURL"></span></b>
-                                       </p>
-                                       Are you sure you want to do this?
-                                       <br/>
-                                       <div style="padding: 10px;"></div>
-                                       <small>Keep in mind, this server will be added back if and when you visit it again.</small>
-                                       <br/>
-                                       <div id="deleteRegistryResponse" style="display: block; width: 100%; text-align: center; padding-top: 20px;"></div>
-                               </div>
-                               <div class="modal-footer">
-                                       <button type="button" class="btn btn-success" data-dismiss="modal">keep it</button>
-                                       <a href="#" onclick="notifyForDeleteRegistry(true); return false;" type="button" class="btn btn-danger">delete it</a>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="modal fade" id="switchRegistryModal" tabindex="-1" role="dialog" aria-labelledby="switchRegistryModalLabel">
-               <div class="modal-dialog" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="switchRegistryModalLabel">Switch Netdata Registry Identity</h4>
-                               </div>
-                               <div class="modal-body">
-                                       You can copy and paste the following ID to all your browsers (e.g. work and home).
-                                       <br/>
-                                       All the browsers with the same ID will identify <b>you</b>, so please don't share this with others.
-                                       <p style="text-align: center; padding-top: 10px; padding-bottom: 10px; line-height: 2;">
-                                       <form action="#">
-                                       <input type="text" class="form-control" id="switchRegistryPersonGUID" placeholder="your personal ID" maxlength="36" autocomplete="off" style="text-align: center; font-size: 1.4em;">
-                                       </form>
-                                       </p>
-                                       Either copy this ID and paste it to another browser, or paste here the ID you have taken from another browser.
-                                       <p style="padding-top: 10px;"><small>
-                                               Keep in mind that:
-                                               <ul>
-                                                       <li>when you switch ID, your previous ID will be lost forever - this is irreversible.</li>
-                                                       <li>both IDs (your old and the new) must list this netdata at their personal lists.</li>
-                                                       <li>both IDs have to be known by the registry: <b><span id="switchRegistryURL"></span></b>.</li>
-                                                       <li>to get a new ID, just clear your browser cookies.</li>
-                                               </ul>
-                                       </small></p>
-                                       <div id="switchRegistryResponse" style="display: block; width: 100%; text-align: center; padding-top: 20px;"></div>
-                               </div>
-                               <div class="modal-footer">
-                                       <button type="button" class="btn btn-success" data-dismiss="modal">cancel</button>
-                                       <a href="#" onclick="notifyForSwitchRegistry(true); return false;" type="button" class="btn btn-danger">impersonate</a>
-                               </div>
-                       </div>
-               </div>
-       </div>
-
-       <div class="modal fade" id="gotoServerModal" tabindex="-1" role="dialog" aria-labelledby="gotoServerModalLabel">
-               <div class="modal-dialog" role="document">
-                       <div class="modal-content">
-                               <div class="modal-header">
-                                       <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
-                                       <h4 class="modal-title" id="gotoServerModalLabel"><span id="gotoServerName"></span></h4>
-                               </div>
-                               <div class="modal-body">
-                                       Checking known URLs for this server...
-                                       <div  style="padding-top: 20px;">
-                                               <table id="gotoServerList">
-                                               </table>
-                                       </div>
-                                       <p style="padding-top: 10px;"><small>
-                                               Checks may fail if you are viewing an HTTPS page and the server to be checked is HTTP only.
-                                       </small></p>
-                                       <div id="gotoServerResponse" style="display: block; width: 100%; text-align: center; padding-top: 20px;"></div>
-                               </div>
-                               <div class="modal-footer">
-                                       <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
-                               </div>
-                       </div>
-               </div>
-       </div>
+    <div id="loadOverlay" class="loadOverlay" style="background-color: #888; color: #888;">
+        netdata<br/><div style="font-size: 3vh;">Real-time performance monitoring, done right!</div>
+    </div>
+    <script type="text/javascript">
+        // change the loadOverlay colors ASAP to match the theme
+        document.getElementById('loadOverlay').style = (urlOptions.theme === 'slate')?"background-color:#272b30; color: #373b40;":"background-color:#fff; color: #ddd;";
+    </script>
+    <nav class="navbar navbar-default navbar-fixed-top" role="banner">
+        <div class="container">
+            <nav id="mynetdata_nav" class="collapse navbar-collapse navbar-left hidden-sm hidden-xs" role="navigation" style="padding-right: 20px;">
+                <ul class="nav navbar-nav">
+                    <li class="dropdown">
+                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a>
+                        <ul class="dropdown-menu scrollable-menu inpagemenu multi-column columns-2" role="menu">
+                            <div class="row">
+                                <div class="col-sm-6" style="width: 85%; padding-right: 0;">
+                                    <ul id="mynetdata_servers" class="multi-column-dropdown">
+                                        <li><a href="#" onclick="return false;" style="color: #999;">loading...</a></li>
+                                    </ul>
+                                </div>
+                                <div class="col-sm-3 hidden-xs" style="width: 15%; padding-left: 0;">
+                                    <ul id="mynetdata_actions1" class="multi-column-dropdown">
+                                    <li style="color: #999;">&nbsp;</li>
+                                    </ul>
+                                </div>
+                            </div>
+                        </ul>
+                    </li>
+                </ul>
+            </nav>
+            <div class="navbar-header">
+                <button class="navbar-toggle" type="button" data-toggle="collapse" data-target=".navbar-collapse">
+                    <span class="sr-only">Toggle navigation</span>
+                    <span class="icon-bar"></span>
+                    <span class="icon-bar"></span>
+                    <span class="icon-bar"></span>
+                </button>
+                <a href="/" class="navbar-brand" id="hostname">netdata</a>
+            </div>
+            <nav class="collapse navbar-collapse navbar-right" role="navigation">
+                <ul class="nav navbar-nav">
+                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
+                    <li class="hidden-sm"><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
+                    <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fa fa-cloud-download"></i> update</a></li>
+                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
+                    <li class="dropdown hidden-md hidden-lg hidden-xs">
+                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">Menu <strong class="caret"></strong></a>
+                        <ul class="dropdown-menu scrollable-menu inpagemenu" role="menu">
+                            <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
+                            <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
+                            <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
+                        </ul>
+                    </li>
+                    <li class="dropdown hidden-sm hidden-md hidden-lg">
+                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">my-netdata <strong class="caret"></strong></a>
+                        <ul id="mynetdata_servers2" class="dropdown-menu scrollable-menu inpagemenu" role="menu">
+                            <li><a href="#" onclick="return false;" style="color: #999;">loading...</a></li>
+                        </ul>
+                    </li>
+                </ul>
+            </nav>
+    </nav>
+        </div>
+    </nav>
+
+    <div id="masthead" style="display: none;">
+        <div class="container">
+            <div class="row">
+                <div class="col-md-7">
+                    <h1>Netdata
+                        <p class="lead">Real-time performance monitoring, in the greatest possible detail</p>
+                    </h1>
+                </div>
+                <div class="col-md-5">
+                    <div class="well well-lg">
+                        <div class="row">
+                        <div class="col-md-6">
+                            <b>Drag</b> charts to pan.
+                            <b>Shift + wheel</b> on them, to zoom in and out.
+                            <b>Double-click</b> on them, to reset.
+                            <b>Hover</b> on them too!
+                            </div>
+                        <div class="col-md-6">
+                            <div data-netdata="system.intr" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dygraph-type="line" data-dygraph-strokewidth="3" data-dygraph-smooth="true" data-dygraph-highlightcirclesize="6" data-after="-90" data-height="60px" data-colors="#C66"></div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="container">
+        <div class="row">
+            <div class="col-md-10" role="main">
+                <div id="charts_div"></div>
+            </div>
+            <div class="col-md-2" role="complementary">
+                <nav class="dashboard-sidebar hidden-print hidden-xs hidden-sm" id="sidebar" role="menu"></nav>
+            </div>
+        </div>
+    </div>
+
+    <div id="footer" class="container" style="display: none;">
+        <div class="row">
+            <div class="col-md-10" role="main">
+                <div class="p">
+                    <big><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></big><br/>
+                    <i class="fa fa-copyright"></i> Copyright 2016, Costa Tsaousis.<br/>
+                    Released under <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GPL v3 or later</a>.<br/>
+                </div>
+                <div class="p">
+                    <small>
+                        <a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a> re-distributes these software tools:
+
+                        <i class="fa fa-circle"></i> The excellent <a href="http://dygraphs.com/" target="_blank">Dygraphs.com</a> web chart library,
+                        <i class="fa fa-copyright"></i> Copyright 2009, Dan Vanderkam, <a href="http://dygraphs.com/legal.html" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://omnipotent.net/jquery.sparkline/" target="_blank">jQuery Sparklines</a> web chart library,
+                        <i class="fa fa-copyright"></i> Copyright 2009-2012, Splunk Inc., <a href="http://opensource.org/licenses/BSD-3-Clause" target="_blank">New BSD License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://benpickles.github.io/peity/" target="_blank">Peity</a> web chart library,
+                        <i class="fa fa-copyright"></i> Copyright 2009-2015, Ben Pickles, <a href="https://github.com/benpickles/peity/blob/master/MIT-LICENCE" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="https://rendro.github.io/easy-pie-chart/" target="_blank">Easy Pie Chart</a> web chart library,
+                        <i class="fa fa-copyright"></i> Copyright 2013, Robert Fleischmann, <a href="https://github.com/rendro/easy-pie-chart/blob/master/LICENSE" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://bernii.github.io/gauge.js/" target="_blank">Gauge.js</a> web chart library,
+                        <i class="fa fa-copyright"></i> Copyright, Bernard Kobos, <a href="http://bernii.github.io/gauge.js/" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="https://jquery.org/" target="_blank">jQuery</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2015, jQuery Foundation, <a href="https://jquery.org/license/" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://getbootstrap.com/getting-started/" target="_blank">Bootstrap</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2015, Twitter, <a href="http://getbootstrap.com/getting-started/#license-faqs" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://www.bootstraptoggle.com/" target="_blank">Bootstrap Toggle</a>,
+                        <i class="fa fa-copyright"></i> Copyright (c) 2011-2014 Min Hur, The New York Times Company, <a href="https://github.com/minhur/bootstrap-toggle/blob/master/LICENSE" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="https://jamesflorentino.github.io/nanoScrollerJS/" target="_blank">NanoScroller</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2012, James Florentino, <a href="https://github.com/jamesflorentino/nanoScrollerJS/blob/master/LICENSE" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="https://github.com/marcj/css-element-queries" target="_blank">CSS Element Queries</a>,
+                        <i class="fa fa-copyright"></i> Copyright Marc J. Schmidt, <a href="https://github.com/marcj/css-element-queries/blob/master/LICENSE" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="https://fortawesome.github.io/Font-Awesome/" target="_blank">FontAwesome</a>,
+                        <i class="fa fa-copyright"></i> Created by Dave Gandy, Font: <a href="http://scripts.sil.org/OFL" target="_blank">SIL OFL 1.1 License</a>, CSS: <a href="http://opensource.org/licenses/mit-license.html" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://www.iconsdb.com/soylent-red-icons/seo-performance-icon.html" target="_blank">IconsDB.com Icons</a>, Icons provided as CC0 1.0 Universal (CC0 1.0) Public Domain Dedication.
+
+                        <i class="fa fa-circle"></i> <a href="http://morrisjs.github.io/morris.js/" target="_blank">morris.js</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2013, Olly Smith, <a href="http://morrisjs.github.io/morris.js/" target="_blank">Simplified BSD License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://raphaeljs.com/" target="_blank">Raphaël</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2008, Dmitry Baranovskiy, <a href="http://raphaeljs.com/license.html" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://C3js.org/" target="_blank">C3</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2013, Masayuki Tanaka, <a href="https://github.com/masayuki0812/c3/blob/master/LICENSE" target="_blank">MIT License</a>
+
+                        <i class="fa fa-circle"></i> <a href="http://D3js.org/" target="_blank">D3</a>,
+                        <i class="fa fa-copyright"></i> Copyright 2015, Mike Bostock, <a href="http://opensource.org/licenses/BSD-3-Clause" target="_blank">BSD License</a>
+
+                    </small>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="welcomeModal" tabindex="-1" role="dialog" aria-labelledby="welcomeModalLabel">
+        <div class="modal-dialog modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="welcomeModalLabel">Welcome!</h4>
+                </div>
+                <div class="modal-body">
+                        <div class="p">
+                        <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any type of real-time time series data, from CPU usage, disk activity, SQL queries, API calls, web site visitors, etc.
+                        </div>
+                        <div class="p">
+                        <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> tries to visualize the truth of <b>now</b>, in its <b>greatest detail</b>, so that you can get insights of what is happening now and what just happened, on your systems and applications.
+                        </div>
+                        <div class="p">
+                        To make a chart in <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b>, you just need a <b>number</b>. Just a number you can read somehow. <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> will turn this number to a real time, interactive, web chart. For collecting these numbers, it supports <a href="https://github.com/firehol/netdata/wiki/External-Plugins" target="_blank">external plugins</a>, even <a href="https://github.com/firehol/netdata/wiki/General-Info---charts.d" target="_blank">shell</a> or <a href="https://github.com/firehol/netdata/wiki/General-Info---charts.d" target="_blank">node.js</a> plugins. Any computer program, in any language, that can print a few lines of text on its standard output, can be a netdata data collector.
+                        </div>
+                        <div class="p">
+                        <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> can embed charts everywhere, like this one <div data-netdata="system.cpu" data-dimensions="system" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-show-value-of-system-at="system.cpu.system.modal.1"></div> (my CPU system usage which is <span id="system.cpu.system.modal.1" style="display: inline-block; width: 40px; text-align: right;"></span>%),
+                        or this one <div data-netdata="ipv4.tcppackets" data-dimensions="received" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-show-value-of-received-at="ipv4.tcppackets.received.modal.1"></div> (my IPv4 received TCP packets, which are <span id="ipv4.tcppackets.received.modal.1" style="display: inline-block; width: 60px; text-align: right;"></span>/second).
+                        </div>
+                        <div class="p">
+                        You can have <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> charts on your site too. Just give it a <code>div</code> and a real time chart, zoomable and draggable will appear (try it even on these tiny ones - <b>drag</b> them to pan horizontally, <b>shift + drag</b> to zoom in, on <b>chrome shift + mouse wheel</b> to zoom in/out, <b>double click</b> on them to reset them - don't be afraid of <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> performance - <a href="https://github.com/firehol/netdata/wiki/Performance" target="_blank">a raspberry pi 2 can sustain 300 charts updates per second</a>!).
+                        </div>
+                        <div class="p">
+                        For more information please refer to the <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata wiki</a></b>.
+                        </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="helpModal" tabindex="-1" role="dialog" aria-labelledby="helpModalLabel">
+        <div class="modal-dialog modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="helpModalLabel">Dashboard Help</h4>
+                </div>
+                <div class="modal-body">
+
+                    <h4>Dygraphs (line, area and stacked area charts)</h4>
+
+                    <!-- Nav tabs -->
+                    <ul class="nav nav-tabs" role="tablist">
+                        <li role="presentation" class="active"><a href="#help_mouse" aria-controls="help_mouse" role="tab" data-toggle="tab">Mouse Interface</a></li>
+                        <li role="presentation"><a href="#help_touch" aria-controls="help_touch" role="tab" data-toggle="tab">Touch Interface</a></li>
+                    </ul>
+
+                    <!-- Tab panes -->
+                    <div class="tab-content">
+                        <div role="tabpanel" class="tab-pane active" id="help_mouse">
+                            <div class="p">
+                                <h4>Mouse Over / Hover</h4>
+                                Mouse over on a chart to show, at its legend, the values for the timestamp under the mouse (the chart will also highlight the point at the chart).
+                                <br/>
+                                All the other visible charts will also show and highlight their values for the same timestamp.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Drag Chart Contents</h4>
+                                Drag the contents of a chart to pan it horizontally.
+                                <br/>
+                                All the charts will follow soon after you let the chart alone (this little delay is by design: it speeds up your browser and lets you focus on what you are exploring).
+                                <br/>
+                                Once a chart is panned, auto refreshing stops for all charts. To enable it again, <b>double click</b> a panned chart.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Double Click</h4>
+                                Double Click a chart to reset all the charts to their default auto-refreshing state.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>SHIFT + Drag</h4>
+                                While pressing the shift key, click on the contents of a chart and move the mouse to select an area, to zoom in. The other charts will follow too. Zooming is performed in two phases:
+                                <ul>
+                                    <li>The already loaded chart contents are zoomed (low resolution)</li>
+                                    <li>New data are transferred from the netdata server, to refresh the chart with possibly more detail.</li>
+                                </ul>
+                                Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>SHIFT + Mouse Wheel <small>(does not work on firefox and IE/Edge)</small></h4>
+                                While pressing the shift key and the mouse pointer is over the contents of a chart, scroll the mouse wheel to zoom in or out. This kind of zooming is aligned to center below the mouse pointer. The other charts will follow too.
+                                <br/>
+                                Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Legend Operations</h4>
+                                Click on the label or value of a dimension, will select / un-select this dimension.
+                                <br/>
+                                You can press any of the SHIFT or CONTROL keys and then click on legend labels or values, to select / un-select multiple dimensions.
+                            </div>
+                        </div>
+                        <div role="tabpanel" class="tab-pane" id="help_touch">
+                            <div class="p">
+                                <h4>Single Tap</h4>
+                                Single Tap on the contents of a chart to show, at its legend, the values for the timestamp tapped (the chart will also highlight the point at the chart).
+                                <br/>
+                                All the other visible charts will also show and highlight their values for the same timestamp.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Drag Chart Contents</h4>
+                                Touch and Drag the contents of a chart to pan it horizontally.
+                                <br/>
+                                All the charts will follow soon after you let the chart alone (this little delay is by design: it speeds up your browser and lets you focus on what you are exploring).
+                                <br/>
+                                Once a chart is panned, auto refreshing stops for all charts. To enable it again, <b>double tap</b> a panned chart.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Double Tap</h4>
+                                Double tap a chart to reset all the charts to their default auto-refreshing state.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Zoom <small>(does not work on firefox and IE/Edge)</small></h4>
+                                With two fingers, zoom in or out.
+                                <br/>
+                                Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
+                            </div>
+                            <hr/>
+                            <div class="p">
+                                <h4>Legend Operations</h4>
+                                Tap on the label or value of a dimension, will select / un-select this dimension.
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="optionsModal" tabindex="-1" role="dialog" aria-labelledby="optionsModalLabel">
+        <div class="modal-dialog modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="optionsModalLabel">netdata dashboard options</h4>
+                </div>
+                <div class="modal-body">
+                    <center>
+                        <small style="color: #BBBBBB;">These are browser settings. Each viewer has its own. They do not affect the operation of your netdata server.
+                        <br/>
+                        Settings take effect immediately and are saved permanently to browser local storage (except the refresh on focus / always option).
+                        <br/>
+                        To reset all options (including charts sizes) to their defaults, click <a href="#" onclick="resetDashboardOptions(); return false;">here</a>.</small>
+                    </center>
+                    <div style="padding: 10px;"></div>
+
+                    <!-- Nav tabs -->
+                    <ul class="nav nav-tabs" role="tablist">
+                        <li role="presentation" class="active"><a href="#settings_performance" aria-controls="settings_performance" role="tab" data-toggle="tab">Performance</a></li>
+                        <li role="presentation"><a href="#settings_sync" aria-controls="settings_sync" role="tab" data-toggle="tab">Synchronization</a></li>
+                        <li role="presentation"><a href="#settings_visual" aria-controls="settings_visual" role="tab" data-toggle="tab">Visual</a></li>
+                    </ul>
+
+                    <!-- Tab panes -->
+                    <div class="tab-content">
+                        <div role="tabpanel" class="tab-pane active" id="settings_performance">
+                            <form id="optionsForm1" method="get" class="form-horizontal">
+                                <div class="form-group">
+                                    <table>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="stop_updates_when_focus_is_lost" type="checkbox" checked data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="On Focus" data-off="Always" data-width="110px"></td>
+                                        <td class="option-info"><strong>When to refresh the charts?</strong><br/>
+                                            <small>When set to <b>On Focus</b>, the charts will stop being updated if the page / tab does not have the focus of the user. When set to <b>Always</b>, the charts will always be refreshed. Set it to <b>On Focus</b> it to lower the CPU requirements of the browser (and extend the battery of laptops and tablets) when this page does not have your focus. Set to <b>Always</b> to work on another window (i.e. change the settings of something) and have the charts auto-refresh in this window.</small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control">
+                                        <input id="eliminate_zero_dimensions" type="checkbox" checked data-toggle="toggle" data-on="Non Zero" data-off="All" data-width="110px">
+                                        </td>
+                                        <td class="option-info"><strong>Which dimensions to show?</strong><br/>
+                                            <small>When set to <b>Non Zero</b>, dimensions that have all their values (within the current view) set to zero will not be transferred from the netdata server (except if all dimensions of the chart are zero, in which case this setting does nothing - all dimensions are transferred and shown). When set to <b>All</b>, all dimensions will always be shown. Set it to <b>Non Zero</b> to lower the data transferred between netdata and your browser, lower the CPU requirements of your browser (fewer lines to draw) and increase the focus on the legends (fewer entries at the legends).</small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="destroy_on_hide" type="checkbox" data-toggle="toggle" data-on="Destroy" data-off="Hide" data-width="110px"></td>
+                                        <td class="option-info"><strong>How to handle hidden charts?</strong><br/>
+                                            <small>When set to <b>Destroy</b>, charts that are not in the current viewport of the browser (are above, or below the visible area of the page), will be destroyed and re-created if and when they become visible again. When set to <b>Hide</b>, the not-visible charts will be just hidden, to simplify the DOM and speed up your browser. Set it to <b>Destroy</b>, to lower the memory requirements of your browser. Set it to <b>Hide</b> for smoother page scrolling.</small>
+                                        </td>
+                                        </tr>
+                                    </table>
+                                </div>
+                            </form>
+                        </div>
+                        <div role="tabpanel" class="tab-pane" id="settings_sync">
+                            <form id="optionsForm2" method="get" class="form-horizontal">
+                                <div class="form-group">
+                                    <table>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="parallel_refresher" type="checkbox" checked data-toggle="toggle" data-on="Parallel" data-off="Sequential" data-width="110px"></td>
+                                        <td class="option-info"><strong>Which chart refresh policy to use?</strong><br/>
+                                            <small>When set to <b>parallel</b>, visible charts are refreshed in parallel (all queries are sent to netdata server in parallel) and are rendered asynchronously. When set to <b>sequential</b> charts are refreshed one after another. Set it to parallel if your browser can cope with it (most modern browsers do), set it to sequential if you work on an older/slower computer.</small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row" id="concurrent_refreshes_row">
+                                        <td class="option-control"></td>
+                                        <td class="option-info">
+                                            <table>
+                                            <tr class="option-row">
+                                            <td class="option-control">
+                                            <input id="concurrent_refreshes" type="checkbox" checked data-toggle="toggle" data-on="Resync" data-off="Best Effort" data-width="110px">
+                                            </td>
+                                            <td class="option-info">
+                                            <strong>Shall we re-sync chart refreshes?</strong><br/>
+                                            <small>When set to <b>Resync</b>, the dashboard will attempt to re-synchronize all the charts so that they are refreshed concurrently. When set to <b>Best Effort</b>, each chart may be refreshed with a little time difference to the others. Normally, the dashboard starts refreshing them in parallel, but depending on the speed of your computer and the network latencies, charts start having a slight time difference. Setting this to <b>Resync</b> will attempt to re-synchronize the charts on every update. Setting it to <b>Best Effort</b> may lower the pressure on your browser and the network.</small>
+                                            </td>
+                                            </tr>
+                                            </table>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="sync_selection" type="checkbox" checked data-toggle="toggle" data-on="Sync" data-off="Don't Sync" data-onstyle="success" data-offstyle="danger" data-width="110px"></td>
+                                        <td class="option-info"><strong>Sync hover selection on all charts?</strong><br/>
+                                            <small>When enabled, a selection on one chart will automatically select the same time on all other visible charts and the legends of all visible charts will be updated to show the selected values. When disabled, only the chart getting the user's attention will be selected. Enable it to get better insights of the data. Disable it if you are on a very slow computer that cannot actually do it.</small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="sync_pan_and_zoom" type="checkbox" checked data-toggle="toggle"  data-on="Sync" data-off="Don't Sync" data-onstyle="success" data-offstyle="danger" data-width="110px"></td>
+                                        <td class="option-info"><strong>Sync pan and zoom on all charts?</strong><br/>
+                                            <small>When enabled, pan and zoom operations on a chart will be replicated to all charts (even the ones that are not currently visible - of course only when they will become visible). When disabled, pan and zoom operations will not be propagated to other charts.</small>
+                                        </td>
+                                        </tr>
+                                    </table>
+                                </div>
+                            </form>
+                        </div>
+                        <div role="tabpanel" class="tab-pane" id="settings_visual">
+                            <form id="optionsForm3" method="get" class="form-horizontal">
+                                <div class="form-group">
+                                    <table>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="netdata_theme_control" type="checkbox" checked data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="Dark" data-off="White" data-width="110px"></td>
+                                        <td class="option-info"><strong>Which theme to use?</strong><br/>
+                                            <small>Netdata comes with two themes: <b>Dark</b> (the default) and <b>White</b>.
+                                            <br/>
+                                            <b>Switching this will reload the dashboard</b>.
+                                            </small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="show_help" type="checkbox" checked data-toggle="toggle" data-on="Help Me" data-off="No Help" data-width="110px"></td>
+                                        <td class="option-info"><strong>Do you need help?</strong><br/>
+                                            <small>Netdata can show some help in some areas to help you use the dashboard. If all these balloons bother you, disable them using this switch.
+                                            <br/>
+                                            <b>Switching this will reload the dashboard</b>.
+                                            </small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="pan_and_zoom_data_padding" type="checkbox" checked data-toggle="toggle"  data-on="Pad" data-off="Don't Pad" data-width="110px"></td>
+                                        <td class="option-info"><strong>Enable data padding when panning and zooming?</strong><br/>
+                                            <small>When set to <b>Pad</b> the charts will be padded with more data, both before and after the visible area, thus giving the impression the whole database is loaded. This padding will happen only after the first pan or zoom operation on the chart (initially all charts have only the visible data). When set to <b>Don't Pad</b> only the visible data will be transfered from the netdata server, even after the first pan and zoom operation.</small>
+                                        </td>
+                                        </tr>
+                                    <tr class="option-row">
+                                        <td class="option-control"><input id="smooth_plot" type="checkbox" checked data-toggle="toggle"  data-on="Smooth" data-off="Rough" data-width="110px"></td>
+                                        <td class="option-info"><strong>Enable Bézier lines on charts?</strong><br/>
+                                            <small>When set to <b>Smooth</b> the charts libraries that support it, will plot smooth curves instead of simple straight lines to connect the points.
+                                            <br/>
+                                            Keep in mind <a href="http://dygraphs.com" target="_blank">dygraphs</a>, the main charting library in netdata dashboards, can only smooth line charts. It cannot smooth area or stacked charts. When set to <b>Rough</b>, this setting can lower the CPU resources consumed by your browser.</small>
+                                        </td>
+                                        </tr>
+                                    </table>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+    <div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-labelledby="updateModalLabel">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="updateModalLabel">Update Check</h4>
+                </div>
+                <div class="modal-body">
+                    Your netdata version: <b><code><span id="netdataVersion">Unknown</span></code></b>
+                    <br/>
+                    <div style="padding: 10px;"></div>
+                    <div id="versionCheckLog">Not checked yet. Please press the Check Now button.</div>
+                </div>
+                <div class="modal-footer">
+                    <a href="#" onclick="notifyForUpdate(true); return false;" type="button" class="btn btn-default">Check Now</a>
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="deleteRegistryModal" tabindex="-1" role="dialog" aria-labelledby="deleteRegistryModalLabel">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="deleteRegistryModalLabel">Delete <span id="deleteRegistryServerName"></span>?</h4>
+                </div>
+                <div class="modal-body">
+                    You are about to delete, from your personal list of netdata servers, the following server:
+                    <p style="text-align: center; padding-top: 10px; padding-bottom: 10px; line-height: 2;">
+                    <b><span id="deleteRegistryServerName2"></span></b>
+                    <br/>
+                    <b><span id="deleteRegistryServerURL"></span></b>
+                    </p>
+                    Are you sure you want to do this?
+                    <br/>
+                    <div style="padding: 10px;"></div>
+                    <small>Keep in mind, this server will be added back if and when you visit it again.</small>
+                    <br/>
+                    <div id="deleteRegistryResponse" style="display: block; width: 100%; text-align: center; padding-top: 20px;"></div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-success" data-dismiss="modal">keep it</button>
+                    <a href="#" onclick="notifyForDeleteRegistry(true); return false;" type="button" class="btn btn-danger">delete it</a>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="switchRegistryModal" tabindex="-1" role="dialog" aria-labelledby="switchRegistryModalLabel">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="switchRegistryModalLabel">Switch Netdata Registry Identity</h4>
+                </div>
+                <div class="modal-body">
+                    You can copy and paste the following ID to all your browsers (e.g. work and home).
+                    <br/>
+                    All the browsers with the same ID will identify <b>you</b>, so please don't share this with others.
+                    <p style="text-align: center; padding-top: 10px; padding-bottom: 10px; line-height: 2;">
+                    <form action="#">
+                    <input type="text" class="form-control" id="switchRegistryPersonGUID" placeholder="your personal ID" maxlength="36" autocomplete="off" style="text-align: center; font-size: 1.4em;">
+                    </form>
+                    </p>
+                    Either copy this ID and paste it to another browser, or paste here the ID you have taken from another browser.
+                    <p style="padding-top: 10px;"><small>
+                        Keep in mind that:
+                        <ul>
+                            <li>when you switch ID, your previous ID will be lost forever - this is irreversible.</li>
+                            <li>both IDs (your old and the new) must list this netdata at their personal lists.</li>
+                            <li>both IDs have to be known by the registry: <b><span id="switchRegistryURL"></span></b>.</li>
+                            <li>to get a new ID, just clear your browser cookies.</li>
+                        </ul>
+                    </small></p>
+                    <div id="switchRegistryResponse" style="display: block; width: 100%; text-align: center; padding-top: 20px;"></div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-success" data-dismiss="modal">cancel</button>
+                    <a href="#" onclick="notifyForSwitchRegistry(true); return false;" type="button" class="btn btn-danger">impersonate</a>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="gotoServerModal" tabindex="-1" role="dialog" aria-labelledby="gotoServerModalLabel">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="gotoServerModalLabel"><span id="gotoServerName"></span></h4>
+                </div>
+                <div class="modal-body">
+                    Checking known URLs for this server...
+                    <div  style="padding-top: 20px;">
+                        <table id="gotoServerList">
+                        </table>
+                    </div>
+                    <p style="padding-top: 10px;"><small>
+                        Checks may fail if you are viewing an HTTPS page and the server to be checked is HTTP only.
+                    </small></p>
+                    <div id="gotoServerResponse" style="display: block; width: 100%; text-align: center; padding-top: 20px;"></div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
 
 <script type="text/javascript">
 var this_is_demo = null;
 function isdemo() {
-       if(this_is_demo !== null) return this_is_demo;
-       this_is_demo = false;
-
-       try {
-               if(typeof document.location.hostname === 'string') {
-                       if(document.location.hostname.endsWith('.my-netdata.io') ||
-                                       document.location.hostname.endsWith('.mynetdata.io') ||
-                                       document.location.hostname.endsWith('.netdata.rocks') ||
-                                       document.location.hostname.endsWith('.firehol.org') ||
-                                       document.location.hostname.endsWith('.netdata.online'))
-                                       this_is_demo = true;
-               }
-       }
-       catch(error) {
-               ;
-       }
-
-       return this_is_demo;
+    if(this_is_demo !== null) return this_is_demo;
+    this_is_demo = false;
+
+    try {
+        if(typeof document.location.hostname === 'string') {
+            if(document.location.hostname.endsWith('.my-netdata.io') ||
+                    document.location.hostname.endsWith('.mynetdata.io') ||
+                    document.location.hostname.endsWith('.netdata.rocks') ||
+                    document.location.hostname.endsWith('.firehol.org') ||
+                    document.location.hostname.endsWith('.netdata.online'))
+                    this_is_demo = true;
+        }
+    }
+    catch(error) {
+        ;
+    }
+
+    return this_is_demo;
 }
 
 if(isdemo()) {
-       document.getElementById('masthead').style.display = 'block';
+    document.getElementById('masthead').style.display = 'block';
 }
 
 function netdataURL(url) {
-       if(typeof url === 'undefined')
-               url = document.location.toString();
+    if(typeof url === 'undefined')
+        url = document.location.toString();
 
-       if(url.indexOf('#') !== -1)
-               url = url.substring(0, url.indexOf('#'));
+    if(url.indexOf('#') !== -1)
+        url = url.substring(0, url.indexOf('#'));
 
-       var hash = netdataHash();
+    var hash = netdataHash();
 
-       // console.log('netdataURL: ' + url + hash);
+    // console.log('netdataURL: ' + url + hash);
 
-       return url + hash;
+    return url + hash;
 }
 
 function netdataReload(url) {
-       var t = netdataURL(url);
-       // console.log('netdataReload: ' + t);
-       document.location = t;
+    var t = netdataURL(url);
+    // console.log('netdataReload: ' + t);
+    document.location = t;
 
-       // since we play with hash
-       // this is needed to reload the page
-       location.reload();
+    // since we play with hash
+    // this is needed to reload the page
+    location.reload();
 }
 
 var gotoServerValidateRemaining = 0;
 var gotoServerMiddleClick = false;
 var gotoServerStop = false;
 function gotoServerValidateUrl(id, guid, url) {
-       var penaldy = 0;
-       if(document.location.toString().startsWith('http://') && url.toString().startsWith('https://'))
-                       // we penalize https only if the current url is http
-                       // to allow the user walk through all its servers.
-                       penaldy = 500;
-
-       var finalURL = netdataURL(url);
-
-       setTimeout(function() {
-               document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + finalURL + '" target="_blank">' + url + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
-
-               NETDATA.registry.hello(url, function(data) {
-                       if (data) {
-                               // console.log('OK ' + id + ' URL: ' + url);
-                               document.getElementById(guid + '-' + id + '-status').innerHTML = "OK";
-
-                               if(!gotoServerStop) {
-                                       gotoServerStop = true;
-
-                                       if(gotoServerMiddleClick) {
-                                               window.open(finalURL, '_blank');
-                                               gotoServerMiddleClick = false;
-                                               document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + finalURL + '">' + url + '</a></b><br/>(check your pop-up blocker if it fails)';
-                                       }
-                                       else
-                                               document.location = finalURL;
-                               }
-                       }
-                       else {
-                               document.getElementById(guid + '-' + id + '-status').innerHTML = "failed!";
-                               gotoServerValidateRemaining--;
-                               if(gotoServerValidateRemaining <= 0) {
-                                       gotoServerMiddleClick = false;
-                                       document.getElementById('gotoServerResponse').innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>';
-                               }
-                       }
-               });
-       }, (id * 50) + penaldy);
+    var penaldy = 0;
+    if(document.location.toString().startsWith('http://') && url.toString().startsWith('https://'))
+            // we penalize https only if the current url is http
+            // to allow the user walk through all its servers.
+            penaldy = 500;
+
+    var finalURL = netdataURL(url);
+
+    setTimeout(function() {
+        document.getElementById('gotoServerList').innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + finalURL + '" target="_blank">' + url + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>';
+
+        NETDATA.registry.hello(url, function(data) {
+            if (data) {
+                // console.log('OK ' + id + ' URL: ' + url);
+                document.getElementById(guid + '-' + id + '-status').innerHTML = "OK";
+
+                if(!gotoServerStop) {
+                    gotoServerStop = true;
+
+                    if(gotoServerMiddleClick) {
+                        window.open(finalURL, '_blank');
+                        gotoServerMiddleClick = false;
+                        document.getElementById('gotoServerResponse').innerHTML = '<b>Opening new window to ' + NETDATA.registry.machines[guid].name + '<br/><a href="' + finalURL + '">' + url + '</a></b><br/>(check your pop-up blocker if it fails)';
+                    }
+                    else
+                        document.location = finalURL;
+                }
+            }
+            else {
+                document.getElementById(guid + '-' + id + '-status').innerHTML = "failed!";
+                gotoServerValidateRemaining--;
+                if(gotoServerValidateRemaining <= 0) {
+                    gotoServerMiddleClick = false;
+                    document.getElementById('gotoServerResponse').innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>';
+                }
+            }
+        });
+    }, (id * 50) + penaldy);
 }
 
 function gotoServerModalHandler(guid) {
-       // console.log('goto server: ' + guid);
+    // console.log('goto server: ' + guid);
 
-       gotoServerStop = false;
-       var len = NETDATA.registry.machines[guid].alternate_urls.length;
+    gotoServerStop = false;
+    var len = NETDATA.registry.machines[guid].alternate_urls.length;
 
-       document.getElementById('gotoServerResponse').innerHTML = '';
-       document.getElementById('gotoServerList').innerHTML = '';
-       document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name;
-       $('#gotoServerModal').modal('show');
+    document.getElementById('gotoServerResponse').innerHTML = '';
+    document.getElementById('gotoServerList').innerHTML = '';
+    document.getElementById('gotoServerName').innerHTML = NETDATA.registry.machines[guid].name;
+    $('#gotoServerModal').modal('show');
 
-       gotoServerValidateRemaining = len;
-       while(len--)
-               gotoServerValidateUrl(len, guid, NETDATA.registry.machines[guid].alternate_urls[len]);
+    gotoServerValidateRemaining = len;
+    while(len--)
+        gotoServerValidateUrl(len, guid, NETDATA.registry.machines[guid].alternate_urls[len]);
 
-       return false;
+    return false;
 }
 
 function gotoServerInit() {
-       $(".registry_link").on('click', function(e) {
-               if(e.which === 2) {
-                       e.preventDefault();
-                       gotoServerMiddleClick = true;
-               }
-               else {
-                       gotoServerMiddleClick = false;
-               }
-
-               return true;
-       });
+    $(".registry_link").on('click', function(e) {
+        if(e.which === 2) {
+            e.preventDefault();
+            gotoServerMiddleClick = true;
+        }
+        else {
+            gotoServerMiddleClick = false;
+        }
+
+        return true;
+    });
 }
 
 function switchRegistryModalHandler() {
-       document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid;
-       document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server;
-       document.getElementById('switchRegistryResponse').innerHTML = '';
-       $('#switchRegistryModal').modal('show');
+    document.getElementById('switchRegistryPersonGUID').value = NETDATA.registry.person_guid;
+    document.getElementById('switchRegistryURL').innerHTML = NETDATA.registry.server;
+    document.getElementById('switchRegistryResponse').innerHTML = '';
+    $('#switchRegistryModal').modal('show');
 }
 
 function notifyForSwitchRegistry() {
-       var n = document.getElementById('switchRegistryPersonGUID').value;
-
-       if(n !== '' && n.length === 36) {
-               NETDATA.registry.switch(n, function(result) {
-                       if(result !== null) {
-                               $('#switchRegistryModal').modal('hide');
-                               NETDATA.registry.init();
-                       }
-                       else {
-                               document.getElementById('switchRegistryResponse').innerHTML = "<b>Sorry! The registry rejected your request.</b>";
-                       }
-               });
-       }
-       else
-               document.getElementById('switchRegistryResponse').innerHTML = "<b>The ID you have entered is not a GUID.</b>";
+    var n = document.getElementById('switchRegistryPersonGUID').value;
+
+    if(n !== '' && n.length === 36) {
+        NETDATA.registry.switch(n, function(result) {
+            if(result !== null) {
+                $('#switchRegistryModal').modal('hide');
+                NETDATA.registry.init();
+            }
+            else {
+                document.getElementById('switchRegistryResponse').innerHTML = "<b>Sorry! The registry rejected your request.</b>";
+            }
+        });
+    }
+    else
+        document.getElementById('switchRegistryResponse').innerHTML = "<b>The ID you have entered is not a GUID.</b>";
 }
 
 var deleteRegistryUrl = null;
 function deleteRegistryModalHandler(guid, name, url) {
-       deleteRegistryUrl = url;
-       document.getElementById('deleteRegistryServerName').innerHTML = name;
-       document.getElementById('deleteRegistryServerName2').innerHTML = name;
-       document.getElementById('deleteRegistryServerURL').innerHTML = url;
-       document.getElementById('deleteRegistryResponse').innerHTML = '';
-       $('#deleteRegistryModal').modal('show');
+    deleteRegistryUrl = url;
+    document.getElementById('deleteRegistryServerName').innerHTML = name;
+    document.getElementById('deleteRegistryServerName2').innerHTML = name;
+    document.getElementById('deleteRegistryServerURL').innerHTML = url;
+    document.getElementById('deleteRegistryResponse').innerHTML = '';
+    $('#deleteRegistryModal').modal('show');
 }
 
 function notifyForDeleteRegistry() {
-       if(deleteRegistryUrl) {
-               NETDATA.registry.delete(deleteRegistryUrl, function(result) {
-                       if(result !== null) {
-                               deleteRegistryUrl = null;
-                               $('#deleteRegistryModal').modal('hide');
-                               NETDATA.registry.init();
-                       }
-                       else {
-                               document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>";
-                       }
-               });
-       }
+    if(deleteRegistryUrl) {
+        NETDATA.registry.delete(deleteRegistryUrl, function(result) {
+            if(result !== null) {
+                deleteRegistryUrl = null;
+                $('#deleteRegistryModal').modal('hide');
+                NETDATA.registry.init();
+            }
+            else {
+                document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>";
+            }
+        });
+    }
 }
 
 var options = {
-       sparklines_registry: {},
-       submenu_names: {},
-       data: null,
-       hostname: 'netdata_server', // will be overwritten by the netdata server
-       categories: new Array(),
-       categories_idx: {},
-       families: new Array(),
-       families_idx: {},
-
-       chartsPerRow: 0,
-       chartsMinWidth: 1450,
-       chartsHeight: 180,
-       sparklinesHeight: 60,
+    sparklines_registry: {},
+    submenu_names: {},
+    data: null,
+    hostname: 'netdata_server', // will be overwritten by the netdata server
+    categories: new Array(),
+    categories_idx: {},
+    families: new Array(),
+    families_idx: {},
+
+    chartsPerRow: 0,
+    chartsMinWidth: 1450,
+    chartsHeight: 180,
+    sparklinesHeight: 60,
 };
 
 // generate a sparkline
 // used in the documentation
 function sparkline(chart, dimension, units) {
-       var key = chart + '.' + dimension;
+    var key = chart + '.' + dimension;
 
-       if(typeof units === 'undefined')
-               units = '';
+    if(typeof units === 'undefined')
+        units = '';
 
-       if(typeof options.sparklines_registry[key] === 'undefined')
-               options.sparklines_registry[key] = { count: 1 };
-       else
-               options.sparklines_registry[key].count++;
+    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;
+    key = key + '.' + options.sparklines_registry[key].count;
 
-       var h = '<div data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')';
+    var h = '<div data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')';
 
-       return h;
+    return h;
 }
 
 function chartsPerRow(total) {
-       if(options.chartsPerRow === 0) {
-               width = Math.floor(total / options.chartsMinWidth);
-               if(width === 0) width = 1;
-               return width;
-       }
-       else return options.chartsPerRow;
+    if(options.chartsPerRow === 0) {
+        width = Math.floor(total / options.chartsMinWidth);
+        if(width === 0) width = 1;
+        return width;
+    }
+    else return options.chartsPerRow;
 }
 
 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;
+    if(a.priority < b.priority) return -1;
+    if(a.priority > b.priority) return 1;
+    if(a.name < b.name) return -1;
+    return 1;
 }
 
 function sortObjectByPriority(object) {
-       var idx = {};
-       var sorted = new Array();
-
-       for(var i in object) {
-               if(typeof idx[i] === 'undefined') {
-                       idx[i] = object[i];
-                       sorted.push(i);
-               }
-       }
-
-       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 sorted;
+    var idx = {};
+    var sorted = new Array();
+
+    for(var i in object) {
+        if(typeof idx[i] === 'undefined') {
+            idx[i] = object[i];
+            sorted.push(i);
+        }
+    }
+
+    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 sorted;
 }
 
 
@@ -1358,200 +1358,200 @@ function sortObjectByPriority(object) {
 // scroll to a section, without changing the browser history
 
 function scrollToId(hash) {
-       if(hash && hash != '') {
-               var offset = $('#' + hash).offset();
-               if(typeof offset !== 'undefined')
-                       $('html, body').animate({ scrollTop: offset.top }, 0);
-       }
-
-       // we must return false to prevent the default action
-       return false;
+    if(hash && hash != '') {
+        var offset = $('#' + hash).offset();
+        if(typeof offset !== 'undefined')
+            $('html, body').animate({ scrollTop: offset.top }, 0);
+    }
+
+    // we must return false to prevent the default action
+    return false;
 }
 
 // ----------------------------------------------------------------------------
 
 function gaugeChart(title, width, dimensions, colors) {
-       if(typeof colors === 'undefined')
-               colors = '';
-
-       if(typeof dimensions === 'undefined')
-               dimensions = '';
-
-       return '<div data-netdata="CHART_UNIQUE_ID"'
-                                                       + ' 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"></div>';
+    if(typeof colors === 'undefined')
+        colors = '';
+
+    if(typeof dimensions === 'undefined')
+        dimensions = '';
+
+    return '<div data-netdata="CHART_UNIQUE_ID"'
+                            + ' 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"></div>';
 }
 
 // ----------------------------------------------------------------------------
 
 var menuData = {
-       'system': {
-               title: 'System Overview',
-               info: 'Overview of the key system metrics.'
-       },
-
-       'ap': {
-               title: 'Access Points',
-               info: undefined
-       },
-
-       'tc': {
-               title: 'Quality of Service',
-               info: 'Netdata collects and visualizes tc class utilization using its <a href="https://github.com/firehol/netdata/blob/master/plugins.d/tc-qos-helper.sh" target="_blank">tc-helper plugin</a>. If you also use <a href="http://firehol.org/#fireqos" target="_blank">FireQOS</a> 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).'
-       },
-
-       'net': {
-               title: 'Network Interfaces',
-               info: 'Per network interface statistics collected from <code>/proc/net/dev</code>.'
-       },
-
-       'ipv4': {
-               title: 'IPv4 Networking',
-               info: undefined
-       },
-
-       'ipv6': {
-               title: 'IPv6 Networking',
-               info: undefined
-       },
-
-       'ipvs': {
-               title: 'IP Virtual Server',
-               info: undefined
-       },
-
-       'netfilter': {
-               title: 'Firewall (netfilter)',
-               info: undefined
-       },
-
-       'cpu': {
-               title: 'CPUs',
-               info: undefined
-       },
-
-       'mem': {
-               title: 'Memory',
-               info: undefined
-       },
-
-       'disk': {
-               title: 'Disks',
-               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 <code>iostat -x</code>. 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.'
-       },
-
-       'sensors': {
-               title: 'Sensors',
-               info: undefined
-       },
-
-       'nfsd': {
-               title: 'File Server (nfsd)',
-               info: undefined
-       },
-
-       'apps': {
-               title: 'Applications',
-               info: 'Per application statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics for applications of interest, defined in <code>/etc/netdata/apps_groups.conf</code> (the default is <a href="https://github.com/firehol/netdata/blob/master/conf.d/apps_groups.conf" target="_blank">here</a>). The plugin internally builds a process tree (much like <code>ps fax</code> 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 <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> 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',
-               info: 'Per user statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> 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',
-               info: 'Per user group statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user group. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> 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',
-               info: undefined
-       },
-
-       'example': {
-               title: 'Example Charts',
-               info: undefined
-       },
-
-       'cgroup': {
-               title: 'Container',
-               info: undefined
-       },
-
-       'memcached': {
-               title: 'memcached',
-               info: undefined
-       },
-
-       'mysql': {
-               title: 'MySQL',
-               info: undefined
-       },
-
-       'redis': {
-               title: 'Redis',
-               info: undefined
-       },
-
-       'ipfs': {
-               title: 'IPFS',
-               info: undefined
-       },
-
-       'phpfpm': {
-               title: 'PHP-FPM',
-               info: undefined,
-       },
-
-       'nginx': {
-               title: 'nginx',
-               info: undefined,
-       },
-
-       'apache': {
-               title: 'Apache',
-               info: undefined,
-       },
-
-       'named': {
-               title: 'named',
-               info: undefined
-       }
+    'system': {
+        title: 'System Overview',
+        info: 'Overview of the key system metrics.'
+    },
+
+    'ap': {
+        title: 'Access Points',
+        info: undefined
+    },
+
+    'tc': {
+        title: 'Quality of Service',
+        info: 'Netdata collects and visualizes tc class utilization using its <a href="https://github.com/firehol/netdata/blob/master/plugins.d/tc-qos-helper.sh" target="_blank">tc-helper plugin</a>. If you also use <a href="http://firehol.org/#fireqos" target="_blank">FireQOS</a> 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).'
+    },
+
+    'net': {
+        title: 'Network Interfaces',
+        info: 'Per network interface statistics collected from <code>/proc/net/dev</code>.'
+    },
+
+    'ipv4': {
+        title: 'IPv4 Networking',
+        info: undefined
+    },
+
+    'ipv6': {
+        title: 'IPv6 Networking',
+        info: undefined
+    },
+
+    'ipvs': {
+        title: 'IP Virtual Server',
+        info: undefined
+    },
+
+    'netfilter': {
+        title: 'Firewall (netfilter)',
+        info: undefined
+    },
+
+    'cpu': {
+        title: 'CPUs',
+        info: undefined
+    },
+
+    'mem': {
+        title: 'Memory',
+        info: undefined
+    },
+
+    'disk': {
+        title: 'Disks',
+        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 <code>iostat -x</code>. 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.'
+    },
+
+    'sensors': {
+        title: 'Sensors',
+        info: undefined
+    },
+
+    'nfsd': {
+        title: 'File Server (nfsd)',
+        info: undefined
+    },
+
+    'apps': {
+        title: 'Applications',
+        info: 'Per application statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics for applications of interest, defined in <code>/etc/netdata/apps_groups.conf</code> (the default is <a href="https://github.com/firehol/netdata/blob/master/conf.d/apps_groups.conf" target="_blank">here</a>). The plugin internally builds a process tree (much like <code>ps fax</code> 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 <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> 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',
+        info: 'Per user statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> 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',
+        info: 'Per user group statistics are collected using netdata\'s <code>apps.plugin</code>. This plugin walks through the entire <code>/proc</code> filesystem and aggregates statistics per user group. The reported values are compatible with <code>top</code>, although the netdata plugin counts also the resources of exited children (unlike <code>top</code> 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',
+        info: undefined
+    },
+
+    'example': {
+        title: 'Example Charts',
+        info: undefined
+    },
+
+    'cgroup': {
+        title: 'Container',
+        info: undefined
+    },
+
+    'memcached': {
+        title: 'memcached',
+        info: undefined
+    },
+
+    'mysql': {
+        title: 'MySQL',
+        info: undefined
+    },
+
+    'redis': {
+        title: 'Redis',
+        info: undefined
+    },
+
+    'ipfs': {
+        title: 'IPFS',
+        info: undefined
+    },
+
+    'phpfpm': {
+        title: 'PHP-FPM',
+        info: undefined,
+    },
+
+    'nginx': {
+        title: 'nginx',
+        info: undefined,
+    },
+
+    'apache': {
+        title: 'Apache',
+        info: undefined,
+    },
+
+    'named': {
+        title: 'named',
+        info: undefined
+    }
 };
 
 var submenuData = {
-       'mem.ksm': {
-               title: 'Memory Deduper',
-               info: 'Kernel Same-page Merging (KSM) performance monitoring, read from several files in <code>/sys/kernel/mm/ksm/</code>. 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.'
-       },
-
-       'netfilter.conntrack': {
-               title: 'Connection Tracker',
-               info: 'Netfilter Connection Tracker performance monitoring, read from <code>/proc/net/stat/nf_conntrack</code>. 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': {
-               title: 'Bandwidth Accounting',
-               info: 'The following information is read using the <code>nfacct.plugin</code>.'
-       },
-
-       'netfilter.synproxy': {
-               title: 'DDoS Protection',
-               info: 'DDoS Protection performance monitoring read from <code>/proc/net/stat/synproxy</code>. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> 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.'
-       }
+    'mem.ksm': {
+        title: 'Memory Deduper',
+        info: 'Kernel Same-page Merging (KSM) performance monitoring, read from several files in <code>/sys/kernel/mm/ksm/</code>. 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.'
+    },
+
+    'netfilter.conntrack': {
+        title: 'Connection Tracker',
+        info: 'Netfilter Connection Tracker performance monitoring, read from <code>/proc/net/stat/nf_conntrack</code>. 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': {
+        title: 'Bandwidth Accounting',
+        info: 'The following information is read using the <code>nfacct.plugin</code>.'
+    },
+
+    'netfilter.synproxy': {
+        title: 'DDoS Protection',
+        info: 'DDoS Protection performance monitoring read from <code>/proc/net/stat/synproxy</code>. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> 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.'
+    }
 };
 
 //
@@ -1565,433 +1565,440 @@ var submenuData = {
 // height: the ratio of the chart height relative to the default
 //
 var chartData = {
-       '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 <a href="#cpu">CPUs</a> section and per application usage at the <a href="#apps">Applications Monitoring</a> section.<br/>Keep an eye on <b>iowait</b> ' + sparkline('system.cpu', 'iowait', '%') + '. If it is constantly high, your disks are a bottleneck and they slow your system down.<br/>Another important metric worth monitoring, is <b>softirq</b> ' + sparkline('system.cpu', 'softirq', '%') + '. A constantly high percentage of softirq may indicate network drivers issues.'
-       },
-
-       'system.load': {
-               info: 'Current system load read from <code>/proc/loadavg</code>.',
-               height: 0.7
-       },
-
-       'system.io': {
-               info: 'Total Disk I/O, for all disks, read from <code>/proc/vmstat</code>. You can get detailed information about each disk at the <a href="#disk">Disks</a> section and per application Disk usage at the <a href="#apps">Applications Monitoring</a> section.'
-       },
-
-       'system.swapio': {
-               info: 'Total Swap I/O, read from <code>/proc/vmstat</code>. (netdata measures both <code>in</code> and <code>out</code>. 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 <code>/proc/vmstat</code>. <b>Major page faults</b> indicates that the system is using its swap. You can find which applications use the swap at the <a href="#apps">Applications Monitoring</a> section.'
-       },
-
-       'system.entropy': {
-               colors: '#CC22AA',
-               info: '<a href="https://en.wikipedia.org/wiki/Entropy_(computing)" target="_blank">Entropy</a>, read from <code>/proc/sys/kernel/random/entropy_avail</code>, is like a pool of random numbers that are mainly used in cryptography. It is advised that the pool remains always <a href="https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/" target="_blank">above 200</a>. 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 <a href="http://www.issihosts.com/haveged/" target="_blank">haveged</a>, to keep the pool in healthy levels.'
-       },
-
-       'system.forks': {
-               colors: '#5555DD',
-               info: 'The number of new processes created per second, read from <code>/proc/stat</code>.'
-       },
-
-       'system.intr': {
-               colors: '#DD5555',
-               info: 'Total number of CPU interrupts, read from <code>/proc/stat</code>. Check <code>system.interrupts</code> that gives more detail about each interrupt and also the <a href="#cpu">CPUs</a> section where interrupts are analyzed per CPU core.'
-       },
-
-       'system.interrupts': {
-               info: 'CPU interrupts in detail, read from <code>/proc/interrupts</code>. At the <a href="#cpu">CPUs</a> section, interrupts are analyzed per CPU core.'
-       },
-
-       'system.softirqs': {
-               info: 'CPU softirqs in detail, read from <code>/proc/softirqs</code>. At the <a href="#cpu">CPUs</a> section, softirqs are analyzed per CPU core.'
-       },
-
-       'system.processes': {
-               info: 'System processes, read from <code>/proc/stat</code>. <b>Blocked</b> are processes that are willing to execute but they cannot, e.g. because they wait for disk activity.'
-       },
-
-       'system.active_processes': {
-               info: 'All system active processes, read from <code>/proc/loadavg</code>.'
-       },
-
-       'system.ctxt': {
-               info: '<a href="https://en.wikipedia.org/wiki/Context_switch" target="_blank">Context Switches</a>, read from <code>/proc/stat</code>, 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': {
-               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 <b>idle jitter</b>. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).'
-       },
-
-       'system.ipv4': {
-               info: 'Total IPv4 Traffic, read from <code>/proc/net/netstat</code>.'
-       },
-
-       'system.ipv6': {
-               info: 'Total IPv6 Traffic, read from <code>/proc/net/snmp6</code>.'
-       },
-
-       'system.ram': {
-               info: 'System memory, read from <code>/proc/meminfo</code>.'
-       },
-
-       'system.swap': {
-               info: 'System swap memory, read from <code>/proc/meminfo</code>.'
-       },
-
-       // ------------------------------------------------------------------------
-       // MEMORY
-
-       'mem.ksm_savings': {
-               heads: [
-                       gaugeChart('Saved', '12%', 'savings', '#0099CC')
-               ]
-       },
-
-       'mem.ksm_ratios': {
-               heads: [
-                       function(id) {
-                               return  '<div data-netdata="' + id + '"'
-                                       + ' data-gauge-max-value="100"'
-                                       + ' data-chart-library="gauge"'
-                                       + ' data-title="Savings"'
-                                       + ' data-units="percentage %"'
-                                       + ' data-gauge-adjust="width"'
-                                       + ' data-width="12%"'
-                                       + ' data-before="0"'
-                                       + ' data-after="-CHART_DURATION"'
-                                       + ' data-points="CHART_DURATION"'
-                                       + ' role="application"></div>';
-                       }
-               ]
-       },
-
-       'mem.committed': {
-               colors: NETDATA.colors[3]
-       },
-
-       // ------------------------------------------------------------------------
-       // APPS
-
-       'apps.cpu': {
-               height: 2.0
-       },
-
-       'apps.preads': {
-               height: 2.0
-       },
-
-       'apps.pwrites': {
-               height: 2.0
-       },
-
-       // ------------------------------------------------------------------------
-       // USERS
-
-       'users.cpu': {
-               height: 2.0
-       },
-
-       'users.preads': {
-               height: 2.0
-       },
-
-       'users.pwrites': {
-               height: 2.0
-       },
-
-       // ------------------------------------------------------------------------
-       // GROUPS
-
-       'groups.cpu': {
-               height: 2.0
-       },
-
-       'groups.preads': {
-               height: 2.0
-       },
-
-       'groups.pwrites': {
-               height: 2.0
-       },
-
-       // ------------------------------------------------------------------------
-       // NETWORK QoS
-
-       'tc.qos': {
-               heads: [
-                       function(id) {
-                               if(id.match(/.*-ifb$/))
-                                       return gaugeChart('Inbound', '12%', '', '#5555AA');
-                               else
-                                       return gaugeChart('Outbound', '12%', '', '#AA9900');
-                       }
-               ]
-       },
-
-       // ------------------------------------------------------------------------
-       // NETWORK INTERFACES
-
-       'net.net': {
-               heads: [
-                       gaugeChart('Received', '12%', 'received'),
-                       gaugeChart('Sent', '12%', 'sent')
-               ]
-       },
-
-       // ------------------------------------------------------------------------
-       // NETFILTER
-
-       'netfilter.sockets': {
-               colors: '#88AA00',
-               heads: [
-                       gaugeChart('Active Connections', '12%', '', '#88AA00')
-               ]
-       },
-
-       'netfilter.new': {
-               heads: [
-                       gaugeChart('New Connections', '12%', 'new', '#5555AA')
-               ]
-       },
-
-       // ------------------------------------------------------------------------
-       // DISKS
-
-       'disk.util': {
-               colors: '#FF5588',
-               heads: [
-                       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.'
-       },
-
-       '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.'
-       },
-
-       'disk.io': {
-               heads: [
-                       gaugeChart('Read', '12%', 'reads'),
-                       gaugeChart('Write', '12%', 'writes')
-               ],
-               info: 'Amount of data transferred to and from disk.'
-       },
-
-       '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).'
-       },
-
-       'disk.qops': {
-               info: 'I/O operations currently in progress. This metric is a snapshot - it is not an average over the last interval.'
-       },
-
-       'disk.iotime': {
-               height: 0.5,
-               info: 'The sum of the duration of all completed I/O operations. This number can exceed the interval if the disk is able to execute I/O operations in parallel.'
-       },
-       '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.'
-       },
-       'disk.svctm': {
-               height: 0.5,
-               info: 'The average service time for completed I/O operations. This metric is calculated using the total busy time of the disk and the number of completed operations. If the disk is able to execute multiple parallel operations the reporting average service time will be misleading.'
-       },
-       'disk.avgsz': {
-               height: 0.5,
-               info: 'The average I/O operation size.'
-       },
-       'disk.await': {
-               height: 0.5,
-               info: 'The average time for I/O requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.'
-       },
-
-       'disk.space': {
-               info: 'Disk space utilization. reserved for root is automatically reserved by the system to prevent the root user from getting out of space.'
-       },
-       'disk.inodes': {
-               info: 'inodes (or index nodes) are filesystem objects (e.g. files and directories). On many types of file system implementations, the maximum number of inodes is fixed at filesystem creation, limiting the maximum number of files the filesystem can hold. It is possible for a device to run out of inodes. When this happens, new files cannot be created on the device, even though there may be free space available.'
-       },
-
-       'mysql.net': {
-               info: 'The amount of data sent to mysql clients (<strong>out</strong>) and received from mysql clients (<strong>in</strong>).'
-       },
-
-       // ------------------------------------------------------------------------
-       // MYSQL
-
-       'mysql.queries': {
-               info: 'The number of statements executed by the server.<ul>' +
-               '<li><strong>queries</strong> counts the statements executed within stored SQL programs.</li>' +
-               '<li><strong>questions</strong> counts the statements sent to the mysql server by mysql clients.</li>' +
-               '<li><strong>slow queries</strong> counts the number of statements that took more than <a href="http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_long_query_time" target="_blank">long_query_time</a> seconds to be executed.' +
-               ' For more information about slow queries check the mysql <a href="http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html" target="_blank">slow query log</a>.</li>' +
-               '</ul>'
-       },
-
-       'mysql.handlers': {
-               info: 'Usage of the internal handlers of mysql. This chart provides very good insights of what the mysql server is actually doing.' +
-               ' (if the chart is not showing all these dimensions it is because they are zero - set <strong>Which dimensions to show?</strong> to <strong>All</strong> from the dashboard settings, to render even the zero values)<ul>' +
-               '<li><strong>commit</strong>, the number of internal <a href="http://dev.mysql.com/doc/refman/5.7/en/commit.html" target="_blank">COMMIT</a> statements.</li>' +
-               '<li><strong>delete</strong>, the number of times that rows have been deleted from tables.</li>' +
-               '<li><strong>prepare</strong>, a counter for the prepare phase of two-phase commit operations.</li>' +
-               '<li><strong>read first</strong>, the number of times the first entry in an index was read. A high value suggests that the server is doing a lot of full index scans; e.g. <strong>SELECT col1 FROM foo</strong>, with col1 indexed.</li>' +
-               '<li><strong>read key</strong>, the number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.</li>' +
-               '<li><strong>read next</strong>, the number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.</li>' +
-               '<li><strong>read prev</strong>, the number of requests to read the previous row in key order. This read method is mainly used to optimize <strong>ORDER BY ... DESC</strong>.</li>' +
-               '<li><strong>read rnd</strong>, the number of requests to read a row based on a fixed position. A high value indicates you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.</li>' +
-               '<li><strong>read rnd next</strong>, the number of requests to read the next row in the data file. This value is high if you are doing a lot of table scans. Generally this suggests that your tables are not properly indexed or that your queries are not written to take advantage of the indexes you have.</li>' +
-               '<li><strong>rollback</strong>, the number of requests for a storage engine to perform a rollback operation.</li>' +
-               '<li><strong>savepoint</strong>, the number of requests for a storage engine to place a savepoint.</li>' +
-               '<li><strong>savepoint rollback</strong>, the number of requests for a storage engine to roll back to a savepoint.</li>' +
-               '<li><strong>update</strong>, the number of requests to update a row in a table.</li>' +
-               '<li><strong>write</strong>, the number of requests to insert a row in a table.</li>' +
-               '</ul>'
-       },
-
-       'mysql.table_locks': {
-               info: 'MySQL table locks counters: <ul>' +
-               '<li><strong>immediate</strong>, the number of times that a request for a table lock could be granted immediately.</li>' +
-               '<li><strong>waited</strong>, the number of times that a request for a table lock could not be granted immediately and a wait was needed. If this is high and you have performance problems, you should first optimize your queries, and then either split your table or tables or use replication.</li>' +
-               '</ul>'
-       },
-
-       // ------------------------------------------------------------------------
-       // APACHE
-
-       'apache.connections': {
-               colors: NETDATA.colors[4],
-               mainheads: [
-                       gaugeChart('Connections', '12%', '', NETDATA.colors[4])
-               ]
-       },
-
-       'apache.requests': {
-               colors: NETDATA.colors[0],
-               mainheads: [
-                       gaugeChart('Requests', '12%', '', NETDATA.colors[0])
-               ]
-       },
-
-       'apache.net': {
-               colors: NETDATA.colors[3],
-               mainheads: [
-                       gaugeChart('Bandwidth', '12%', '', NETDATA.colors[3])
-               ]
-       },
-
-       'apache.workers': {
-               mainheads: [
-                       function(id) {
-                               return  '<div data-netdata="' + id + '"'
-                                       + ' data-dimensions="busy"'
-                                       + ' data-append-options="percentage"'
-                                       + ' data-gauge-max-value="100"'
-                                       + ' data-chart-library="gauge"'
-                                       + ' data-title="Workers Utilization"'
-                                       + ' data-units="percentage %"'
-                                       + ' data-gauge-adjust="width"'
-                                       + ' data-width="12%"'
-                                       + ' data-before="0"'
-                                       + ' data-after="-CHART_DURATION"'
-                                       + ' data-points="CHART_DURATION"'
-                                       + ' role="application"></div>';
-                       }
-               ]
-       },
-
-       'apache.bytesperreq': {
-               colors: NETDATA.colors[3],
-               height: 0.5
-       },
-
-       'apache.reqpersec': {
-               colors: NETDATA.colors[4],
-               height: 0.5
-       },
-
-       'apache.bytespersec': {
-               colors: NETDATA.colors[6],
-               height: 0.5
-       },
-
-
-       // ------------------------------------------------------------------------
-       // NGINX
-
-       'nginx.connections': {
-               colors: NETDATA.colors[4],
-               mainheads: [
-                       gaugeChart('Connections', '12%', '', NETDATA.colors[4])
-               ]
-       },
-
-       'nginx.requests': {
-               colors: NETDATA.colors[0],
-               mainheads: [
-                       gaugeChart('Requests', '12%', '', NETDATA.colors[0])
-               ]
-       }
+    '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 <a href="#cpu">CPUs</a> section and per application usage at the <a href="#apps">Applications Monitoring</a> section.<br/>Keep an eye on <b>iowait</b> ' + sparkline('system.cpu', 'iowait', '%') + '. If it is constantly high, your disks are a bottleneck and they slow your system down.<br/>Another important metric worth monitoring, is <b>softirq</b> ' + sparkline('system.cpu', 'softirq', '%') + '. A constantly high percentage of softirq may indicate network drivers issues.'
+    },
+
+    '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 <code>/proc/loadavg</code>. For more information check <a href="https://en.wikipedia.org/wiki/Load_(computing)" target="_blank">this wikipedia article</a>',
+        height: 0.7
+    },
+
+    'system.io': {
+        info: 'Total Disk I/O, for all disks, read from <code>/proc/vmstat</code>. You can get detailed information about each disk at the <a href="#disk">Disks</a> section and per application Disk usage at the <a href="#apps">Applications Monitoring</a> section.'
+    },
+
+    'system.swapio': {
+        info: 'Total Swap I/O, read from <code>/proc/vmstat</code>. (netdata measures both <code>in</code> and <code>out</code>. 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 <code>/proc/vmstat</code>. <b>Major page faults</b> indicates that the system is using its swap. You can find which applications use the swap at the <a href="#apps">Applications Monitoring</a> section.'
+    },
+
+    'system.entropy': {
+        colors: '#CC22AA',
+        info: '<a href="https://en.wikipedia.org/wiki/Entropy_(computing)" target="_blank">Entropy</a>, read from <code>/proc/sys/kernel/random/entropy_avail</code>, is like a pool of random numbers that are mainly used in cryptography. It is advised that the pool remains always <a href="https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/" target="_blank">above 200</a>. 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 <a href="http://www.issihosts.com/haveged/" target="_blank">haveged</a>, to keep the pool in healthy levels.'
+    },
+
+    'system.forks': {
+        colors: '#5555DD',
+        info: 'The number of new processes created per second, read from <code>/proc/stat</code>.'
+    },
+
+    'system.intr': {
+        colors: '#DD5555',
+        info: 'Total number of CPU interrupts, read from <code>/proc/stat</code>. Check <code>system.interrupts</code> that gives more detail about each interrupt and also the <a href="#cpu">CPUs</a> section where interrupts are analyzed per CPU core.'
+    },
+
+    'system.interrupts': {
+        info: 'CPU interrupts in detail, read from <code>/proc/interrupts</code>. At the <a href="#cpu">CPUs</a> section, interrupts are analyzed per CPU core.'
+    },
+
+    'system.softirqs': {
+        info: 'CPU softirqs in detail, read from <code>/proc/softirqs</code>. At the <a href="#cpu">CPUs</a> section, softirqs are analyzed per CPU core.'
+    },
+
+    'system.processes': {
+        info: 'System processes, read from <code>/proc/stat</code>. <b>Blocked</b> are processes that are willing to execute but they cannot, e.g. because they wait for disk activity.'
+    },
+
+    'system.active_processes': {
+        info: 'All system active processes, read from <code>/proc/loadavg</code>.'
+    },
+
+    'system.ctxt': {
+        info: '<a href="https://en.wikipedia.org/wiki/Context_switch" target="_blank">Context Switches</a>, read from <code>/proc/stat</code>, 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': {
+        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 <b>idle jitter</b>. This number is useful in real-time environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).'
+    },
+
+    'system.ipv4': {
+        info: 'Total IPv4 Traffic, read from <code>/proc/net/netstat</code>.'
+    },
+
+    'system.ipv6': {
+        info: 'Total IPv6 Traffic, read from <code>/proc/net/snmp6</code>.'
+    },
+
+    'system.ram': {
+        info: 'System memory, read from <code>/proc/meminfo</code>.'
+    },
+
+    'system.swap': {
+        info: 'System swap memory, read from <code>/proc/meminfo</code>.'
+    },
+
+    // ------------------------------------------------------------------------
+    // MEMORY
+
+    'mem.ksm_savings': {
+        heads: [
+            gaugeChart('Saved', '12%', 'savings', '#0099CC')
+        ]
+    },
+
+    'mem.ksm_ratios': {
+        heads: [
+            function(id) {
+                return  '<div data-netdata="' + id + '"'
+                    + ' data-gauge-max-value="100"'
+                    + ' data-chart-library="gauge"'
+                    + ' data-title="Savings"'
+                    + ' data-units="percentage %"'
+                    + ' data-gauge-adjust="width"'
+                    + ' data-width="12%"'
+                    + ' data-before="0"'
+                    + ' data-after="-CHART_DURATION"'
+                    + ' data-points="CHART_DURATION"'
+                    + ' role="application"></div>';
+            }
+        ]
+    },
+
+    'mem.committed': {
+        colors: NETDATA.colors[3]
+    },
+
+    // ------------------------------------------------------------------------
+    // APPS
+
+    'apps.cpu': {
+        height: 2.0
+    },
+
+    'apps.preads': {
+        height: 2.0
+    },
+
+    'apps.pwrites': {
+        height: 2.0
+    },
+
+    // ------------------------------------------------------------------------
+    // USERS
+
+    'users.cpu': {
+        height: 2.0
+    },
+
+    'users.preads': {
+        height: 2.0
+    },
+
+    'users.pwrites': {
+        height: 2.0
+    },
+
+    // ------------------------------------------------------------------------
+    // GROUPS
+
+    'groups.cpu': {
+        height: 2.0
+    },
+
+    'groups.preads': {
+        height: 2.0
+    },
+
+    'groups.pwrites': {
+        height: 2.0
+    },
+
+    // ------------------------------------------------------------------------
+    // NETWORK QoS
+
+    'tc.qos': {
+        heads: [
+            function(id) {
+                if(id.match(/.*-ifb$/))
+                    return gaugeChart('Inbound', '12%', '', '#5555AA');
+                else
+                    return gaugeChart('Outbound', '12%', '', '#AA9900');
+            }
+        ]
+    },
+
+    // ------------------------------------------------------------------------
+    // NETWORK INTERFACES
+
+    'net.net': {
+        heads: [
+            gaugeChart('Received', '12%', 'received'),
+            gaugeChart('Sent', '12%', 'sent')
+        ]
+    },
+
+    // ------------------------------------------------------------------------
+    // NETFILTER
+
+    'netfilter.sockets': {
+        colors: '#88AA00',
+        heads: [
+            gaugeChart('Active Connections', '12%', '', '#88AA00')
+        ]
+    },
+
+    'netfilter.new': {
+        heads: [
+            gaugeChart('New Connections', '12%', 'new', '#5555AA')
+        ]
+    },
+
+    // ------------------------------------------------------------------------
+    // DISKS
+
+    'disk.util': {
+        colors: '#FF5588',
+        heads: [
+            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.'
+    },
+
+    '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.'
+    },
+
+    'disk.io': {
+        heads: [
+            gaugeChart('Read', '12%', 'reads'),
+            gaugeChart('Write', '12%', 'writes')
+        ],
+        info: 'Amount of data transferred to and from disk.'
+    },
+
+    '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).'
+    },
+
+    'disk.qops': {
+        info: 'I/O operations currently in progress. This metric is a snapshot - it is not an average over the last interval.'
+    },
+
+    'disk.iotime': {
+        height: 0.5,
+        info: 'The sum of the duration of all completed I/O operations. This number can exceed the interval if the disk is able to execute I/O operations in parallel.'
+    },
+    '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.'
+    },
+    'disk.svctm': {
+        height: 0.5,
+        info: 'The average service time for completed I/O operations. This metric is calculated using the total busy time of the disk and the number of completed operations. If the disk is able to execute multiple parallel operations the reporting average service time will be misleading.'
+    },
+    'disk.avgsz': {
+        height: 0.5,
+        info: 'The average I/O operation size.'
+    },
+    'disk.await': {
+        height: 0.5,
+        info: 'The average time for I/O requests issued to the device to be served. This includes the time spent by the requests in queue and the time spent servicing them.'
+    },
+
+    'disk.space': {
+        info: 'Disk space utilization. reserved for root is automatically reserved by the system to prevent the root user from getting out of space.'
+    },
+    'disk.inodes': {
+        info: 'inodes (or index nodes) are filesystem objects (e.g. files and directories). On many types of file system implementations, the maximum number of inodes is fixed at filesystem creation, limiting the maximum number of files the filesystem can hold. It is possible for a device to run out of inodes. When this happens, new files cannot be created on the device, even though there may be free space available.'
+    },
+
+    'mysql.net': {
+        info: 'The amount of data sent to mysql clients (<strong>out</strong>) and received from mysql clients (<strong>in</strong>).'
+    },
+
+    // ------------------------------------------------------------------------
+    // MYSQL
+
+    'mysql.queries': {
+        info: 'The number of statements executed by the server.<ul>' +
+        '<li><strong>queries</strong> counts the statements executed within stored SQL programs.</li>' +
+        '<li><strong>questions</strong> counts the statements sent to the mysql server by mysql clients.</li>' +
+        '<li><strong>slow queries</strong> counts the number of statements that took more than <a href="http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_long_query_time" target="_blank">long_query_time</a> seconds to be executed.' +
+        ' For more information about slow queries check the mysql <a href="http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html" target="_blank">slow query log</a>.</li>' +
+        '</ul>'
+    },
+
+    'mysql.handlers': {
+        info: 'Usage of the internal handlers of mysql. This chart provides very good insights of what the mysql server is actually doing.' +
+        ' (if the chart is not showing all these dimensions it is because they are zero - set <strong>Which dimensions to show?</strong> to <strong>All</strong> from the dashboard settings, to render even the zero values)<ul>' +
+        '<li><strong>commit</strong>, the number of internal <a href="http://dev.mysql.com/doc/refman/5.7/en/commit.html" target="_blank">COMMIT</a> statements.</li>' +
+        '<li><strong>delete</strong>, the number of times that rows have been deleted from tables.</li>' +
+        '<li><strong>prepare</strong>, a counter for the prepare phase of two-phase commit operations.</li>' +
+        '<li><strong>read first</strong>, the number of times the first entry in an index was read. A high value suggests that the server is doing a lot of full index scans; e.g. <strong>SELECT col1 FROM foo</strong>, with col1 indexed.</li>' +
+        '<li><strong>read key</strong>, the number of requests to read a row based on a key. If this value is high, it is a good indication that your tables are properly indexed for your queries.</li>' +
+        '<li><strong>read next</strong>, the number of requests to read the next row in key order. This value is incremented if you are querying an index column with a range constraint or if you are doing an index scan.</li>' +
+        '<li><strong>read prev</strong>, the number of requests to read the previous row in key order. This read method is mainly used to optimize <strong>ORDER BY ... DESC</strong>.</li>' +
+        '<li><strong>read rnd</strong>, the number of requests to read a row based on a fixed position. A high value indicates you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.</li>' +
+        '<li><strong>read rnd next</strong>, the number of requests to read the next row in the data file. This value is high if you are doing a lot of table scans. Generally this suggests that your tables are not properly indexed or that your queries are not written to take advantage of the indexes you have.</li>' +
+        '<li><strong>rollback</strong>, the number of requests for a storage engine to perform a rollback operation.</li>' +
+        '<li><strong>savepoint</strong>, the number of requests for a storage engine to place a savepoint.</li>' +
+        '<li><strong>savepoint rollback</strong>, the number of requests for a storage engine to roll back to a savepoint.</li>' +
+        '<li><strong>update</strong>, the number of requests to update a row in a table.</li>' +
+        '<li><strong>write</strong>, the number of requests to insert a row in a table.</li>' +
+        '</ul>'
+    },
+
+    'mysql.table_locks': {
+        info: 'MySQL table locks counters: <ul>' +
+        '<li><strong>immediate</strong>, the number of times that a request for a table lock could be granted immediately.</li>' +
+        '<li><strong>waited</strong>, the number of times that a request for a table lock could not be granted immediately and a wait was needed. If this is high and you have performance problems, you should first optimize your queries, and then either split your table or tables or use replication.</li>' +
+        '</ul>'
+    },
+
+    // ------------------------------------------------------------------------
+    // APACHE
+
+    'apache.connections': {
+        colors: NETDATA.colors[4],
+        mainheads: [
+            gaugeChart('Connections', '12%', '', NETDATA.colors[4])
+        ]
+    },
+
+    'apache.requests': {
+        colors: NETDATA.colors[0],
+        mainheads: [
+            gaugeChart('Requests', '12%', '', NETDATA.colors[0])
+        ]
+    },
+
+    'apache.net': {
+        colors: NETDATA.colors[3],
+        mainheads: [
+            gaugeChart('Bandwidth', '12%', '', NETDATA.colors[3])
+        ]
+    },
+
+    'apache.workers': {
+        mainheads: [
+            function(id) {
+                return  '<div data-netdata="' + id + '"'
+                    + ' data-dimensions="busy"'
+                    + ' data-append-options="percentage"'
+                    + ' data-gauge-max-value="100"'
+                    + ' data-chart-library="gauge"'
+                    + ' data-title="Workers Utilization"'
+                    + ' data-units="percentage %"'
+                    + ' data-gauge-adjust="width"'
+                    + ' data-width="12%"'
+                    + ' data-before="0"'
+                    + ' data-after="-CHART_DURATION"'
+                    + ' data-points="CHART_DURATION"'
+                    + ' role="application"></div>';
+            }
+        ]
+    },
+
+    'apache.bytesperreq': {
+        colors: NETDATA.colors[3],
+        height: 0.5
+    },
+
+    'apache.reqpersec': {
+        colors: NETDATA.colors[4],
+        height: 0.5
+    },
+
+    'apache.bytespersec': {
+        colors: NETDATA.colors[6],
+        height: 0.5
+    },
+
+
+    // ------------------------------------------------------------------------
+    // NGINX
+
+    'nginx.connections': {
+        colors: NETDATA.colors[4],
+        mainheads: [
+            gaugeChart('Connections', '12%', '', NETDATA.colors[4])
+        ]
+    },
+
+    'nginx.requests': {
+        colors: NETDATA.colors[0],
+        mainheads: [
+            gaugeChart('Requests', '12%', '', NETDATA.colors[0])
+        ]
+    },
+
+    // ------------------------------------------------------------------------
+    // NETDATA
+
+    'netdata.response_time': {
+        info: 'The netdata API response time measures the time netdata needed to serve requests. This time includes everything, from the reception of the first byte of a request, to the dispatch of the last byte of its reply, therefore it includes all network latencies involved (i.e. a client over a slow network will influence these metrics).'
+    }
 };
 
 function anyAttribute(obj, attr, key, def) {
-       if(typeof obj[key] !== 'undefined') {
-               if(typeof obj[key][attr] !== 'undefined')
-                       return obj[key][attr];
-       }
-       return def;
+    if(typeof obj[key] !== 'undefined') {
+        if(typeof obj[key][attr] !== 'undefined')
+            return obj[key][attr];
+    }
+    return def;
 }
 
 function menuTitle(chart) {
-       if(typeof chart.menu_pattern !== 'undefined') {
-               return anyAttribute(menuData, 'title', chart.menu_pattern, chart.menu_pattern).toString()
-                               + ': ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString();
-       }
+    if(typeof chart.menu_pattern !== 'undefined') {
+        return anyAttribute(menuData, 'title', chart.menu_pattern, chart.menu_pattern).toString()
+                + ': ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString();
+    }
 
-       return anyAttribute(menuData, 'title', chart.menu, chart.menu);
+    return anyAttribute(menuData, 'title', chart.menu, chart.menu);
 }
 
 function menuInfo(menu) {
-       return anyAttribute(menuData, 'info', menu, null);
+    return anyAttribute(menuData, 'info', menu, null);
 }
 
 function menuHeight(menu, relative) {
-       return anyAttribute(menuData, 'height', menu, 1.0) * relative;
+    return anyAttribute(menuData, 'height', menu, 1.0) * relative;
 }
 
 function submenuTitle(menu, submenu) {
-       var key = menu + '.' + submenu;
-       return anyAttribute(submenuData, 'title', key, submenu);
+    var key = menu + '.' + submenu;
+    return anyAttribute(submenuData, 'title', key, submenu);
 }
 
 function submenuInfo(menu, submenu) {
-       var key = menu + '.' + submenu;
-       return anyAttribute(submenuData, 'info', key, null);
+    var key = menu + '.' + submenu;
+    return anyAttribute(submenuData, 'info', key, null);
 }
 
 function submenuHeight(menu, submenu, relative) {
-       var key = menu + '.' + submenu;
-       return anyAttribute(submenuData, 'height', key, 1.0) * relative;
+    var key = menu + '.' + submenu;
+    return anyAttribute(submenuData, 'height', key, 1.0) * relative;
 }
 
 
 function chartInfo(id) {
-       if(typeof chartData[id] !== 'undefined' && typeof chartData[id].info !== 'undefined')
-               return '<div class="chart-message netdata-chart-alignment" role="document">' + chartData[id].info + '</div>';
-       else
-               return '';
+    if(typeof chartData[id] !== 'undefined' && typeof chartData[id].info !== 'undefined')
+        return '<div class="chart-message netdata-chart-alignment" role="document">' + chartData[id].info + '</div>';
+    else
+        return '';
 }
 
 function chartHeight(id, def) {
-       if(typeof chartData[id] !== 'undefined' && typeof chartData[id].height !== 'undefined')
-               return def * chartData[id].height;
-       else
-               return def;
+    if(typeof chartData[id] !== 'undefined' && typeof chartData[id].height !== 'undefined')
+        return def * chartData[id].height;
+    else
+        return def;
 }
 
 
@@ -2000,794 +2007,794 @@ function chartHeight(id, def) {
 // enrich the data structure returned by netdata
 // to reflect our menu system and content
 function enrichChartData(chart) {
-       var tmp = chart.type.split('_')[0];
-
-       switch(tmp) {
-               case 'ap':
-               case 'net':
-               case 'disk':
-                       chart.menu = tmp;
-                       break;
-
-               case 'apache':
-               case 'cgroup':
-               case 'exim':
-               case 'memcached':
-               case 'mysql':
-               case 'named':
-               case 'nginx':
-               case 'phpfpm':
-               case 'postfix':
-               case 'redis':
-               case 'ipfs':
-               case 'squid':
-               case 'snmp':
-               case 'tomcat':
-                       chart.menu = chart.type;
-                       chart.menu_pattern = tmp;
-                       break;
-
-               case 'tc':
-                       chart.menu = tmp;
-
-                       // 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) {
-                               var n = chart.name.split('.')[1];
-                               if(n.endsWith('_in'))
-                                       options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in'));
-                               else if(n.endsWith('_out'))
-                                       options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out'));
-                               else if(n.startsWith('in_'))
-                                       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);
-                       }
-
-                       // increase the priority of IFB devices
-                       // to have inbound appear before outbound
-                       if(chart.id.match(/.*-ifb$/))
-                               chart.priority--;
-
-                       break;
-
-               default:
-                       chart.menu = chart.type;
-                       break;
-       }
-
-       chart.submenu = chart.family;
+    var tmp = chart.type.split('_')[0];
+
+    switch(tmp) {
+        case 'ap':
+        case 'net':
+        case 'disk':
+            chart.menu = tmp;
+            break;
+
+        case 'apache':
+        case 'cgroup':
+        case 'exim':
+        case 'memcached':
+        case 'mysql':
+        case 'named':
+        case 'nginx':
+        case 'phpfpm':
+        case 'postfix':
+        case 'redis':
+        case 'ipfs':
+        case 'squid':
+        case 'snmp':
+        case 'tomcat':
+            chart.menu = chart.type;
+            chart.menu_pattern = tmp;
+            break;
+
+        case 'tc':
+            chart.menu = tmp;
+
+            // 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) {
+                var n = chart.name.split('.')[1];
+                if(n.endsWith('_in'))
+                    options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in'));
+                else if(n.endsWith('_out'))
+                    options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out'));
+                else if(n.startsWith('in_'))
+                    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);
+            }
+
+            // increase the priority of IFB devices
+            // to have inbound appear before outbound
+            if(chart.id.match(/.*-ifb$/))
+                chart.priority--;
+
+            break;
+
+        default:
+            chart.menu = chart.type;
+            break;
+    }
+
+    chart.submenu = chart.family;
 }
 
 // ----------------------------------------------------------------------------
 
 function name2id(s) {
-       return s
-               .replace(/ /g, '_')
-               .replace(/\(/g, '_')
-               .replace(/\)/g, '_')
-               .replace(/\./g, '_')
-               .replace(/\//g, '_');
+    return s
+        .replace(/ /g, '_')
+        .replace(/\(/g, '_')
+        .replace(/\)/g, '_')
+        .replace(/\./g, '_')
+        .replace(/\//g, '_');
 }
 
 function headMain(charts, duration) {
-       var head = '';
-
-       if(typeof charts['system.swap'] !== 'undefined')
-               head += '<div style="margin-right: 10px;" data-netdata="system.swap"'
-               + ' data-dimensions="free"'
-               + ' data-append-options="percentage"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="Free Swap"'
-               + ' data-units="%"'
-               + ' data-easypiechart-max-value="100"'
-               + ' data-width="8%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' data-colors="#DD4400"'
-               + ' role="application"></div>';
-
-       if(typeof charts['system.io'] !== 'undefined') {
-               head += '<div style="margin-right: 10px;" data-netdata="system.io"'
-               + ' data-dimensions="in"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="Disk Read"'
-               + ' data-width="10%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' role="application"></div>';
-
-               head += '<div style="margin-right: 10px;" data-netdata="system.io"'
-               + ' data-dimensions="out"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="Disk Write"'
-               + ' data-width="10%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' role="application"></div>';
-       }
-
-       if(typeof charts['system.cpu'] !== 'undefined')
-               head += '<div data-netdata="system.cpu"'
-               + ' data-chart-library="gauge"'
-               + ' data-title="CPU"'
-               + ' data-units="%"'
-               + ' data-gauge-max-value="100"'
-               + ' data-width="18%"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' data-colors="' + NETDATA.colors[12] + '"'
-               + ' role="application"></div>';
-
-       if(typeof charts['system.ipv4'] !== 'undefined') {
-               head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"'
-               + ' data-dimensions="received"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="IPv4 Inbound"'
-               + ' data-width="10%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' role="application"></div>';
-
-               head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"'
-               + ' data-dimensions="sent"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="IPv4 Outbound"'
-               + ' data-width="10%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' role="application"></div>';
-       }
-       else if(typeof charts['system.ipv6'] !== 'undefined') {
-               head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"'
-               + ' data-dimensions="received"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="IPv6 Inbound"'
-               + ' data-units="kbps"'
-               + ' data-width="10%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' role="application"></div>';
-
-               head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"'
-               + ' data-dimensions="sent"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="IPv6 Outbound"'
-               + ' data-units="kbps"'
-               + ' data-width="10%"'
-               + ' data-before="0"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' role="application"></div>';
-       }
-
-       if(typeof charts['system.ram'] !== 'undefined')
-               head += '<div style="margin-right: 10px;" data-netdata="system.ram"'
-               + ' data-dimensions="cached|free"'
-               + ' data-append-options="percentage"'
-               + ' data-chart-library="easypiechart"'
-               + ' data-title="Available RAM"'
-               + ' data-units="%"'
-               + ' data-easypiechart-max-value="100"'
-               + ' data-width="8%"'
-               + ' data-after="-' + duration.toString() + '"'
-               + ' data-points="' + duration.toString() + '"'
-               + ' data-colors="' + NETDATA.colors[7] + '"'
-               + ' role="application"></div>';
-
-       return head;
+    var head = '';
+
+    if(typeof charts['system.swap'] !== 'undefined')
+        head += '<div style="margin-right: 10px;" data-netdata="system.swap"'
+        + ' data-dimensions="free"'
+        + ' data-append-options="percentage"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="Free Swap"'
+        + ' data-units="%"'
+        + ' data-easypiechart-max-value="100"'
+        + ' data-width="8%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' data-colors="#DD4400"'
+        + ' role="application"></div>';
+
+    if(typeof charts['system.io'] !== 'undefined') {
+        head += '<div style="margin-right: 10px;" data-netdata="system.io"'
+        + ' data-dimensions="in"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="Disk Read"'
+        + ' data-width="10%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' role="application"></div>';
+
+        head += '<div style="margin-right: 10px;" data-netdata="system.io"'
+        + ' data-dimensions="out"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="Disk Write"'
+        + ' data-width="10%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' role="application"></div>';
+    }
+
+    if(typeof charts['system.cpu'] !== 'undefined')
+        head += '<div data-netdata="system.cpu"'
+        + ' data-chart-library="gauge"'
+        + ' data-title="CPU"'
+        + ' data-units="%"'
+        + ' data-gauge-max-value="100"'
+        + ' data-width="18%"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' data-colors="' + NETDATA.colors[12] + '"'
+        + ' role="application"></div>';
+
+    if(typeof charts['system.ipv4'] !== 'undefined') {
+        head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"'
+        + ' data-dimensions="received"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="IPv4 Inbound"'
+        + ' data-width="10%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' role="application"></div>';
+
+        head += '<div style="margin-right: 10px;" data-netdata="system.ipv4"'
+        + ' data-dimensions="sent"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="IPv4 Outbound"'
+        + ' data-width="10%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' role="application"></div>';
+    }
+    else if(typeof charts['system.ipv6'] !== 'undefined') {
+        head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"'
+        + ' data-dimensions="received"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="IPv6 Inbound"'
+        + ' data-units="kbps"'
+        + ' data-width="10%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' role="application"></div>';
+
+        head += '<div style="margin-right: 10px;" data-netdata="system.ipv6"'
+        + ' data-dimensions="sent"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="IPv6 Outbound"'
+        + ' data-units="kbps"'
+        + ' data-width="10%"'
+        + ' data-before="0"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' role="application"></div>';
+    }
+
+    if(typeof charts['system.ram'] !== 'undefined')
+        head += '<div style="margin-right: 10px;" data-netdata="system.ram"'
+        + ' data-dimensions="cached|free"'
+        + ' data-append-options="percentage"'
+        + ' data-chart-library="easypiechart"'
+        + ' data-title="Available RAM"'
+        + ' data-units="%"'
+        + ' data-easypiechart-max-value="100"'
+        + ' data-width="8%"'
+        + ' data-after="-' + duration.toString() + '"'
+        + ' data-points="' + duration.toString() + '"'
+        + ' data-colors="' + NETDATA.colors[7] + '"'
+        + ' role="application"></div>';
+
+    return head;
 }
 
 function generateHeadCharts(type, chart, duration) {
-       var head = '';
-       var hcharts = anyAttribute(chartData, type, chart.context, []);
-       if(hcharts.length > 0) {
-               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);
-                       else
-                               head += hcharts[hi].replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id);
-                       hi++;
-               }
-       }
-       return head;
+    var head = '';
+    var hcharts = anyAttribute(chartData, type, chart.context, []);
+    if(hcharts.length > 0) {
+        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);
+            else
+                head += hcharts[hi].replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id);
+            hi++;
+        }
+    }
+    return head;
 }
 
 function renderPage(menus, data) {
-       var div = document.getElementById('charts_div');
-       var pcent_width = Math.floor(100 / chartsPerRow($(div).width()));
-
-       // find the proper duration for per-second updates
-       var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60;
-       var html = '';
-       var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">';
-       var mainhead = headMain(data.charts, duration);
-
-       // sort the menus
-       var main = sortObjectByPriority(menus);
-       var i = 0, len = main.length;
-       while(i < len) {
-               var menu = main[i++];
-
-               // generate an entry at the main menu
-
-               var menuid = name2id(menu);
-               sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].title + '</a><ul class="nav">';
-               html += '<div role="section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].title + '</h1></div><div id="menu_' + menuid + '" role="document">';
-
-               if(menus[menu].info !== null)
-                       html += menus[menu].info;
-
-               // console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title);
-
-               var shtml = '';
-               var mhead = '<div class="netdata-chart-row">' + mainhead;
-               mainhead = '';
-
-               // sort the submenus of this menu
-               var sub = sortObjectByPriority(menus[menu].submenus);
-               var si = 0, slen = sub.length;
-               while(si < slen) {
-                       var submenu = sub[si++];
-
-                       // generate an entry at the submenu
-                       var submenuid = name2id(menu + '_' + submenu);
-                       sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus[menu].submenus[submenu].title + '</a></li>';
-                       shtml += '<div class="netdata-group-container" id="submenu_' + submenuid + '" style="display: inline-block; width: ' + pcent_width.toString() + '%"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus[menu].submenus[submenu].title + '</h2>';
-
-                       if(menus[menu].submenus[submenu].info !== null)
-                               shtml += '<div class="chart-message netdata-chart-alignment" role="document">' + menus[menu].submenus[submenu].info + '</div>';
-
-                       var head = '<div class="netdata-chart-row">';
-                       var chtml = '';
-
-                       // console.log('    \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title);
-
-                       // sort the charts in this submenu of this menu
-                       menus[menu].submenus[submenu].charts.sort(prioritySort);
-                       var ci = 0, clen = menus[menu].submenus[submenu].charts.length;
-                       while(ci < clen) {
-                               var chart = menus[menu].submenus[submenu].charts[ci++];
-
-                               // generate the submenu heading charts
-                               mhead += generateHeadCharts('mainheads', chart, duration);
-                               head += generateHeadCharts('heads', chart, duration);
-
-                               // generate the chart
-                               chtml += chartInfo(chart.context) + '<div data-netdata="' + chart.id + '"'
-                                       + ' data-width="' + pcent_width.toString() + '%"'
-                                       + ' data-height="' + chartHeight(chart.context, options.chartsHeight).toString() + 'px"'
-                                       + ' data-before="0"'
-                                       + ' data-after="-' + duration.toString() + '"'
-                                       + ' data-id="' + name2id(options.hostname + '/' + chart.id) + '"'
-                                       + ' data-colors="' + anyAttribute(chartData, 'colors', chart.context, '') + '"'
-                                       + ' role="application"></div>';
-
-                               // console.log('         \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context  + ' height: ' + menus[menu].submenus[submenu].height);
-                       }
-
-                       head += '</div>';
-                       shtml += head + chtml + '</div>';
-               }
-
-               mhead += '</div>';
-               sidebar += '</ul></li>';
-               html += mhead + shtml + '</div></div><hr role="separator"/>';
-       }
-
-       sidebar += '</ul>';
-       div.innerHTML = html;
-       document.getElementById('sidebar').innerHTML = sidebar;
-       finalizePage();
+    var div = document.getElementById('charts_div');
+    var pcent_width = Math.floor(100 / chartsPerRow($(div).width()));
+
+    // find the proper duration for per-second updates
+    var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60;
+    var html = '';
+    var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">';
+    var mainhead = headMain(data.charts, duration);
+
+    // sort the menus
+    var main = sortObjectByPriority(menus);
+    var i = 0, len = main.length;
+    while(i < len) {
+        var menu = main[i++];
+
+        // generate an entry at the main menu
+
+        var menuid = name2id(menu);
+        sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus[menu].title + '</a><ul class="nav">';
+        html += '<div role="section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus[menu].title + '</h1></div><div id="menu_' + menuid + '" role="document">';
+
+        if(menus[menu].info !== null)
+            html += menus[menu].info;
+
+        // console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title);
+
+        var shtml = '';
+        var mhead = '<div class="netdata-chart-row">' + mainhead;
+        mainhead = '';
+
+        // sort the submenus of this menu
+        var sub = sortObjectByPriority(menus[menu].submenus);
+        var si = 0, slen = sub.length;
+        while(si < slen) {
+            var submenu = sub[si++];
+
+            // generate an entry at the submenu
+            var submenuid = name2id(menu + '_' + submenu);
+            sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus[menu].submenus[submenu].title + '</a></li>';
+            shtml += '<div class="netdata-group-container" id="submenu_' + submenuid + '" style="display: inline-block; width: ' + pcent_width.toString() + '%"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus[menu].submenus[submenu].title + '</h2>';
+
+            if(menus[menu].submenus[submenu].info !== null)
+                shtml += '<div class="chart-message netdata-chart-alignment" role="document">' + menus[menu].submenus[submenu].info + '</div>';
+
+            var head = '<div class="netdata-chart-row">';
+            var chtml = '';
+
+            // console.log('    \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title);
+
+            // sort the charts in this submenu of this menu
+            menus[menu].submenus[submenu].charts.sort(prioritySort);
+            var ci = 0, clen = menus[menu].submenus[submenu].charts.length;
+            while(ci < clen) {
+                var chart = menus[menu].submenus[submenu].charts[ci++];
+
+                // generate the submenu heading charts
+                mhead += generateHeadCharts('mainheads', chart, duration);
+                head += generateHeadCharts('heads', chart, duration);
+
+                // generate the chart
+                chtml += chartInfo(chart.context) + '<div data-netdata="' + chart.id + '"'
+                    + ' data-width="' + pcent_width.toString() + '%"'
+                    + ' data-height="' + chartHeight(chart.context, options.chartsHeight).toString() + 'px"'
+                    + ' data-before="0"'
+                    + ' data-after="-' + duration.toString() + '"'
+                    + ' data-id="' + name2id(options.hostname + '/' + chart.id) + '"'
+                    + ' data-colors="' + anyAttribute(chartData, 'colors', chart.context, '') + '"'
+                    + ' role="application"></div>';
+
+                // console.log('         \------- ' + chart.id + ' (' + chart.priority + '): ' + chart.context  + ' height: ' + menus[menu].submenus[submenu].height);
+            }
+
+            head += '</div>';
+            shtml += head + chtml + '</div>';
+        }
+
+        mhead += '</div>';
+        sidebar += '</ul></li>';
+        html += mhead + shtml + '</div></div><hr role="separator"/>';
+    }
+
+    sidebar += '</ul>';
+    div.innerHTML = html;
+    document.getElementById('sidebar').innerHTML = sidebar;
+    finalizePage();
 }
 
 function renderChartsAndMenu(data) {
-       var menus = {};
-       var charts = data.charts;
-
-       for(var c in charts) {
-               enrichChartData(charts[c]);
-
-               // create the menu
-               if(typeof menus[charts[c].menu] === 'undefined') {
-                       menus[charts[c].menu] = {
-                               priority: charts[c].priority,
-                               submenus: {},
-                               title: menuTitle(charts[c]),
-                               info: menuInfo(charts[c].menu),
-                               height: menuHeight(charts[c].menu, options.chartsHeight)
-                       };
-               }
-
-               if(charts[c].priority < menus[charts[c].menu].priority)
-                       menus[charts[c].menu].priority = charts[c].priority;
-
-               // 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(),
-                               title: null,
-                               info: submenuInfo(charts[c].menu, charts[c].submenu),
-                               height: submenuHeight(charts[c].menu, charts[c].submenu, menus[charts[c].menu].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;
-
-               // index the chart in the menu/submenu
-               menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]);
-       }
-
-       // propagate the descriptive subname given to QoS
-       // to all the other submenus with the same name
-       for(var m in menus) {
-               for(var s in menus[m].submenus) {
-                       // 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 = submenuTitle(m, s);
-                       }
-               }
-       }
-
-       renderPage(menus, data);
+    var menus = {};
+    var charts = data.charts;
+
+    for(var c in charts) {
+        enrichChartData(charts[c]);
+
+        // create the menu
+        if(typeof menus[charts[c].menu] === 'undefined') {
+            menus[charts[c].menu] = {
+                priority: charts[c].priority,
+                submenus: {},
+                title: menuTitle(charts[c]),
+                info: menuInfo(charts[c].menu),
+                height: menuHeight(charts[c].menu, options.chartsHeight)
+            };
+        }
+
+        if(charts[c].priority < menus[charts[c].menu].priority)
+            menus[charts[c].menu].priority = charts[c].priority;
+
+        // 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(),
+                title: null,
+                info: submenuInfo(charts[c].menu, charts[c].submenu),
+                height: submenuHeight(charts[c].menu, charts[c].submenu, menus[charts[c].menu].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;
+
+        // index the chart in the menu/submenu
+        menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]);
+    }
+
+    // propagate the descriptive subname given to QoS
+    // to all the other submenus with the same name
+    for(var m in menus) {
+        for(var s in menus[m].submenus) {
+            // 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 = submenuTitle(m, s);
+            }
+        }
+    }
+
+    renderPage(menus, data);
 }
 
 function downloadAllCharts(netdata_url) {
-       if(typeof netdata_url === 'undefined' || netdata_url === null)
-               netdata_url = NETDATA.serverDefault;
+    if(typeof netdata_url === 'undefined' || netdata_url === null)
+        netdata_url = NETDATA.serverDefault;
 
-       NETDATA.pause(function() {
-               $("#loadOverlay").css("display","none");
-               $("#loadOverlay").css("display","none");
+    NETDATA.pause(function() {
+        $("#loadOverlay").css("display","none");
+        $("#loadOverlay").css("display","none");
 
-               // download all the charts the server knows
-               NETDATA.chartRegistry.downloadAll(netdata_url, function(data) {
+        // download all the charts the server knows
+        NETDATA.chartRegistry.downloadAll(netdata_url, function(data) {
 
-                       options.data = data;
-                       options.hostname = data.hostname;
-                       document.getElementById('hostname').innerHTML = options.hostname;
-                       document.getElementById('hostname').href = NETDATA.serverDefault;
-                       document.title = options.hostname + ' netdata dashboard';
+            options.data = data;
+            options.hostname = data.hostname;
+            document.getElementById('hostname').innerHTML = options.hostname;
+            document.getElementById('hostname').href = NETDATA.serverDefault;
+            document.title = options.hostname + ' netdata dashboard';
 
-                       renderChartsAndMenu(data);
+            renderChartsAndMenu(data);
 
-                       // prepare our DOM
-//                     prepareScreen(data);
+            // prepare our DOM
+//          prepareScreen(data);
 
-                       // due to affix issues, prepareScreen() will unpause
-                       // netdata as needed
-               });
-       });
+            // due to affix issues, prepareScreen() will unpause
+            // netdata as needed
+        });
+    });
 }
 
 // ----------------------------------------------------------------------------
 
 function versionLog(msg) {
-       document.getElementById('versionCheckLog').innerHTML = msg;
+    document.getElementById('versionCheckLog').innerHTML = msg;
 }
 
 function getNetdataVersion(callback) {
-       versionLog('Downloading installed version info from netdata...');
-
-       $.ajax({
-               url: 'version.txt',
-               async: true,
-               cache: false
-       })
-       .done(function(data) {
-               data = data.replace(/(\r\n|\n|\r| |\t)/gm,"");
-               if(data.length !== 40) {
-                       versionLog('Received version string is invalid.');
-                       callback(null);
-               }
-               else {
-                       versionLog('Installed version of netdata is ' + data);
-                       document.getElementById('netdataVersion').innerHTML = data;
-                       callback(data);
-               }
-       })
-       .fail(function() {
-               versionLog('Failed to download installed version info from netdata!');
-               callback(null);
-       });
+    versionLog('Downloading installed version info from netdata...');
+
+    $.ajax({
+        url: 'version.txt',
+        async: true,
+        cache: false
+    })
+    .done(function(data) {
+        data = data.replace(/(\r\n|\n|\r| |\t)/gm,"");
+        if(data.length !== 40) {
+            versionLog('Received version string is invalid.');
+            callback(null);
+        }
+        else {
+            versionLog('Installed version of netdata is ' + data);
+            document.getElementById('netdataVersion').innerHTML = data;
+            callback(data);
+        }
+    })
+    .fail(function() {
+        versionLog('Failed to download installed version info from netdata!');
+        callback(null);
+    });
 }
 
 function getGithubLatestCommit(callback) {
-       versionLog('Downloading latest version info from github...');
-
-       $.ajax({
-               url: 'https://api.github.com/repos/firehol/netdata/commits',
-               async: true,
-               cache: false
-       })
-       .done(function(data) {
-               versionLog('Latest version info from github is ' + data[0].sha);
-               callback(data[0].sha);
-       })
-       .fail(function() {
-               versionLog('Failed to download installed version info from github!');
-               callback(null);
-       });
+    versionLog('Downloading latest version info from github...');
+
+    $.ajax({
+        url: 'https://api.github.com/repos/firehol/netdata/commits',
+        async: true,
+        cache: false
+    })
+    .done(function(data) {
+        versionLog('Latest version info from github is ' + data[0].sha);
+        callback(data[0].sha);
+    })
+    .fail(function() {
+        versionLog('Failed to download installed version info from github!');
+        callback(null);
+    });
 }
 
 function checkForUpdate(callback) {
-       getNetdataVersion(function(sha1) {
-               if(sha1 === null) callback(null, null);
+    getNetdataVersion(function(sha1) {
+        if(sha1 === null) callback(null, null);
 
-               getGithubLatestCommit(function(sha2) {
-                       callback(sha1, sha2);
-               });
-       });
+        getGithubLatestCommit(function(sha2) {
+            callback(sha1, sha2);
+        });
+    });
 
-       return null;
+    return null;
 }
 
 var updateBlinkCounter = 0;
 function notifyForUpdate(force) {
-       versionLog('<p>checking for updates...</p>');
-
-       var now = new Date().getTime();
-
-       if(typeof force === 'undefined' || force !== true) {
-               var last = loadLocalStorage('last_update_check');
-
-               if(typeof last === 'string')
-                       last = parseInt(last);
-               else
-                       last = 0;
-
-               if(now - last < 3600000 * 8) {
-                       // no need to check it - too soon
-                       return;
-               }
-       }
-
-       checkForUpdate(function(sha1, sha2) {
-               var save = false;
-
-               if(sha1 === null) {
-                       save = false;
-                       versionLog('<p><big>Failed to get your netdata version!</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>');
-               }
-               else if(sha2 === null) {
-                       save = false;
-                       versionLog('<p><big>Failed to get the latest version from github.</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>');
-               }
-               else if(sha1 === sha2) {
-                       save = true;
-                       versionLog('<p><big>You already have the latest version of netdata!</big></p><p>No update yet?<br/>Probably, we need some motivation to keep going on!</p><p>If you haven\'t already, <a href="https://github.com/firehol/netdata" target="_blank">give netdata a <b>Star</b> at its github page</a>.</p>');
-               }
-               else {
-                       save = true;
-                       var compare = 'https://github.com/firehol/netdata/compare/' + sha1.toString() + '...' + sha2.toString();
-
-                       versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest version: ' + sha2.toString() + '</p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/firehol/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>');
-
-                       function updateButtonBlink() {
-                               updateBlinkCounter--;
-                               if(updateBlinkCounter > 0)
-                                       $('#updateButton').fadeOut(500).fadeIn(500, updateButtonBlink);
-                       }
-
-                       if(updateBlinkCounter === 0) {
-                               updateBlinkCounter = 300;
-                               updateButtonBlink();
-                       }
-               }
-
-               if(save)
-                       saveLocalStorage('last_update_check', now.toString());
-       });
+    versionLog('<p>checking for updates...</p>');
+
+    var now = new Date().getTime();
+
+    if(typeof force === 'undefined' || force !== true) {
+        var last = loadLocalStorage('last_update_check');
+
+        if(typeof last === 'string')
+            last = parseInt(last);
+        else
+            last = 0;
+
+        if(now - last < 3600000 * 8) {
+            // no need to check it - too soon
+            return;
+        }
+    }
+
+    checkForUpdate(function(sha1, sha2) {
+        var save = false;
+
+        if(sha1 === null) {
+            save = false;
+            versionLog('<p><big>Failed to get your netdata version!</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>');
+        }
+        else if(sha2 === null) {
+            save = false;
+            versionLog('<p><big>Failed to get the latest version from github.</big></p><p>You can always get the latest version of netdata from <a href="https://github.com/firehol/netdata" target="_blank">its github page</a>.</p>');
+        }
+        else if(sha1 === sha2) {
+            save = true;
+            versionLog('<p><big>You already have the latest version of netdata!</big></p><p>No update yet?<br/>Probably, we need some motivation to keep going on!</p><p>If you haven\'t already, <a href="https://github.com/firehol/netdata" target="_blank">give netdata a <b>Star</b> at its github page</a>.</p>');
+        }
+        else {
+            save = true;
+            var compare = 'https://github.com/firehol/netdata/compare/' + sha1.toString() + '...' + sha2.toString();
+
+            versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest version: ' + sha2.toString() + '</p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/firehol/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>');
+
+            function updateButtonBlink() {
+                updateBlinkCounter--;
+                if(updateBlinkCounter > 0)
+                    $('#updateButton').fadeOut(500).fadeIn(500, updateButtonBlink);
+            }
+
+            if(updateBlinkCounter === 0) {
+                updateBlinkCounter = 300;
+                updateButtonBlink();
+            }
+        }
+
+        if(save)
+            saveLocalStorage('last_update_check', now.toString());
+    });
 }
 
 // ----------------------------------------------------------------------------
 
 function finalizePage() {
-       // resize all charts - without starting the background thread
-       // this has to be done while NETDATA is paused
-       // if we ommit this, the affix menu will be wrong, since all
-       // the Dom elements are initially zero-sized
-       NETDATA.parseDom();
-
-       if(urlOptions.pan_and_zoom === true) {
-               urlOptions.nowelcome = true;
-               NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
-       }
-
-       // ------------------------------------------------------------------------
-       // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js
-       $.fn.shorten = function(settings) {
-               "use strict";
-
-               var config = {
-                       showChars: 750,
-                       minHideChars: 10,
-                       ellipsesText: "...",
-                       moreText: '<i class="fa fa-expand" aria-hidden="true"></i> show more information',
-                       lessText: '<i class="fa fa-compress" aria-hidden="true"></i> show less information',
-                       onLess: function() { NETDATA.onscroll(); },
-                       onMore: function() { NETDATA.onscroll(); },
-                       errMsg: null,
-                       force: false
-               };
-
-               if (settings) {
-                       $.extend(config, settings);
-               }
-
-               if ($(this).data('jquery.shorten') && !config.force) {
-                       return false;
-               }
-               $(this).data('jquery.shorten', true);
-
-               $(document).off("click", '.morelink');
-
-               $(document).on({
-                       click: function() {
-
-                               var $this = $(this);
-                               if ($this.hasClass('less')) {
-                                       $this.removeClass('less');
-                                       $this.html(config.moreText);
-                                       $this.parent().prev().animate({'height':'0'+'%'}, 0, function () { $this.parent().prev().prev().show(); }).hide(0, function() {
-                                               config.onLess();
-                                       });
-
-                               } else {
-                                       $this.addClass('less');
-                                       $this.html(config.lessText);
-                                       $this.parent().prev().animate({'height':'100'+'%'}, 0, function () { $this.parent().prev().prev().hide(); }).show(0, function() {
-                                               config.onMore();
-                                       });
-                               }
-                               return false;
-                       }
-               }, '.morelink');
-
-               return this.each(function() {
-                       var $this = $(this);
-
-                       var content = $this.html();
-                       var contentlen = $this.text().length;
-                       if (contentlen > config.showChars + config.minHideChars) {
-                               var c = content.substr(0, config.showChars);
-                               if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it
-                               {
-                                       var inTag = false; // I'm in a tag?
-                                       var bag = ''; // Put the characters to be shown here
-                                       var countChars = 0; // Current bag size
-                                       var openTags = []; // Stack for opened tags, so I can close them later
-                                       var tagName = null;
-
-                                       for (var i = 0, r = 0; r <= config.showChars; i++) {
-                                               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 != '/' + 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!)
-                                                               }
-
-                                                       } else {
-                                                               // There are some nasty tags that don't have a close tag like <br/>
-                                                               if (tagName.toLowerCase() != 'br') {
-                                                                       openTags.unshift(tagName); // Add to start the name of the tag that opens
-                                                               }
-                                                       }
-                                               }
-                                               if (inTag && content[i] == '>') {
-                                                       inTag = false;
-                                               }
-
-                                               if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result
-                                               else {
-                                                       r++;
-                                                       if (countChars <= config.showChars) {
-                                                               bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the []
-                                                               countChars++;
-                                                       } else // Now I have the characters needed
-                                                       {
-                                                               if (openTags.length > 0) // I have unclosed tags
-                                                               {
-                                                                       //console.log('They were open tags');
-                                                                       //console.log(openTags);
-                                                                       for (j = 0; j < openTags.length; j++) {
-                                                                               //console.log('Cierro tag ' + openTags[j]);
-                                                                               bag += '</' + openTags[j] + '>'; // Close all tags that were opened
-
-                                                                               // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
-                                                                       }
-                                                                       break;
-                                                               }
-                                                       }
-                                               }
-                                       }
-                                       c = $('<div/>').html(bag + '<span class="ellip">' + config.ellipsesText + '</span>').html();
-                               }else{
-                                       c+=config.ellipsesText;
-                               }
-
-                               var html = '<div class="shortcontent">' + c +
-                                               '</div><div class="allcontent">' + content +
-                                               '</div><span><a href="javascript://nop/" class="morelink">' + config.moreText + '</a></span>';
-
-                               $this.html(html);
-                               $this.find(".allcontent").hide(); // Hide all text
-                               $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened
-                       }
-               });
-
-       };
-       $(".chart-message").shorten();
-       // ------------------------------------------------------------------------
-
-       // callback for us to track PanAndZoom operations
-       NETDATA.globalPanAndZoom.callback = netdataPanAndZoomCallback;
-
-       // let it run (update the charts)
-       NETDATA.unpause();
-
-       // check if we have to jump to a specific section
-       scrollToId(urlOptions.hash.replace('#',''));
-
-       /* activate bootstrap sidebar (affix) */
-       $('#sidebar').affix({
-               offset: {
-                       top: (isdemo())?150:0,
-                       bottom: 0
-               }
-       });
-
-       /* 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() {
-               $(this).removeAttr('style');
-       });
-
-       /* activate bootstrap scrollspy (needed for sidebar) */
-       $(document.body).scrollspy({
-               target: '#sidebar',
-               offset: $(window).height() / 3 // controls the diff of the <hX> 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) {
-               var el = $(e.target);
-               if(el.find('ul').size() == 0) {
-                       var hash = el.find('a').attr('href');
-                       if(typeof hash === 'string' && hash.substring(0, 1) == '#') {
-                               urlOptions.hash = hash;
-                               // console.log(urlOptions.hash);
-                               netdataHashUpdate();
-                       }
-               }
-       });
-
-       document.getElementById('footer').style.display = 'block';
-
-       var update_options_modal = function() {
-               // console.log('update_options_modal');
-
-               var sync_option = function(option) {
-                       var self = $('#' + option);
-
-                       if(self.prop('checked') !== NETDATA.getOption(option)) {
-                               // 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');
-               sync_option('parallel_refresher');
-               sync_option('concurrent_refreshes');
-               sync_option('sync_selection');
-               sync_option('sync_pan_and_zoom');
-               sync_option('stop_updates_when_focus_is_lost');
-               sync_option('smooth_plot');
-               sync_option('pan_and_zoom_data_padding');
-               sync_option('show_help');
-               theme_sync_option('netdata_theme_control');
-
-               if(NETDATA.getOption('parallel_refresher') === false) {
-                       $('#concurrent_refreshes_row').hide();
-               }
-               else {
-                       $('#concurrent_refreshes_row').show();
-               }
-       };
-       NETDATA.setOption('setOptionCallback', update_options_modal);
-
-       // handle options changes
-       $('#eliminate_zero_dimensions').change(function()       { NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked')); });
-       $('#destroy_on_hide').change(function()                 { NETDATA.setOption('destroy_on_hide', $(this).prop('checked')); });
-       $('#parallel_refresher').change(function()              { NETDATA.setOption('parallel_refresher', $(this).prop('checked')); });
-       $('#concurrent_refreshes').change(function()            { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); });
-       $('#sync_selection').change(function()                  { NETDATA.setOption('sync_selection', $(this).prop('checked')); });
-       $('#sync_pan_and_zoom').change(function()               { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); });
-       $('#stop_updates_when_focus_is_lost').change(function() { NETDATA.setOption('stop_updates_when_focus_is_lost', $(this).prop('checked')); });
-       $('#smooth_plot').change(function()                     { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
-       $('#pan_and_zoom_data_padding').change(function()       { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
-       $('#show_help').change(function()                       {
-               urlOptions.help = $(this).prop('checked');
-               NETDATA.setOption('show_help', urlOptions.help);
-               netdataReload();
-       });
-
-       // this has to be the last
-       // it reloads the page
-       $('#netdata_theme_control').change(function() {
-               urlOptions.theme = $(this).prop('checked')?'slate':'white';
-               if(setTheme(urlOptions.theme))
-                       netdataReload();
-       });
-
-       $('#updateModal').on('shown.bs.modal', function() {
-               notifyForUpdate(true);
-       });
-
-       $('#deleteRegistryModal').on('hidden.bs.modal', function() {
-               deleteRegistryGuid = null;
-       });
-
-       if(isdemo()) {
-               if(!urlOptions.nowelcome) {
-                       setTimeout(function() {
-                               $('#welcomeModal').modal();
-                       }, 1000);
-               }
-
-               // google analytics when this is used for the home page of the demo sites
-               // this does not run on user's installations
-               setTimeout(function() {
-                       (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-                       (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-                       m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-                       })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
-
-                       ga('create', 'UA-64295674-3', 'auto');
-                       ga('send', 'pageview');
-               }, 2000);
-       }
-       else notifyForUpdate();
+    // resize all charts - without starting the background thread
+    // this has to be done while NETDATA is paused
+    // if we ommit this, the affix menu will be wrong, since all
+    // the Dom elements are initially zero-sized
+    NETDATA.parseDom();
+
+    if(urlOptions.pan_and_zoom === true) {
+        urlOptions.nowelcome = true;
+        NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
+    }
+
+    // ------------------------------------------------------------------------
+    // https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js
+    $.fn.shorten = function(settings) {
+        "use strict";
+
+        var config = {
+            showChars: 750,
+            minHideChars: 10,
+            ellipsesText: "...",
+            moreText: '<i class="fa fa-expand" aria-hidden="true"></i> show more information',
+            lessText: '<i class="fa fa-compress" aria-hidden="true"></i> show less information',
+            onLess: function() { NETDATA.onscroll(); },
+            onMore: function() { NETDATA.onscroll(); },
+            errMsg: null,
+            force: false
+        };
+
+        if (settings) {
+            $.extend(config, settings);
+        }
+
+        if ($(this).data('jquery.shorten') && !config.force) {
+            return false;
+        }
+        $(this).data('jquery.shorten', true);
+
+        $(document).off("click", '.morelink');
+
+        $(document).on({
+            click: function() {
+
+                var $this = $(this);
+                if ($this.hasClass('less')) {
+                    $this.removeClass('less');
+                    $this.html(config.moreText);
+                    $this.parent().prev().animate({'height':'0'+'%'}, 0, function () { $this.parent().prev().prev().show(); }).hide(0, function() {
+                        config.onLess();
+                    });
+
+                } else {
+                    $this.addClass('less');
+                    $this.html(config.lessText);
+                    $this.parent().prev().animate({'height':'100'+'%'}, 0, function () { $this.parent().prev().prev().hide(); }).show(0, function() {
+                        config.onMore();
+                    });
+                }
+                return false;
+            }
+        }, '.morelink');
+
+        return this.each(function() {
+            var $this = $(this);
+
+            var content = $this.html();
+            var contentlen = $this.text().length;
+            if (contentlen > config.showChars + config.minHideChars) {
+                var c = content.substr(0, config.showChars);
+                if (c.indexOf('<') >= 0) // If there's HTML don't want to cut it
+                {
+                    var inTag = false; // I'm in a tag?
+                    var bag = ''; // Put the characters to be shown here
+                    var countChars = 0; // Current bag size
+                    var openTags = []; // Stack for opened tags, so I can close them later
+                    var tagName = null;
+
+                    for (var i = 0, r = 0; r <= config.showChars; i++) {
+                        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 != '/' + 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!)
+                                }
+
+                            } else {
+                                // There are some nasty tags that don't have a close tag like <br/>
+                                if (tagName.toLowerCase() != 'br') {
+                                    openTags.unshift(tagName); // Add to start the name of the tag that opens
+                                }
+                            }
+                        }
+                        if (inTag && content[i] == '>') {
+                            inTag = false;
+                        }
+
+                        if (inTag) { bag += content.charAt(i); } // Add tag name chars to the result
+                        else {
+                            r++;
+                            if (countChars <= config.showChars) {
+                                bag += content.charAt(i); // Fix to ie 7 not allowing you to reference string characters using the []
+                                countChars++;
+                            } else // Now I have the characters needed
+                            {
+                                if (openTags.length > 0) // I have unclosed tags
+                                {
+                                    //console.log('They were open tags');
+                                    //console.log(openTags);
+                                    for (j = 0; j < openTags.length; j++) {
+                                        //console.log('Cierro tag ' + openTags[j]);
+                                        bag += '</' + openTags[j] + '>'; // Close all tags that were opened
+
+                                        // You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    c = $('<div/>').html(bag + '<span class="ellip">' + config.ellipsesText + '</span>').html();
+                }else{
+                    c+=config.ellipsesText;
+                }
+
+                var html = '<div class="shortcontent">' + c +
+                        '</div><div class="allcontent">' + content +
+                        '</div><span><a href="javascript://nop/" class="morelink">' + config.moreText + '</a></span>';
+
+                $this.html(html);
+                $this.find(".allcontent").hide(); // Hide all text
+                $('.shortcontent p:last', $this).css('margin-bottom', 0); //Remove bottom margin on last paragraph as it's likely shortened
+            }
+        });
+
+    };
+    $(".chart-message").shorten();
+    // ------------------------------------------------------------------------
+
+    // callback for us to track PanAndZoom operations
+    NETDATA.globalPanAndZoom.callback = netdataPanAndZoomCallback;
+
+    // let it run (update the charts)
+    NETDATA.unpause();
+
+    // check if we have to jump to a specific section
+    scrollToId(urlOptions.hash.replace('#',''));
+
+    /* activate bootstrap sidebar (affix) */
+    $('#sidebar').affix({
+        offset: {
+            top: (isdemo())?150:0,
+            bottom: 0
+        }
+    });
+
+    /* 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() {
+        $(this).removeAttr('style');
+    });
+
+    /* activate bootstrap scrollspy (needed for sidebar) */
+    $(document.body).scrollspy({
+        target: '#sidebar',
+        offset: $(window).height() / 3 // controls the diff of the <hX> 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) {
+        var el = $(e.target);
+        if(el.find('ul').size() == 0) {
+            var hash = el.find('a').attr('href');
+            if(typeof hash === 'string' && hash.substring(0, 1) == '#') {
+                urlOptions.hash = hash;
+                // console.log(urlOptions.hash);
+                netdataHashUpdate();
+            }
+        }
+    });
+
+    document.getElementById('footer').style.display = 'block';
+
+    var update_options_modal = function() {
+        // console.log('update_options_modal');
+
+        var sync_option = function(option) {
+            var self = $('#' + option);
+
+            if(self.prop('checked') !== NETDATA.getOption(option)) {
+                // 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');
+        sync_option('parallel_refresher');
+        sync_option('concurrent_refreshes');
+        sync_option('sync_selection');
+        sync_option('sync_pan_and_zoom');
+        sync_option('stop_updates_when_focus_is_lost');
+        sync_option('smooth_plot');
+        sync_option('pan_and_zoom_data_padding');
+        sync_option('show_help');
+        theme_sync_option('netdata_theme_control');
+
+        if(NETDATA.getOption('parallel_refresher') === false) {
+            $('#concurrent_refreshes_row').hide();
+        }
+        else {
+            $('#concurrent_refreshes_row').show();
+        }
+    };
+    NETDATA.setOption('setOptionCallback', update_options_modal);
+
+    // handle options changes
+    $('#eliminate_zero_dimensions').change(function()       { NETDATA.setOption('eliminate_zero_dimensions', $(this).prop('checked')); });
+    $('#destroy_on_hide').change(function()                 { NETDATA.setOption('destroy_on_hide', $(this).prop('checked')); });
+    $('#parallel_refresher').change(function()              { NETDATA.setOption('parallel_refresher', $(this).prop('checked')); });
+    $('#concurrent_refreshes').change(function()            { NETDATA.setOption('concurrent_refreshes', $(this).prop('checked')); });
+    $('#sync_selection').change(function()                  { NETDATA.setOption('sync_selection', $(this).prop('checked')); });
+    $('#sync_pan_and_zoom').change(function()               { NETDATA.setOption('sync_pan_and_zoom', $(this).prop('checked')); });
+    $('#stop_updates_when_focus_is_lost').change(function() { NETDATA.setOption('stop_updates_when_focus_is_lost', $(this).prop('checked')); });
+    $('#smooth_plot').change(function()                     { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
+    $('#pan_and_zoom_data_padding').change(function()       { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
+    $('#show_help').change(function()                       {
+        urlOptions.help = $(this).prop('checked');
+        NETDATA.setOption('show_help', urlOptions.help);
+        netdataReload();
+    });
+
+    // this has to be the last
+    // it reloads the page
+    $('#netdata_theme_control').change(function() {
+        urlOptions.theme = $(this).prop('checked')?'slate':'white';
+        if(setTheme(urlOptions.theme))
+            netdataReload();
+    });
+
+    $('#updateModal').on('shown.bs.modal', function() {
+        notifyForUpdate(true);
+    });
+
+    $('#deleteRegistryModal').on('hidden.bs.modal', function() {
+        deleteRegistryGuid = null;
+    });
+
+    if(isdemo()) {
+        if(!urlOptions.nowelcome) {
+            setTimeout(function() {
+                $('#welcomeModal').modal();
+            }, 1000);
+        }
+
+        // google analytics when this is used for the home page of the demo sites
+        // this does not run on user's installations
+        setTimeout(function() {
+            (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+            (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+            m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+            })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
+
+            ga('create', 'UA-64295674-3', 'auto');
+            ga('send', 'pageview');
+        }, 2000);
+    }
+    else notifyForUpdate();
 }
 
 function resetDashboardOptions() {
-       var help = NETDATA.options.current.show_help;
+    var help = NETDATA.options.current.show_help;
 
-       NETDATA.resetOptions();
-       if(setTheme('slate'))
-               netdataReload();
+    NETDATA.resetOptions();
+    if(setTheme('slate'))
+        netdataReload();
 
-       if(help !== NETDATA.options.current.show_help)
-               netdataReload();
+    if(help !== NETDATA.options.current.show_help)
+        netdataReload();
 }
 
 downloadAllCharts();
index a1416dbbe49decdadcaed4594a19d5a5a3e402e2..49c08b614b59a6084f35bf3e5e614b98ee2f3107 100644 (file)
 <!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>NetData Registry Dashboard</title>
-       <meta name="application-name" content="netdata">
-
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
-       
-       <script>
-       // this section has to appear before loading dashboard.js
-
-       // Select a theme.
-       // uncomment on of the two themes:
-
-       // var netdataTheme = 'default'; // this is white
-       var netdataTheme = 'slate'; // this is dark
-
-
-       // Set the default netdata server.
-       // on charts without a 'data-host', this one will be used.
-       // the default is the server that dashboard.js is downloaded from.
-
-       // var netdataServer = 'http://my.server:19999/';
-
-       function registryGotoServer(guid) {
-               console.log('goto server: ' + guid);
-       }
-
-       function registryAddServer(u) {
-               return '<div id="registry_server_' + u.guid + '" class="registry-server-container" onClick="registryGotoServer(\'' + u.guid + '\'); return false;">'
-                               + '<div class="registry-server-name">' + u.name + '</div>'
-                               + '<div data-netdata="system.cpu"'
-                               + ' data-host="' + u.url + '"'
-                               + ' data-chart-library="sparkline"'
-                               + ' data-sparkline-chartrangemin="0"'
-                               + ' data-sparkline-chartrangemax="100"'
-                               + ' data-sparkline-chartrangeclip="true"'
-                               + ' data-sparkline-disabletooltips="true"'
-                               + ' data-sparkline-disableinteraction="true"'
-                               + ' data-sparkline-disablehighlight="true"'
-                               + ' data-sparkline-linecolor="#444"'
-                               + ' data-sparkline-spotcolor="disable"'
-                               + ' data-sparkline-minspotcolor="disable"'
-                               + ' data-sparkline-maxspotcolor="disable"'
-                               + ' data-width="100%"'
-                               + ' data-height="20px"'
-                               + ' data-after="-200"'
-                               + '></div>'
-                               + '</div>';
-       }
-
-       var netdataRegistryCallback = function(machines_array) {
-               var el = '';
-               var a1 = '';
-               var found = 0;
-
-               if(machines_array) {
-                       function name_comparator_desc(a, b) {
-                               if (a.name > b.name) return -1;
-                               if (a.name < b.name) return 1;
-                               return 0;
-                       }
-
-                       var machines = machines_array.sort(name_comparator_desc);
-                       var len = machines.length;
-                       while(len--) {
-                               var u = machines[len];
-
-                               var status = "enabled";
-                               found++;
-
-                               if(u.guid === NETDATA.registry.machine_guid)
-                                       status = "disabled"
-
-                               el += registryAddServer(u);
-                               a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>';
-                       }
-               }
-
-               if(!found) {
-                       if(machines)
-                               el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
-                       else
-                               el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
-
-                       a1 += '<li><a href="#">&nbsp;</a></li>';
-
-                       el += '<li role="separator" class="divider"></li>' +
-                                       '<li><a href="//london.netdata.rocks/default.html">EU - London (DigitalOcean.com)</a></li>' +
-                                       '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
-                                       '<li><a href="//athens.netdata.rocks/default.html">EU - Athens</a></li>';
-                       a1 += '<li role="separator" class="divider"></li>' +
-                                       '<li><a href="#">&nbsp;</a></li>' +
-                                       '<li><a href="#">&nbsp;</a></li>'+
-                                       '<li><a href="#">&nbsp;</a></li>';
-               }
-
-               el += '<li role="separator" class="divider"></li>';
-               a1 += '<li role="separator" class="divider"></li>';
-
-               el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>';
-               a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-cog" aria-hidden="true" style="color: #999;"></i></a></li>'
-
-               document.getElementById('mynetdata_servers').innerHTML = el;
-               //document.getElementById('mynetdata_servers2').innerHTML = el;
-               //document.getElementById('mynetdata_actions1').innerHTML = a1;
-               NETDATA.updatedDom();
-       };
-       </script>
-
-       <!--
-               Load dashboard.js
-
-               to host this HTML file on your web server,
-               you have to load dashboard.js from the netdata server.
-
-               So, pick one the two below
-               If you pick the first, set the server name/IP.
-
-               The second assumes you host this file on /usr/share/netdata/web
-               and that you have chown it to be owned by netdata:netdata
-       -->
-       <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-       <script type="text/javascript" src="dashboard.js?v40"></script>
-
-       <script>
-       // Set options for TV operation
-       // This has to be done, after dashboard.js is loaded
-
-       // destroy charts not shown (lowers memory on the browser)
-       NETDATA.options.current.destroy_on_hide = true;
-       
-       // set this to false, to always show all dimensions
-       NETDATA.options.current.eliminate_zero_dimensions = true;
-       
-       // lower the pressure on this browser
-       NETDATA.options.current.concurrent_refreshes = true;
-
-       // if the tv browser is too slow (a pi?)
-       // set this to false
-       NETDATA.options.current.parallel_refresher = true;
-
-       // always update the charts, even if focus is lost
-       // NETDATA.options.current.stop_updates_when_focus_is_lost = false;
-       </script>
-
-       <style>
-               .registry-container {
-                       margin-left: 50px;
-                       max-width: 400px;
-                       min-width: 200px;
-                       border: 1px solid #555;
-                       max-height: 80vh;
-                       height: auto;
-                       overflow-x: hidden;
-                       /* background-color: #3a3f44; */
-                       text-align: center;
-               }
-               .registry-server-container {
-                       width: 100%;
-                       height: auto;
-                       overflow-x: hidden;
-                       font-size: 10px;
-                       padding-left: 10px;
-                       padding-right: 10px;
-               }
-               .registry-server-container:hover {
-                       color: #f5f5f5;
-                       background-color: #262626;
-               }
-               .registry-server-name {
-                       display: block;
-                       text-decoration: none !important;
-                       clear: both;
-                       font-size: 16px;
-                       font-weight: bold;
-                       white-space: normal;
-                       padding-top: 10px;
-               }
-       </style>
+    <title>NetData Registry Dashboard</title>
+    <meta name="application-name" content="netdata">
+
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+    
+    <script>
+    // this section has to appear before loading dashboard.js
+
+    // Select a theme.
+    // uncomment on of the two themes:
+
+    // var netdataTheme = 'default'; // this is white
+    var netdataTheme = 'slate'; // this is dark
+
+
+    // Set the default netdata server.
+    // on charts without a 'data-host', this one will be used.
+    // the default is the server that dashboard.js is downloaded from.
+
+    // var netdataServer = 'http://my.server:19999/';
+
+    function registryGotoServer(guid) {
+        console.log('goto server: ' + guid);
+    }
+
+    function registryAddServer(u) {
+        return '<div id="registry_server_' + u.guid + '" class="registry-server-container" onClick="registryGotoServer(\'' + u.guid + '\'); return false;">'
+                + '<div class="registry-server-name">' + u.name + '</div>'
+                + '<div data-netdata="system.cpu"'
+                + ' data-host="' + u.url + '"'
+                + ' data-chart-library="sparkline"'
+                + ' data-sparkline-chartrangemin="0"'
+                + ' data-sparkline-chartrangemax="100"'
+                + ' data-sparkline-chartrangeclip="true"'
+                + ' data-sparkline-disabletooltips="true"'
+                + ' data-sparkline-disableinteraction="true"'
+                + ' data-sparkline-disablehighlight="true"'
+                + ' data-sparkline-linecolor="#444"'
+                + ' data-sparkline-spotcolor="disable"'
+                + ' data-sparkline-minspotcolor="disable"'
+                + ' data-sparkline-maxspotcolor="disable"'
+                + ' data-width="100%"'
+                + ' data-height="20px"'
+                + ' data-after="-200"'
+                + '></div>'
+                + '</div>';
+    }
+
+    var netdataRegistryCallback = function(machines_array) {
+        var el = '';
+        var a1 = '';
+        var found = 0;
+
+        if(machines_array) {
+            function name_comparator_desc(a, b) {
+                if (a.name > b.name) return -1;
+                if (a.name < b.name) return 1;
+                return 0;
+            }
+
+            var machines = machines_array.sort(name_comparator_desc);
+            var len = machines.length;
+            while(len--) {
+                var u = machines[len];
+
+                var status = "enabled";
+                found++;
+
+                if(u.guid === NETDATA.registry.machine_guid)
+                    status = "disabled"
+
+                el += registryAddServer(u);
+                a1 += '<li id="registry_action_' + u.guid + '"><a href="#" onclick="deleteRegistryModalHandler(\'' + u.guid + '\',\'' + u.name + '\',\'' + u.url + '\'); return false;"><i class="fa fa-trash-o" aria-hidden="true" style="color: #999;"></i></a></li>';
+            }
+        }
+
+        if(!found) {
+            if(machines)
+                el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">your netdata server list is empty...</a></li>';
+            else
+                el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #666;" target="_blank">failed to contact the registry...</a></li>';
+
+            a1 += '<li><a href="#">&nbsp;</a></li>';
+
+            el += '<li role="separator" class="divider"></li>' +
+                    '<li><a href="//london.netdata.rocks/default.html">EU - London (DigitalOcean.com)</a></li>' +
+                    '<li><a href="//atlanta.netdata.rocks/default.html">US - Atlanta (CDN77.com)</a></li>' +
+                    '<li><a href="//athens.netdata.rocks/default.html">EU - Athens</a></li>';
+            a1 += '<li role="separator" class="divider"></li>' +
+                    '<li><a href="#">&nbsp;</a></li>' +
+                    '<li><a href="#">&nbsp;</a></li>'+
+                    '<li><a href="#">&nbsp;</a></li>';
+        }
+
+        el += '<li role="separator" class="divider"></li>';
+        a1 += '<li role="separator" class="divider"></li>';
+
+        el += '<li><a href="https://github.com/firehol/netdata/wiki/mynetdata-menu-item" style="color: #999;" target="_blank">What is this?</a></li>';
+        a1 += '<li><a href="#" style="color: #999;" onclick="switchRegistryModalHandler(); return false;"><i class="fa fa-cog" aria-hidden="true" style="color: #999;"></i></a></li>'
+
+        document.getElementById('mynetdata_servers').innerHTML = el;
+        //document.getElementById('mynetdata_servers2').innerHTML = el;
+        //document.getElementById('mynetdata_actions1').innerHTML = a1;
+        NETDATA.updatedDom();
+    };
+    </script>
+
+    <!--
+        Load dashboard.js
+
+        to host this HTML file on your web server,
+        you have to load dashboard.js from the netdata server.
+
+        So, pick one the two below
+        If you pick the first, set the server name/IP.
+
+        The second assumes you host this file on /usr/share/netdata/web
+        and that you have chown it to be owned by netdata:netdata
+    -->
+    <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
+    <script type="text/javascript" src="dashboard.js?v40"></script>
+
+    <script>
+    // Set options for TV operation
+    // This has to be done, after dashboard.js is loaded
+
+    // destroy charts not shown (lowers memory on the browser)
+    NETDATA.options.current.destroy_on_hide = true;
+    
+    // set this to false, to always show all dimensions
+    NETDATA.options.current.eliminate_zero_dimensions = true;
+    
+    // lower the pressure on this browser
+    NETDATA.options.current.concurrent_refreshes = true;
+
+    // if the tv browser is too slow (a pi?)
+    // set this to false
+    NETDATA.options.current.parallel_refresher = true;
+
+    // always update the charts, even if focus is lost
+    // NETDATA.options.current.stop_updates_when_focus_is_lost = false;
+    </script>
+
+    <style>
+        .registry-container {
+            margin-left: 50px;
+            max-width: 400px;
+            min-width: 200px;
+            border: 1px solid #555;
+            max-height: 80vh;
+            height: auto;
+            overflow-x: hidden;
+            /* background-color: #3a3f44; */
+            text-align: center;
+        }
+        .registry-server-container {
+            width: 100%;
+            height: auto;
+            overflow-x: hidden;
+            font-size: 10px;
+            padding-left: 10px;
+            padding-right: 10px;
+        }
+        .registry-server-container:hover {
+            color: #f5f5f5;
+            background-color: #262626;
+        }
+        .registry-server-name {
+            display: block;
+            text-decoration: none !important;
+            clear: both;
+            font-size: 16px;
+            font-weight: bold;
+            white-space: normal;
+            padding-top: 10px;
+        }
+    </style>
 </head>
 <body>
 &nbsp;
 <div id="mynetdata_servers" class="registry-container" onscroll="NETDATA.onscroll();">
 
-       Loading....
+    Loading....
 
 </div> <!-- registry-container -->
 </body>
index 8d9d71e1b35146aa4d2ec25be1e738716bc3386e..6642bf30a97b9026fd96ce9250a5d00bf22ec4bb 100644 (file)
 <!DOCTYPE html>
 <html lang="en">
 <head>
-       <title>NetData TV Dashboard</title>
-       <meta name="application-name" content="netdata">
-
-       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-       <meta charset="utf-8">
-       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
-       <meta name="viewport" content="width=device-width, initial-scale=1">
-       <meta name="apple-mobile-web-app-capable" content="yes">
-       <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
-
-       <meta property="og:locale" content="en_US" />
-       <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
-       <meta property="og:url" content="http://my-netdata.io/"/>
-       <meta property="og:type" content="website"/>
-       <meta property="og:site_name" content="netdata"/>
-       <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
-       <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
-       
-       <script>
-       // this section has to appear before loading dashboard.js
-
-       // Select a theme.
-       // uncomment on of the two themes:
-
-       // var netdataTheme = 'default'; // this is white
-       var netdataTheme = 'slate'; // this is dark
-
-
-       // Set the default netdata server.
-       // on charts without a 'data-host', this one will be used.
-       // the default is the server that dashboard.js is downloaded from.
-
-       // var netdataServer = 'http://my.server:19999/';
-       </script>
-
-       <!--
-               Load dashboard.js
-
-               to host this HTML file on your web server,
-               you have to load dashboard.js from the netdata server.
-
-               So, pick one the two below
-               If you pick the first, set the server name/IP.
-
-               The second assumes you host this file on /usr/share/netdata/web
-               and that you have chown it to be owned by netdata:netdata
-       -->
-       <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
-       <script type="text/javascript" src="dashboard.js?v39"></script>
-
-       <script>
-       // Set options for TV operation
-       // This has to be done, after dashboard.js is loaded
-
-       // destroy charts not shown (lowers memory on the browser)
-       NETDATA.options.current.destroy_on_hide = true;
-       
-       // set this to false, to always show all dimensions
-       NETDATA.options.current.eliminate_zero_dimensions = true;
-       
-       // lower the pressure on this browser
-       NETDATA.options.current.concurrent_refreshes = false;
-
-       // if the tv browser is too slow (a pi?)
-       // set this to false
-       NETDATA.options.current.parallel_refresher = true;
-
-       // always update the charts, even if focus is lost
-       // NETDATA.options.current.stop_updates_when_focus_is_lost = false;
-
-       // Since you may render charts from many servers and any of them may
-       // become offline for some time, the charts will break.
-       // This will reload the page every RELOAD_EVERY minutes
-
-       var RELOAD_EVERY = 5;
-       setTimeout(function(){
-               location.reload();
-       }, RELOAD_EVERY * 60 * 1000);
-
-       </script>
+    <title>NetData TV Dashboard</title>
+    <meta name="application-name" content="netdata">
+
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+
+    <meta property="og:locale" content="en_US" />
+    <meta property="og:image" content="https://my-netdata.io/images/seo-performance-512.png"/>
+    <meta property="og:url" content="http://my-netdata.io/"/>
+    <meta property="og:type" content="website"/>
+    <meta property="og:site_name" content="netdata"/>
+    <meta property="og:title" content="netdata - real-time performance monitoring, done right!"/>
+    <meta property="og:description" content="Stunning real-time dashboards, blazingly fast and extremely interactive. Zero configuration, zero dependencies, zero maintenance." />
+    
+    <script>
+    // this section has to appear before loading dashboard.js
+
+    // Select a theme.
+    // uncomment on of the two themes:
+
+    // var netdataTheme = 'default'; // this is white
+    var netdataTheme = 'slate'; // this is dark
+
+
+    // Set the default netdata server.
+    // on charts without a 'data-host', this one will be used.
+    // the default is the server that dashboard.js is downloaded from.
+
+    // var netdataServer = 'http://my.server:19999/';
+    </script>
+
+    <!--
+        Load dashboard.js
+
+        to host this HTML file on your web server,
+        you have to load dashboard.js from the netdata server.
+
+        So, pick one the two below
+        If you pick the first, set the server name/IP.
+
+        The second assumes you host this file on /usr/share/netdata/web
+        and that you have chown it to be owned by netdata:netdata
+    -->
+    <!-- <script type="text/javascript" src="http://my.server:19999/dashboard.js"></script> -->
+    <script type="text/javascript" src="dashboard.js?v39"></script>
+
+    <script>
+    // Set options for TV operation
+    // This has to be done, after dashboard.js is loaded
+
+    // destroy charts not shown (lowers memory on the browser)
+    NETDATA.options.current.destroy_on_hide = true;
+    
+    // set this to false, to always show all dimensions
+    NETDATA.options.current.eliminate_zero_dimensions = true;
+    
+    // lower the pressure on this browser
+    NETDATA.options.current.concurrent_refreshes = false;
+
+    // if the tv browser is too slow (a pi?)
+    // set this to false
+    NETDATA.options.current.parallel_refresher = true;
+
+    // always update the charts, even if focus is lost
+    // NETDATA.options.current.stop_updates_when_focus_is_lost = false;
+
+    // Since you may render charts from many servers and any of them may
+    // become offline for some time, the charts will break.
+    // This will reload the page every RELOAD_EVERY minutes
+
+    var RELOAD_EVERY = 5;
+    setTimeout(function(){
+        location.reload();
+    }, RELOAD_EVERY * 60 * 1000);
+
+    </script>
 
 </head>
 <body>
 
 <div style="width: 100%; text-align: center; display: inline-block;">
 
-       <div style="width: 100%; height: 24vh; text-align: center; display: inline-block;">
-               <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
-                       <b>CPU On both servers</b>
-               </div>
-               <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
-                       <br/>
-                       <div data-netdata="system.cpu"
-                                       data-host="http://netdata.firehol.org"
-                                       data-title="CPU usage of netdata.firehol.org"
-                                       data-chart-library="dygraph"
-                                       data-width="49%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       <div data-netdata="system.cpu"
-                                       data-title="CPU usage of your netdata server"
-                                       data-chart-library="dygraph"
-                                       data-width="49%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-               </div>
-       </div>
-
-
-       <div style="width: 100%; height: 24vh; text-align: center; display: inline-block;">
-               <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
-                       <b>Disk I/O on both servers</b>
-               </div>
-               <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
-                       <div data-netdata="system.io"
-                                       data-host="http://netdata.firehol.org"
-                                       data-title="I/O on netdata.firehol.org"
-                                       data-chart-library="dygraph"
-                                       data-width="49%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       <div data-netdata="system.io"
-                                       data-title="I/O on your netdata server"
-                                       data-chart-library="dygraph"
-                                       data-width="49%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-               </div>
-       </div>
-
-
-       <div style="width: 100%; height: 24vh; text-align: center; display: inline-block;">
-               <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
-                       <b>IPv4 traffic on both servers</b>
-               </div>
-               <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
-                       <div data-netdata="system.ipv4"
-                                       data-host="http://netdata.firehol.org"
-                                       data-title="IPv4 traffic on netdata.firehol.org"
-                                       data-chart-library="dygraph"
-                                       data-width="49%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-                       <div data-netdata="system.ipv4"
-                                       data-title="IPv4 traffic on your netdata server"
-                                       data-chart-library="dygraph"
-                                       data-width="49%"
-                                       data-height="100%"
-                                       data-after="-300"
-                                       ></div>
-               </div>
-       </div>
-
-       <div style="width: 100%; height: 23vh; text-align: center; display: inline-block;">
-               <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
-                       <b>Netdata statistics on both servers</b>
-               </div>
-               <div style="width: 100%; max-height: calc(100% - 15px); text-align: center; display: inline-block;">
-                       <div style="width: 49%; height:100%; align: center; display: inline-block;">
-                               netdata.firehol.org
-                               <br/>
-                               <div data-netdata="netdata.requests"
-                                               data-host="http://netdata.firehol.org"
-                                               data-title="Chart Refreshes/s"
-                                               data-chart-library="gauge"
-                                               data-width="20%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               ></div>
-                               <div data-netdata="netdata.clients"
-                                               data-host="http://netdata.firehol.org"
-                                               data-title="Sockets"
-                                               data-chart-library="gauge"
-                                               data-width="20%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               data-colors="#AA5500"
-                                               ></div>
-                               <div data-netdata="netdata.net"
-                                               data-dimensions="in"
-                                               data-host="http://netdata.firehol.org"
-                                               data-title="Requests Traffic"
-                                               data-chart-library="easypiechart"
-                                               data-width="15%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               ></div>
-                               <div data-netdata="netdata.net"
-                                               data-dimensions="out"
-                                               data-host="http://netdata.firehol.org"
-                                               data-title="Chart Data Traffic"
-                                               data-chart-library="easypiechart"
-                                               data-width="15%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               ></div>
-                       </div>
-                       <div style="width: 49%; height:100%; align: center; display: inline-block;">
-                               your netdata server
-                               <br/>
-                               <div data-netdata="netdata.requests"
-                                               data-title="Chart Refreshes/s"
-                                               data-chart-library="gauge"
-                                               data-width="20%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               ></div>
-                               <div data-netdata="netdata.clients"
-                                               data-title="Sockets"
-                                               data-chart-library="gauge"
-                                               data-width="20%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               data-colors="#AA5500"
-                                               ></div>
-                               <div data-netdata="netdata.net"
-                                               data-dimensions="in"
-                                               data-title="Requests Traffic"
-                                               data-chart-library="easypiechart"
-                                               data-width="15%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               ></div>
-                               <div data-netdata="netdata.net"
-                                               data-dimensions="out"
-                                               data-title="Chart Data Traffic"
-                                               data-chart-library="easypiechart"
-                                               data-width="15%"
-                                               data-height="100%"
-                                               data-after="-300"
-                                               data-points="300"
-                                               ></div>
-                       </div>
-               </div>
-       </div>
+    <div style="width: 100%; height: 24vh; text-align: center; display: inline-block;">
+        <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
+            <b>CPU On both servers</b>
+        </div>
+        <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
+            <br/>
+            <div data-netdata="system.cpu"
+                    data-host="http://netdata.firehol.org"
+                    data-title="CPU usage of netdata.firehol.org"
+                    data-chart-library="dygraph"
+                    data-width="49%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            <div data-netdata="system.cpu"
+                    data-title="CPU usage of your netdata server"
+                    data-chart-library="dygraph"
+                    data-width="49%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+        </div>
+    </div>
+
+
+    <div style="width: 100%; height: 24vh; text-align: center; display: inline-block;">
+        <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
+            <b>Disk I/O on both servers</b>
+        </div>
+        <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
+            <div data-netdata="system.io"
+                    data-host="http://netdata.firehol.org"
+                    data-title="I/O on netdata.firehol.org"
+                    data-chart-library="dygraph"
+                    data-width="49%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            <div data-netdata="system.io"
+                    data-title="I/O on your netdata server"
+                    data-chart-library="dygraph"
+                    data-width="49%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+        </div>
+    </div>
+
+
+    <div style="width: 100%; height: 24vh; text-align: center; display: inline-block;">
+        <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
+            <b>IPv4 traffic on both servers</b>
+        </div>
+        <div style="width: 100%; height: calc(100% - 15px); text-align: center; display: inline-block;">
+            <div data-netdata="system.ipv4"
+                    data-host="http://netdata.firehol.org"
+                    data-title="IPv4 traffic on netdata.firehol.org"
+                    data-chart-library="dygraph"
+                    data-width="49%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+            <div data-netdata="system.ipv4"
+                    data-title="IPv4 traffic on your netdata server"
+                    data-chart-library="dygraph"
+                    data-width="49%"
+                    data-height="100%"
+                    data-after="-300"
+                    ></div>
+        </div>
+    </div>
+
+    <div style="width: 100%; height: 23vh; text-align: center; display: inline-block;">
+        <div style="width: 100%; height: 15px; text-align: center; display: inline-block;">
+            <b>Netdata statistics on both servers</b>
+        </div>
+        <div style="width: 100%; max-height: calc(100% - 15px); text-align: center; display: inline-block;">
+            <div style="width: 49%; height:100%; align: center; display: inline-block;">
+                netdata.firehol.org
+                <br/>
+                <div data-netdata="netdata.requests"
+                        data-host="http://netdata.firehol.org"
+                        data-title="Chart Refreshes/s"
+                        data-chart-library="gauge"
+                        data-width="20%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        ></div>
+                <div data-netdata="netdata.clients"
+                        data-host="http://netdata.firehol.org"
+                        data-title="Sockets"
+                        data-chart-library="gauge"
+                        data-width="20%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        data-colors="#AA5500"
+                        ></div>
+                <div data-netdata="netdata.net"
+                        data-dimensions="in"
+                        data-host="http://netdata.firehol.org"
+                        data-title="Requests Traffic"
+                        data-chart-library="easypiechart"
+                        data-width="15%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        ></div>
+                <div data-netdata="netdata.net"
+                        data-dimensions="out"
+                        data-host="http://netdata.firehol.org"
+                        data-title="Chart Data Traffic"
+                        data-chart-library="easypiechart"
+                        data-width="15%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        ></div>
+            </div>
+            <div style="width: 49%; height:100%; align: center; display: inline-block;">
+                your netdata server
+                <br/>
+                <div data-netdata="netdata.requests"
+                        data-title="Chart Refreshes/s"
+                        data-chart-library="gauge"
+                        data-width="20%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        ></div>
+                <div data-netdata="netdata.clients"
+                        data-title="Sockets"
+                        data-chart-library="gauge"
+                        data-width="20%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        data-colors="#AA5500"
+                        ></div>
+                <div data-netdata="netdata.net"
+                        data-dimensions="in"
+                        data-title="Requests Traffic"
+                        data-chart-library="easypiechart"
+                        data-width="15%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        ></div>
+                <div data-netdata="netdata.net"
+                        data-dimensions="out"
+                        data-title="Chart Data Traffic"
+                        data-chart-library="easypiechart"
+                        data-width="15%"
+                        data-height="100%"
+                        data-after="-300"
+                        data-points="300"
+                        ></div>
+            </div>
+        </div>
+    </div>
 </div>
 </body>
 </html>