]> arthur.barton.de Git - netdata.git/commitdiff
Merge pull request #132 from mcnewton/debianpkg
authorCosta Tsaousis <costa@tsaousis.gr>
Tue, 19 Apr 2016 10:00:06 +0000 (13:00 +0300)
committerCosta Tsaousis <costa@tsaousis.gr>
Tue, 19 Apr 2016 10:00:06 +0000 (13:00 +0300)
Add Debian packaging

42 files changed:
.gitignore
.travis.yml [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0755]
Makefile.am
README.md
conf.d/apps_groups.conf
configure.ac
netdata-9999.ebuild
netdata-installer.sh
netdata.spec.in
plugins.d/charts.d.plugin
plugins.d/tc-qos-helper.sh
src/apps_plugin.c
src/common.c
src/common.h
src/daemon.c
src/log.c
src/log.h
src/main.c
src/plugin_tc.c
src/plugins_d.c
src/proc_diskstats.c
src/proc_net_snmp6.c [changed mode: 0755->0644]
src/proc_net_stat_synproxy.c [changed mode: 0755->0644]
src/procfile.c
src/procfile.h
src/rrd.c
src/rrd2json.c
src/web_client.c
src/web_client.h
src/web_server.c
system/Makefile.am
system/netdata-init-d.in
system/netdata-openrc.in
system/netdata.conf [new file with mode: 0644]
system/netdata.service.in
web/Makefile.am
web/dashboard.css
web/dashboard.js
web/dashboard.slate.css
web/index.html
web/netdata-swagger.yaml

index d3d8aaf802627cab5b58d9eb7994ae47eeb19d5f..f4b9fe85e3ba6cab7bbe1b561feb812b530f9c80 100644 (file)
@@ -32,7 +32,6 @@ netdata.spec
 .cproject
 .idea/
 .project
-CMakeLists.txt
 README
 TODO.md
 asan_symbolize.py
@@ -67,5 +66,8 @@ netdata-uninstaller.sh
 
 gmon.out
 gmon.txt
-
 apps.plugin-profiler.sh
+
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..f2fc25f
--- /dev/null
@@ -0,0 +1,13 @@
+language: c
+compiler:
+ - gcc
+ - clang
+before_install:
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq automake make zlib1g-dev
+script:
+   # default build
+ - ./autogen.sh && ./configure && make -j4
+
+   # test installer
+ - fakeroot ./netdata-installer.sh --install $HOME --dont-wait --dont-start-it
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..2c7abd2
--- /dev/null
@@ -0,0 +1,94 @@
+
+# This file is just a hack to make netdata
+# open in Clion
+# It cannot build netdata
+
+cmake_minimum_required(VERSION 3.3)
+project(netdata)
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+
+set(NETDATA_SOURCE_FILES
+       src/appconfig.c
+       src/appconfig.h
+       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/global_statistics.c
+       src/global_statistics.h
+       src/log.c
+       src/log.h
+       src/main.c
+       src/main.h
+       src/plugin_checks.c
+       src/plugin_checks.h
+       src/plugin_idlejitter.c
+       src/plugin_idlejitter.h
+       src/plugin_nfacct.c
+       src/plugin_nfacct.h
+       src/plugin_proc.c
+       src/plugin_proc.h
+       src/plugins_d.c
+       src/plugins_d.h
+       src/plugin_tc.c
+       src/plugin_tc.h
+       src/popen.c
+       src/popen.h
+       src/proc_diskstats.c
+       src/procfile.c
+       src/procfile.h
+       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_snmp6.c
+       src/proc_net_snmp.c
+       src/proc_net_stat_conntrack.c
+       src/proc_net_stat_synproxy.c
+       src/proc_softirqs.c
+       src/proc_stat.c
+       src/proc_sys_kernel_random_entropy_avail.c
+       src/proc_vmstat.c
+       src/rrd2json.c
+       src/rrd2json.h
+       src/rrd.c
+       src/rrd.h
+       src/storage_number.c
+       src/storage_number.h
+       src/sys_kernel_mm_ksm.c
+       src/unit_test.c
+       src/unit_test.h
+       src/url.c
+       src/url.h
+       src/web_buffer.c
+       src/web_buffer.h
+       src/web_client.c
+       src/web_client.h
+       src/web_server.c
+       src/web_server.h
+        config.h)
+
+set(APPS_PLUGIN_SOURCE_FILES
+       src/appconfig.c
+       src/appconfig.h
+       src/apps_plugin.c
+       src/avl.c
+       src/avl.h
+       src/common.c
+       src/common.h
+       src/log.c
+       src/log.h
+        config.h)
+
+add_definitions(-DHAVE_CONFIG_H -DNETDATA_INTERNAL_CHECKS=1 -DCACHE_DIR="/tmp" -DCONFIG_DIR="/tmp" -DLOG_DIR="/tmp" -DPLUGINS_DIR="/tmp" -DWEB_DIR="/tmp")
+
+add_executable(netdata ${NETDATA_SOURCE_FILES})
+add_executable(apps.plugin ${APPS_PLUGIN_SOURCE_FILES})
index 62325edccc1afdcadde24ee222b051ad92d32136..8c7e1949bb6cd4ecdcaaa010b5e78fdec91915d9 100644 (file)
@@ -19,7 +19,7 @@ MAINTAINERCLEANFILES= \
 EXTRA_DIST = \
        .gitignore \
        autogen.sh \
-       README \
+       README.md \
        LICENSE.md \
        COPYING \
        autogen.sh \
@@ -37,15 +37,6 @@ SUBDIRS = \
        web \
        $(NULL)
 
-if GIT_TREE
-
-all-local: README
-
-README: README.md
-       sed -e '/^## Features$$/p' -e '/^## Git/,/^## Features$$/d' $< > $@
-
-endif
-
 dist_noinst_DATA = netdata.spec
 
 # until integrated within build
index c20179232842101f562f6cdefeba5ed6304c17e0..59360d455f1f082ae8bd8c466ec9eef80559c878 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,8 +1,10 @@
 # netdata
 
-#### 180.000+ views, 50.000+ visitors, 15.000+ downloads, 8.000+ github stars, 400+ forks, 7 days!
+#### 230.000+ views, 62.000+ visitors, 18.500+ downloads, 9.500+ github stars, 500+ forks, 14 days!
 
-And we still run with 1.000+ downloads... per day!
+And it still runs with 700+ git downloads... per day!
+
+**[Check what our users say about netdata](https://github.com/firehol/netdata/issues/148)**.
 
 Thank you!
 
@@ -30,13 +32,14 @@ It tries to visualize the **truth of now**, in its **greatest detail**, so that
 
 This is what you get:
 
-1. **Beautiful out of the box** bootstrap dashboards
-2. **Custom dashboards** that can be built using simple HTML (no javascript necessary)
-3. **Blazingly fast** and **super efficient**, written in C (for default installations, expect just 2% of a single core CPU usage and a few MB of RAM)
-3. **Zero configuration** - you just install it and it autodetects everything
-4. **Zero dependencies**, it is its own web server for its static web files and its web API
-4. **Extensible**, you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin - from BASH to node.js)
-7. **Embeddable**, it can run anywhere a Linux kernel runs
+- **Stunning bootstrap dashboards**, out of the box (themable: dark, light)
+- **Blazingly fast** and **super efficient**, mostly written in C (for default installations, expect just 2% of a single core CPU usage and a few MB of RAM)
+- **Zero configuration** - you just install it and it autodetects everything
+- **Zero dependencies**, it is its own web server for its static web files and its web API
+- **Zero maintenance**, you just run it, it does the rest
+- **Custom dashboards** that can be built using simple HTML (no javascript necessary)
+- **Extensible**, you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin - from BASH to node.js, so you can easily monitor any application, any API)
+- **Embeddable**, it can run anywhere a Linux kernel runs and its charts can be embedded on your web pages too
 
 ---
 
@@ -44,41 +47,59 @@ This is what you get:
 
 This is what it currently monitors (most with zero configuration):
 
-1. **CPU usage, interrupts, softirqs and frequency** (total and per core)
-2. **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
-3. **Disk I/O** (per disk: bandwidth, operations, backlog, utilization, etc)
+- **CPU usage, interrupts, softirqs and frequency** (total and per core)
+
+- **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
+
+- **Disks** (per disk: I/O, operations, backlog, utilization, etc)
 
    ![sda](https://cloud.githubusercontent.com/assets/2662304/14093195/c882bbf4-f554-11e5-8863-1788d643d2c0.gif)
 
-4. **Network interfaces** (per interface: bandwidth, packets, errors, drops, etc)
+- **Network interfaces** (per interface: bandwidth, packets, errors, drops, etc)
 
    ![dsl0](https://cloud.githubusercontent.com/assets/2662304/14093128/4d566494-f554-11e5-8ee4-5392e0ac51f0.gif)
 
-5. **IPv4 networking** (bandwidth, packets, errors, fragments, tcp: connections, packets, errors, handshake, udp: packets, errors, broadcast: bandwidth, packets, multicast: bandwidth, packets)
-6. **IPv6 networking** (bandwidth, packets, errors, fragments, ECT, udp: packets, errors, udplite: packets, errors, broadcast: bandwidth, multicast: bandwidth, packets, icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, break down by type)
-7. **netfilter / iptables Linux firewall** (connections, connection tracker events, errors, etc)
-8. **Processes** (running, blocked, forks, active, etc)
-9. **Entropy**
-10. **NFS file servers**, v2, v3, v4 (I/O, cache, read ahead, RPC calls)
-11. **Network QoS** (yes, the only tool that visualizes network `tc` classes in realtime)
+- **IPv4 networking** (bandwidth, packets, errors, fragments, tcp: connections, packets, errors, handshake, udp: packets, errors, broadcast: bandwidth, packets, multicast: bandwidth, packets)
 
-   ![qos-tc-classes](https://cloud.githubusercontent.com/assets/2662304/14093004/68966020-f553-11e5-98fe-ffee2086fafd.gif)
+- **IPv6 networking** (bandwidth, packets, errors, fragments, ECT, udp: packets, errors, udplite: packets, errors, broadcast: bandwidth, multicast: bandwidth, packets, icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, break down by type)
+
+- **netfilter / iptables Linux firewall** (connections, connection tracker events, errors, etc)
+
+- **Linux anti-DDoS protection** (SYNPROXY metrics)
 
+- **Processes** (running, blocked, forks, active, etc)
 
-12. **Applications**, by grouping the process tree (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
+- **Entropy** (random numbers pool, using in cryptography)
+
+- **NFS file servers**, v2, v3, v4 (I/O, cache, read ahead, RPC calls)
+
+- **Network QoS** (yes, the only tool that visualizes network `tc` classes in realtime)
+
+   ![qos-tc-classes](https://cloud.githubusercontent.com/assets/2662304/14093004/68966020-f553-11e5-98fe-ffee2086fafd.gif)
+
+- **Applications**, by grouping the process tree (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
 
    ![apps](https://cloud.githubusercontent.com/assets/2662304/14093565/67c4002c-f557-11e5-86bd-0154f5135def.gif)
 
-13. **Apache web server** mod-status (v2.2, v2.4)
-14. **Nginx web server** stub-status
-15. **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc)
-16. **ISC Bind name server** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics)
-17. **Postfix email server** message queue (entries, size)
-18. **Squid proxy server** (clients bandwidth and requests, servers bandwidth and requests) 
-19. **Hardware sensors** (temperature, voltage, fans, power, humidity, etc)
-20. **NUT UPSes** (load, charge, battery voltage, temperature, utility metrics, output metrics)
+- **Users and User Groups resource usage**, by summarizing the process tree per user and group (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
+
+- **Apache web server** mod-status (v2.2, v2.4)
 
-Any number of **SNMP devices** can be monitored, although you will need to configure these.
+- **Nginx web server** stub-status
+
+- **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc)
+
+- **ISC Bind name server** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics)
+
+- **Postfix email server** message queue (entries, size)
+
+- **Squid proxy server** (clients bandwidth and requests, servers bandwidth and requests) 
+
+- **Hardware sensors** (temperature, voltage, fans, power, humidity, etc)
+
+- **NUT UPSes** (load, charge, battery voltage, temperature, utility metrics, output metrics)
+
+- **SNMP devices** can be monitored too (although you will need to configure these)
 
 And you can extend it, by writing plugins that collect data from any source, using any computer language.
 
@@ -88,35 +109,26 @@ And you can extend it, by writing plugins that collect data from any source, usi
 
 Read **[Why netdata?](https://github.com/firehol/netdata/wiki/Why-netdata%3F)**
 
-Or **[check what our users say about netdata](https://github.com/firehol/netdata/issues/148)**.
-
 ---
 
 ## Installation
 
 Use our **[automatic installer](https://github.com/firehol/netdata/wiki/Installation)** to build and install it on your system
 
-It should run on any Linux system. We have tested it on:
+It should run on **any Linux** system. It has been tested on:
 
 - Gentoo
 - ArchLinux
 - Ubuntu / Debian
 - CentOS
 - Fedora
+- RedHat Enterprise Linux
+- SUSE
+- Alpine Linux
+- PLD Linux
 
 ---
 
 ## Documentation
 
 Check the **[netdata wiki](https://github.com/firehol/netdata/wiki)**.
-
-
-## Git sources
-
-You are looking at a version of the sources extracted directly from git.
-If you want a version of the source package where `configure` and any
-documentation has been built for you, please get an official
-[netdata package download](https://firehol.org/download/netdata/).
-The `unsigned/master` folder tracks the head of the git tree and
-released packages are also available.
-
index cf6397519d0e4c7fbd83890b91cf1a518f4b0ed3..995ee5d748cbfb9747f379b66ee8029d1d71f232 100644 (file)
 # group_name: process1 process2 process3 ...
 #
 # The process names are the same to the ones returned by: ps -e
+# or /proc/PID/stat
 #
-# If a group_name starts with a -, the dimension will be hidden (cpu chart only)
+# To add process names with spaces, enclose them in quotes (single or double)
+# example: 'Plex Media Serv' "my other process"
 #
+# Wildcard support:
+# You can add an asterisk (*) at the beginning and/or the end of a process name:
+#  *name    suffix mode: will search for processes ending with 'name' (/proc/PID/stat)
+#   name*   prefix mode: will search for processes beginning with 'name' (/proc/PID/stat)
+#  *name*   substring mode: will search for 'name' in the whole command line (/proc/PID/cmdline)
+#
+# If you enter even just one *name* (substring), apps.plugin will process
+# /proc/PID/cmdline for all processes, on every iteration.
+#
+# To add process names with single quotes, enclose them in double quotes
+# example: "process with this ' single quote"
+#
+# To add process names with double quotes, enclose them in single quotes:
+# example: 'process with this " double quote'
+#
+# If a group name starts with a -, the dimension will be hidden (cpu chart only)
+#
+# If any process name starts with a +, debugging will be enabled for it
+# (debugging produces a lot of output - do not enable it in production systems)
+#
+# You can add any number of groups you like. Only the ones found running will
+# affect the charts generated. However, producing charts with hundreds of
+# dimensions may slow down your web browser.
 
-compile: cc1 cc1plus as gcc ld make automake autoconf git
+compile: cc1 cc1plus as gcc* ld make automake autoconf git
 rsync: rsync
-media: mplayer vlc xine mediatomb omxplayer omxplayer.bin kodi kodi.bin xbmc xbmc.bin mediacenter eventlircd
-squid: squid squid2 squid3 c-icap
-apache: apache apache2
-mysql: mysqld mysql
+media: mplayer vlc xine mediatomb omxplayer* kodi* xbmc* mediacenter eventlircd
+squid: squid* c-icap
+apache: apache*
+mysql: mysql*
 asterisk: asterisk
-opensips: opensips opensips-mi-pro stund
-radius: radiusd radiusclient
-fail2ban: fail2ban-server
+opensips: opensips* stund
+radius: radius*
+fail2ban: fail2ban*
 mail: dovecot imapd pop3d
 postfix: master
 nginx: nginx
@@ -38,34 +63,34 @@ mongo: mongod
 lighttpd: lighttpd
 ftpd: proftpd in.tftpd
 samba: smbd nmbd winbindd
-nfs: rpcbind rpc.statd rpc.idmapd rpc.mountd nfsd4 nfsd4_callbacks nfsd nfsiod
-ssh: ssh sshd scp
+nfs: rpcbind rpc.* nfs*
+ssh: ssh* scp
 X: X lightdm xdm pulseaudio gkrellm
-xfce: xfwm4 xfdesktop xfce4-appfinder Thunar xfsettingsd xfce4-panel
-gnome: gnome-session gdm gconfd-2 gnome-terminal gnome-screensaver gnome-settings-daemon
+xfce: xfwm4 xfdesktop xfce* Thunar xfsettingsd
+gnome: gnome-* gdm gconfd-2
 named: named rncd
-clam: clamd freshclam
-cups: cupsd cups-browsed
-ntp: ntpq ntpd
-deluge: deluge deluged
-vbox: vboxwebsrv VBoxXPCOMIPCD VBoxSVC
-log: ulogd syslogd syslog-ng rsyslogd logrotate
-nms: snmpd vnstatd smokeping zabbix_agentd monit munin-node mon openhpid
-ppp: ppp pppd pptpd pptpctrl
+clam: clam* *clam
+cups: cups*
+ntp: ntp*
+deluge: deluge*
+vbox: vbox* VBox*
+log: ulogd syslog* rsyslog* logrotate
+nms: snmpd vnstatd smokeping zabbix* monit munin* mon openhpid
+ppp: ppp* pptp*
 inetd: inetd xinetd
 openvpn: openvpn
 cjdns: cjdroute
 cron: cron atd
 ha: corosync hs_logd ha_logd stonithd
-ipvs: ipvs_syncmaster ipvs_syncbackup
+ipvs: ipvs_*
 kernel: kthreadd kauditd lockd khelper kdevtmpfs khungtaskd rpciod fsnotify_mark kthrotld iscsi_eh deferwq
-netdata: netdata apps.plugin charts.d.plugin
+netdata: netdata
 crsproxy: crsproxy
 wifi: hostapd wpa_supplicant
-system: systemd-journal systemd-udevd systemd-logind udisks-glue udisks-daemon udevd udevd connmand ipv6_addrconf dbus-daemon
+system: systemd* udisks* udevd connmand ipv6_addrconf dbus-*
 ksmd: ksmd
-lxc: lxc-start
-zfs-spl: spl_kmem_cache spl_system_task spl_dynamic_tas 
-zfs-posix: z_import z_unmount z_rd_iss z_rd_int_ z_wr_iss z_wr_int z_fr_iss z_fr_int z_cl_iss z_ioctl_iss z_zvol z_iput
-zfs-txg: txg_quiesce txg_sync zil_clean
-zfs-arc: arc_prune arc_reclaim arc_user_evicts l2arc_feed 
+lxc: lxc*
+zfs-spl: spl_* 
+zfs-posix: z_*
+zfs-txg: txg_* zil_*
+zfs-arc: arc_* l2arc* 
index ab1550614fc629698933586d47979bc1403d50f6..1474bffdbaeaae7192fd9fbfe32e380bfb45ee3a 100644 (file)
@@ -14,8 +14,6 @@ PACKAGE_RPM_RELEASE="0.0.$(echo VERSION_SUFFIX | sed 's/^_//')"
 
 AC_INIT([netdata], VERSION_NUMBER[]VERSION_SUFFIX)
 
-AM_CONDITIONAL([GIT_TREE], [test -f README.md])
-
 AM_MAINTAINER_MODE([disable])
 if test x"$USE_MAINTAINER_MODE" = xyes; then
 AC_MSG_NOTICE(***************** MAINTAINER MODE *****************)
index 1a4b3ba7316796a7f5ad849b75db9690d7ad7bf5..7bcb908012ea369aabdb1e88b6d2493d24c34b1a 100644 (file)
@@ -48,6 +48,7 @@ CONFIG_CHECK="
 "
 
 : ${NETDATA_USER:=netdata}
+: ${NETDATA_GROUP:=netdata}
 
 pkg_setup() {
        linux-info_pkg_setup
@@ -72,9 +73,10 @@ src_configure() {
 src_install() {
        default
 
-       fowners ${NETDATA_USER} /var/log/netdata
+       fowners ${NETDATA_USER}:${NETDATA_GROUP} /var/log/netdata
+       fowners ${NETDATA_USER}:${NETDATA_GROUP} /var/cache/netdata
 
-       chown -Rc ${NETDATA_USER} "${ED}"/usr/share/${PN} || die
+       chown -Rc ${NETDATA_USER}:${NETDATA_GROUP} "${ED}"/usr/share/${PN} || die
 
        cat >> "${T}"/${PN}-sysctl <<- EOF
        kernel.mm.ksm.run = 1
index c67b72d698a849ac78b69ebc868557c2b5c97cf8..06a283df9a4f9be426730a562422bba0909fd4c8 100755 (executable)
@@ -1,5 +1,8 @@
 #!/bin/bash
 
+# reload the user profile
+[ -f /etc/profile ] && . /etc/profile
+
 LC_ALL=C
 
 # you can set CFLAGS before running installer
@@ -111,20 +114,109 @@ It will be installed at these locations:
   - plugins       at ${NETDATA_PREFIX}/usr/libexec/netdata
   - cache files   at ${NETDATA_PREFIX}/var/cache/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.
 
 BANNER
 
-if [ ! "${UID}" = 0 -o "$1" = "-h" -o "$1" = "--help" ]
+if [ "${UID}" -ne 0 ]
        then
-       echo >&2
-       echo >&2 "You have to run netdata as root."
-       echo >&2 "The netdata daemon will drop priviliges"
-       echo >&2 "but you have to start it as root."
-       echo >&2
-       exit 1
+       if [ -z "${NETDATA_PREFIX}" ]
+               then
+               cat <<NONROOTNOPREFIX
+
+Sorry! This will wrong!
+
+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:
+
+    $0 ${@} --install /tmp
+
+or, run the installer as root:
+
+    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.
+
+NONROOTNOPREFIX
+               exit 1
+
+       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.
+
+If you installing permanently on your system, run the
+installer like this:
+
+    sudo $0 ${@}
+
+NONROOT
+       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
+else
+       echo "No autotools found"
+fi
+
+if [ ! "$have_autotools" ]
+then
+       if [ -f configure ]
+       then
+               echo "Will skip autoreconf step"
+       else
+               cat <<-"EOF"
+
+       -------------------------------------------------------------------------------
+       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.
+
+       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/
+
+       The unsigned/master folder tracks the head of the git tree and released
+       packages are also available.
+       EOF
+               exit 1
+       fi
 fi
 
 if [ ${DONOTWAIT} -eq 0 ]
@@ -137,9 +229,6 @@ if [ ${DONOTWAIT} -eq 0 ]
        fi
 fi
 
-# reload the profile
-[ -f /etc/profile ] && . /etc/profile
-
 build_error() {
        cat <<EOF
 
@@ -155,9 +244,12 @@ You many need to check these:
 
    gcc make autoconf automake pkg-config
 
+   Autoconf version 2.60 or higher is required
+
 3. If your system cannot find ZLIB, although it is installed
    run me with the option:  --zlib-is-really-here
 
+
 If you still cannot get it to build, ask for help at github:
 
    https://github.com/firehol/netdata/issues
@@ -169,6 +261,7 @@ EOF
 }
 
 run() {
+       printf >&2 "\n"
        printf >&2 ":-----------------------------------------------------------------------------\n"
        printf >&2 "Running command:\n"
        printf >&2 "\n"
@@ -189,10 +282,11 @@ fi
 
 trap build_error EXIT
 
-echo >&2 "Running ./autogen.sh ..."
-run ./autogen.sh || exit 1
+if [ "$have_autotools" ]
+then
+       run ./autogen.sh || exit 1
+fi
 
-echo >&2 "Running ./configure ..."
 run ./configure \
        --prefix="${NETDATA_PREFIX}/usr" \
        --sysconfdir="${NETDATA_PREFIX}/etc" \
@@ -212,15 +306,35 @@ fi
 echo >&2 "Compiling netdata ..."
 run make || exit 1
 
+# backup user configurations
+for x in apps_groups.conf charts.d.conf
+do
+       if [ -f "${NETDATA_PREFIX}/etc/netdata/${x}" ]
+               then
+               cp -p "${NETDATA_PREFIX}/etc/netdata/${x}" "${NETDATA_PREFIX}/etc/netdata/${x}.installer_backup"
+       fi
+done
+
 echo >&2 "Installing netdata ..."
 run make install || exit 1
 
-echo >&2 "Adding netdata user group ..."
-getent group netdata > /dev/null || run groupadd -r netdata
+# restore user configurations
+for x in apps_groups.conf charts.d.conf
+do
+       if [ -f "${NETDATA_PREFIX}/etc/netdata/${x}.installer_backup" ]
+               then
+               cp -p "${NETDATA_PREFIX}/etc/netdata/${x}.installer_backup" "${NETDATA_PREFIX}/etc/netdata/${x}"
+       fi
+done
 
-echo >&2 "Adding netdata user account ..."
-getent passwd netdata > /dev/null || run useradd -r -g netdata -c netdata -s /sbin/nologin -d / netdata
+if [ ${UID} -eq 0 ]
+       then
+       echo >&2 "Adding netdata user group ..."
+       getent group netdata > /dev/null || run groupadd -r netdata
 
+       echo >&2 "Adding netdata user account ..."
+       getent passwd netdata > /dev/null || run useradd -r -g netdata -c netdata -s /sbin/nologin -d / netdata
+fi
 
 
 # -----------------------------------------------------------------------------
@@ -247,6 +361,9 @@ defuser="netdata"
 [ ! "${UID}" = "0" ] && defuser="${USER}"
 NETDATA_USER="$( config_option "run as user" "${defuser}" )"
 
+NETDATA_WEB_USER="$( config_option "web files owner" "${defuser}" )"
+NETDATA_WEB_GROUP="$( config_option "web files group" "${NETDATA_WEB_USER}" )"
+
 # debug flags
 defdebug=0
 NETDATA_DEBUG="$( config_option "debug flags" ${defdebug} )"
@@ -260,12 +377,19 @@ NETDATA_CACHE_DIR="$( config_option "cache directory" "${NETDATA_PREFIX}/var/cac
 NETDATA_WEB_DIR="$( config_option "web files directory" "${NETDATA_PREFIX}/usr/share/netdata/web" )"
 NETDATA_LOG_DIR="$( config_option "log directory" "${NETDATA_PREFIX}/var/log/netdata" )"
 NETDATA_CONF_DIR="$( config_option "config directory" "${NETDATA_PREFIX}/etc/netdata" )"
+NETDATA_RUN_DIR="${NETDATA_PREFIX}/var/run"
 
 
 # -----------------------------------------------------------------------------
 # prepare the directories
 
-echo >&2 "Fixing directory permissions for user ${NETDATA_USER}..."
+# this is needed if NETDATA_PREFIX is not empty
+if [ ! -d "${NETDATA_RUN_DIR}" ]
+       then
+       mkdir -p "${NETDATA_RUN_DIR}" || exit 1
+fi
+
+echo >&2 "Fixing directories (user: ${NETDATA_USER})..."
 for x in "${NETDATA_WEB_DIR}" "${NETDATA_CONF_DIR}" "${NETDATA_CACHE_DIR}" "${NETDATA_LOG_DIR}"
 do
        if [ ! -d "${x}" ]
@@ -273,14 +397,26 @@ do
                echo >&2 "Creating directory '${x}'"
                run mkdir -p "${x}" || exit 1
        fi
-       run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${x}" || echo >&2 "WARNING: Cannot change the ownership of the files in directory ${x} to ${NETDATA_USER}..."
+
+       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 0775 "${x}" || echo >&2 "WARNING: Cannot change the permissions of the directory ${x} to 0755..."
 done
 
-# 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"
-
+if [ ${UID} -eq 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
 
 # -----------------------------------------------------------------------------
 # check if we can re-start netdata
@@ -291,7 +427,11 @@ if [ ${DONOTSTART} -eq 1 ]
                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"
-               chown "${NETDATA_USER}" "${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."
@@ -313,7 +453,7 @@ ret=0
 count=0
 while [ $ret -eq 0 ]
 do
-       if [ $count -gt 15 ]
+       if [ $count -gt 30 ]
                then
                echo >&2 "Cannot stop the running netdata."
                exit 1
@@ -321,8 +461,9 @@ do
 
        count=$((count + 1))
 
-       pid=$(cat /var/run/netdata.pid 2>/dev/null)
+       pid=$(cat "${NETDATA_RUN_DIR}/netdata.pid" 2>/dev/null)
        # backwards compatibility
+       [ -z "${pid}" ] && pid=$(cat /var/run/netdata.pid 2>/dev/null)
        [ -z "${pid}" ] && pid=$(cat /var/run/netdata/netdata.pid 2>/dev/null)
        
        isnetdata $pid || pid=
@@ -338,13 +479,14 @@ do
        test $ret -eq 0 && printf >&2 "." && sleep 2
 done
 echo >&2
+echo >&2
 
 
 # -----------------------------------------------------------------------------
 # run netdata
 
 echo >&2 "Starting netdata..."
-run ${NETDATA_PREFIX}/usr/sbin/netdata -pidfile /var/run/netdata.pid "${@}"
+run ${NETDATA_PREFIX}/usr/sbin/netdata -pidfile ${NETDATA_RUN_DIR}/netdata.pid "${@}"
 
 if [ $? -ne 0 ]
        then
@@ -387,7 +529,10 @@ if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]
                mv "${NETDATA_PREFIX}/etc/netdata/netdata.conf.new" "${NETDATA_PREFIX}/etc/netdata/netdata.conf"
                echo >&2 "New configuration saved for you to edit at ${NETDATA_PREFIX}/etc/netdata/netdata.conf"
 
-               chown "${NETDATA_USER}" "${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'"
@@ -395,36 +540,14 @@ if [ ! -s "${NETDATA_PREFIX}/etc/netdata/netdata.conf" ]
        fi
 fi
 
-# -----------------------------------------------------------------------------
-
-cat <<END
-
-
--------------------------------------------------------------------------------
-
-ok. NetData is installed and is running.
-
-Hit http://localhost:${NETDATA_PORT}/ from your browser.
-
-To stop netdata, just kill it, with:
-
-  killall netdata
-
-To start it, just run it:
-
-  ${NETDATA_PREFIX}/usr/sbin/netdata
-
-Enjoy!
-
-END
-
 # -----------------------------------------------------------------------------
 # Check for KSM
 
 ksm_is_available_but_disabled() {
        cat <<KSM1
 
-INFORMATION:
+-------------------------------------------------------------------------------
+Memory de-duplication instructions
 
 I see you have kernel memory de-duper (called Kernel Same-page Merging,
 or KSM) available, but it is not currently enabled.
@@ -434,7 +557,7 @@ To enable it run:
 echo 1 >/sys/kernel/mm/ksm/run
 echo 1000 >/sys/kernel/mm/ksm/sleep_millisecs
 
-If you enable it, you will save 20-60% of netdata memory.
+If you enable it, you will save 40-60% of netdata memory.
 
 KSM1
 }
@@ -442,14 +565,15 @@ KSM1
 ksm_is_not_available() {
        cat <<KSM2
 
-INFORMATION:
+-------------------------------------------------------------------------------
+Memory de-duplication not present in your kernel
 
-I see you do not have kernel memory de-duper (called Kernel Same-page
+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
 
-If you can have it, you will save 20-60% of netdata memory.
+If you can have it, you will save 40-60% of netdata memory.
 
 KSM2
 }
@@ -471,20 +595,46 @@ if [ ! -s web/version.txt ]
        then
 cat <<VERMSG
 
-VERSION UPDATE CHECK:
+-------------------------------------------------------------------------------
+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:
 
 https://github.com/firehol/netdata/wiki/Installation
 
-
 VERMSG
 fi
 
+# -----------------------------------------------------------------------------
+# apps.plugin warning
+
+if [ "${UID}" -ne 0 ]
+       then
+cat <<SETUID_WARNING
+
+-------------------------------------------------------------------------------
+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
+these commands:
+
+       sudo chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+       sudo chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin"
+
+The commands allow apps.plugin to run as root.
+
+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
+
 # -----------------------------------------------------------------------------
 # Keep un-install info
 
@@ -556,4 +706,35 @@ fi
 UNINSTALL
 chmod 750 netdata-uninstaller.sh
 
+# -----------------------------------------------------------------------------
+
+cat <<END
+
+
+-------------------------------------------------------------------------------
+
+OK. NetData is installed and it is running.
+
+-------------------------------------------------------------------------------
+
+
+Hit http://localhost:${NETDATA_PORT}/ from your browser.
+
+To stop netdata, just kill it, with:
+
+  killall netdata
+
+To start it, just run it:
+
+  ${NETDATA_PREFIX}/usr/sbin/netdata
+
+
+Enjoy!
+
+                 Give netdata a Github Star, at:
+
+             https://github.com/firehol/netdata/wiki
+
+
+END
 echo >&2 "Uninstall script generated: ./netdata-uninstaller.sh"
index 507806ba8b3dd698257d6d027565ce4068cd9628..40655a52263d48ea1bab98aab2ffaf49a2dcaadd 100644 (file)
+#
+# Conditional build:
+%bcond_without systemd         # systemd
+%bcond_without nfacct          # build with nfacct plugin
+
 %if 0%{?fedora} || 0%{?rhel} >= 7
-%global do_systemd 1
 %else
-%global do_systemd 0
+%undefine      with_systemd
 %endif
 
 Summary:       Linux real time system monitoring, over the web
 Name:          @PACKAGE_NAME@
 Version:       @PACKAGE_RPM_VERSION@
 Release:       @PACKAGE_RPM_RELEASE@%{?release_suffix}%{?dist}
-License:       GPLv2+
-URL:           http://firehol.org
-Source:                %{name}-@PACKAGE_VERSION@.tar.bz2
-
-BuildRequires: libmnl-devel
+License:       GPL v3+
+Group:         Applications/System
+Source0:       http://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz
+URL:           http://netdata.firehol.org/
+BuildRequires: pkgconfig
+BuildRequires: xz
 BuildRequires: zlib-devel
-Requires:      libmnl
-Requires:      zlib
-%if !0%{?rhel}
+%if %{with nfacct}
+BuildRequires: libmnl-devel
 BuildRequires: libnetfilter_acct-devel
-Requires:      libnetfilter_acct
 %endif
-%if 0%{do_systemd}
+%if %{with systemd}
 BuildRequires:         systemd
 Requires(post):                systemd
 Requires(preun):       systemd
 Requires(postun):      systemd
 %endif
-
-BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRoot:     %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
 %description
-Real-time performance monitoring, in the greatest possible detail!
-
-%pre
-getent group netdata > /dev/null || groupadd -r netdata
-getent passwd netdata > /dev/null || useradd -r -g netdata -c netdata -s /sbin/nologin -d / netdata
-
-%if 0%{do_systemd}
-%post
-%systemd_post netdata.service
-%preun
-%systemd_preun netdata.service
-%postun
-%systemd_postun_with_restart netdata.service
-%endif
-
-%global ovirt_create_user_engine \
-%_ovirt_create_user %{engine_user} %{engine_uid} %{engine_group} %{engine_gid} "%{ovirt_user_description}" %{engine_state}
+netdata is the fastest way to visualize metrics. It is a resource
+efficient, highly optimized system for collecting and visualizing any
+type of realtime timeseries data, from CPU usage, disk activity, SQL
+queries, API calls, web site visitors, etc.
 
+netdata tries to visualize the truth of now, in its greatest detail,
+so that you can get insights of what is happening now and what just
+happened, on your systems and applications.
 
 %prep
 %setup -q -n %{name}-@PACKAGE_VERSION@
 
 %build
 %configure \
-       --docdir="%{_docdir}/%{name}-%{version}" \
+       --docdir=%{_docdir}/%{name}-%{version} \
        --with-zlib \
        --with-math \
-       --with-user=netdata \
-%if !0%{?rhel}
-       --enable-plugin-nfacct \
-%endif
-       %{?conf}
-make %{?_smp_mflags}
+       %{?with_nfacct:--enable-plugin-nfacct} \
+       --with-user=netdata
+%{__make} %{?_smp_mflags}
 
 %install
-rm -rf "%{buildroot}"
-make %{?_smp_mflags} install DESTDIR="%{buildroot}"
-find "%{buildroot}" -name .keep -exec rm {} \;
+rm -rf $RPM_BUILD_ROOT
+%{__make} %{?_smp_mflags} install \
+       DESTDIR=$RPM_BUILD_ROOT
 
-%if 0%{do_systemd}
-install -m 0755 -d "%{buildroot}/%{_unitdir}"
-install -m 0644 system/netdata.service "%{buildroot}/%{_unitdir}"
+install -m 644 -p system/netdata.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/*.conf
+
+find $RPM_BUILD_ROOT -name .keep | xargs rm
+
+%if %{with systemd}
+install -d $RPM_BUILD_ROOT%{_unitdir}
+install -m 644 -p system/netdata.service $RPM_BUILD_ROOT%{_unitdir}/netdata.service
+%endif
+
+%pre
+getent group netdata > /dev/null || groupadd -r netdata
+getent passwd netdata > /dev/null || useradd -r -g netdata -c netdata -s /sbin/nologin -d / netdata
+
+%if %{with systemd}
+%post
+%systemd_post netdata.service
+
+%preun
+%systemd_preun netdata.service
+
+%postun
+%systemd_postun_with_restart netdata.service
 %endif
 
+%clean
+rm -rf $RPM_BUILD_ROOT
+
 %files
-%attr(-, netdata, netdata) %dir %{_localstatedir}/cache/%{name}/
-%attr(-, netdata, netdata) %dir %{_localstatedir}/log/%{name}/
-%config(noreplace) %{_sysconfdir}/%{name}/
-%{_datadir}/%{name}/
-%{_libexecdir}/%{name}/
+%attr(-,netdata,netdata) %dir %{_localstatedir}/cache/%{name}
+%attr(-,netdata,netdata) %dir %{_localstatedir}/log/%{name}
+%config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/%{name}/*.conf
+%dir %{_sysconfdir}/%{name}
+%{?with_systemd:%{_unitdir}/netdata.service}
+%{_libexecdir}/%{name}
 %{_sbindir}/%{name}
+%dir %{_datadir}/%{name}
 
-%if 0%{do_systemd}
-%{_unitdir}/netdata.service
-%endif
+# override defattr for web files
+%defattr(755,root,netdata,644)
+%{_datadir}/%{name}/web
 
 %changelog
 * Tue Mar 22 2016 Costa Tsaousis <costa@tsaousis.gr> - 1.0.0-1
index 40c0356c8f3699bc2bc33dab307fdb82aaf35f4d..27b29470926657bed1ef33f6fe5fa2037a71920f 100755 (executable)
@@ -249,15 +249,26 @@ float2int() {
        # 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/*./}
 
-       # prepend a zero to the integer part
-       # if it is missing
+       # if the integer part is missing
+       # set it to zero
        [ -z "${a}" ] && a="0"
-       # strip leading zeros - base 10 convertion
+
+       # strip leading zeros from the integer part
+       # base 10 convertion
        a=$[10#$a]
 
        # check the length of the decimal part
@@ -266,13 +277,16 @@ float2int() {
                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 - base 10 convertion
+
+       # strip leading zeros from the decimal part
+       # base 10 convertion
        b=$[10#$b]
 
        # store the result
index 86f71712282bd307c7eb1d0cf64634958083228d..7b4739815481d2097bb1573500b8f92e8757a338 100755 (executable)
@@ -1,5 +1,7 @@
 #!/bin/bash
 
+export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
+
 # default time function
 now_ms=
 current_time_ms() {
index e0232361fc9407a2ae2a85d633a07c532a3a24e0..e8a6f43aefe55a0bcc77d714b0c89f831016b042 100644 (file)
 #include "common.h"
 #include "log.h"
 #include "procfile.h"
+#include "../config.h"
 
-#define MAX_COMPARE_NAME 15
+#define MAX_COMPARE_NAME 100
 #define MAX_NAME 100
+#define MAX_CMDLINE 1024
 
 unsigned long long Hertz = 1;
 
@@ -49,6 +51,7 @@ int debug = 0;
 
 int update_every = 1;
 unsigned long long file_counter = 0;
+int proc_pid_cmdline_is_needed = 0;
 
 char *host_prefix = "";
 char *config_dir = CONFIG_DIR;
@@ -208,25 +211,24 @@ char *strdup_debug(const char *file, int line, const char *function, const char
 
 #endif /* NETDATA_INTERNAL_CHECKS */
 
-
 // ----------------------------------------------------------------------------
 // system functions
 // to retrieve settings of the system
 
-procfile *ff = NULL;
-
 long get_system_cpus(void) {
+       procfile *ff = NULL;
+
        int processors = 0;
 
        char filename[FILENAME_MAX + 1];
        snprintf(filename, FILENAME_MAX, "%s/proc/stat", host_prefix);
 
-       ff = procfile_reopen(ff, filename, "", PROCFILE_FLAG_DEFAULT);
+       ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
        if(!ff) return 1;
 
        ff = procfile_readall(ff);
        if(!ff) {
-               // procfile_close(ff);
+               procfile_close(ff);
                return 1;
        }
 
@@ -239,28 +241,29 @@ long get_system_cpus(void) {
        processors--;
        if(processors < 1) processors = 1;
 
-       // procfile_close(ff);
+       procfile_close(ff);
        return processors;
 }
 
 long get_system_pid_max(void) {
+       procfile *ff = NULL;
        long mpid = 32768;
 
        char filename[FILENAME_MAX + 1];
        snprintf(filename, FILENAME_MAX, "%s/proc/sys/kernel/pid_max", host_prefix);
-       ff = procfile_reopen(ff, filename, "", PROCFILE_FLAG_DEFAULT);
+       ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
        if(!ff) return mpid;
 
        ff = procfile_readall(ff);
        if(!ff) {
-               // procfile_close(ff);
+               procfile_close(ff);
                return mpid;
        }
 
        mpid = atol(procfile_lineword(ff, 0, 0));
        if(!mpid) mpid = 32768;
 
-       // procfile_close(ff);
+       procfile_close(ff);
        return mpid;
 }
 
@@ -292,9 +295,12 @@ unsigned long long get_system_hertz(void)
 
 struct target {
        char compare[MAX_COMPARE_NAME + 1];
-       uint32_t hash;
+       uint32_t comparehash;
+       size_t comparelen;
 
        char id[MAX_NAME + 1];
+       uint32_t idhash;
+
        char name[MAX_NAME + 1];
 
        uid_t uid;
@@ -359,6 +365,9 @@ struct target {
        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;
@@ -389,9 +398,11 @@ struct target *get_users_target(uid_t uid)
        }
 
        snprintf(w->compare, MAX_COMPARE_NAME, "%d", uid);
-       w->hash = simple_hash(w->compare);
+       w->comparehash = simple_hash(w->compare);
+       w->comparelen = strlen(w->compare);
 
        snprintf(w->id, MAX_NAME, "%d", uid);
+       w->idhash = simple_hash(w->id);
 
        struct passwd *pw = getpwuid(uid);
        if(!pw)
@@ -399,6 +410,8 @@ struct target *get_users_target(uid_t uid)
        else
                snprintf(w->name, MAX_NAME, "%s", pw->pw_name);
 
+       netdata_fix_id(w->name);
+
        w->uid = uid;
 
        w->next = users_root_target;
@@ -423,9 +436,11 @@ struct target *get_groups_target(gid_t gid)
        }
 
        snprintf(w->compare, MAX_COMPARE_NAME, "%d", gid);
-       w->hash = simple_hash(w->compare);
+       w->comparehash = simple_hash(w->compare);
+       w->comparelen = strlen(w->compare);
 
        snprintf(w->id, MAX_NAME, "%d", gid);
+       w->idhash = simple_hash(w->id);
 
        struct group *gr = getgrgid(gid);
        if(!gr)
@@ -433,6 +448,8 @@ struct target *get_groups_target(gid_t gid)
        else
                snprintf(w->name, MAX_NAME, "%s", gr->gr_name);
 
+       netdata_fix_id(w->name);
+
        w->gid = gid;
 
        w->next = groups_root_target;
@@ -448,12 +465,22 @@ struct target *get_groups_target(gid_t gid)
 // 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;
-       if(nid[0] == '-') nid++;
+
+       while(nid[0] == '-' || nid[0] == '+' || nid[0] == '*') {
+               if(nid[0] == '-') thidden = 1;
+               if(nid[0] == '+') tdebug = 1;
+               if(nid[0] == '*') ends_with = 1;
+               nid++;
+       }
+       uint32_t hash = simple_hash(id);
 
        struct target *w;
-       for(w = apps_groups_root_target ; w ; w = w->next)
-               if(strncmp(nid, w->id, MAX_NAME) == 0) return w;
+       for(w = apps_groups_root_target ; w ; w = w->next) {
+               if(w->idhash == hash && strncmp(nid, w->id, MAX_NAME) == 0)
+                       return w;
+       }
 
        w = calloc(sizeof(struct target), 1);
        if(unlikely(!w)) {
@@ -462,19 +489,39 @@ struct target *get_apps_groups_target(const char *id, struct target *target)
        }
 
        strncpy(w->id, nid, MAX_NAME);
+       w->idhash = simple_hash(w->id);
+
        strncpy(w->name, nid, MAX_NAME);
+
        strncpy(w->compare, nid, MAX_COMPARE_NAME);
-       w->hash = simple_hash(w->compare);
+       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(id[0] == '-') w->hidden = 1;
+       if(w->starts_with && w->ends_with)
+               proc_pid_cmdline_is_needed = 1;
 
+       w->comparehash = simple_hash(w->compare);
+       w->comparelen = strlen(w->compare);
+
+       w->hidden = thidden;
+       w->debug = tdebug;
        w->target = target;
 
        w->next = apps_groups_root_target;
        apps_groups_root_target = w;
 
        if(unlikely(debug))
-               fprintf(stderr, "apps.plugin: adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");
+               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;
 }
@@ -482,7 +529,6 @@ struct target *get_apps_groups_target(const char *id, struct target *target)
 // read the apps_groups.conf file
 int read_apps_groups_conf(const char *name)
 {
-       char buffer[4096+1];
        char filename[FILENAME_MAX + 1];
 
        snprintf(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, name);
@@ -490,95 +536,77 @@ int read_apps_groups_conf(const char *name)
        if(unlikely(debug))
                fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
 
-       FILE *fp = fopen(filename, "r");
-       if(unlikely(!fp)) {
-               error("Cannot open file '%s'", filename);
-               return 1;
-       }
+       // ----------------------------------------
 
-       long line = 0;
-       while(fgets(buffer, 4096, fp) != NULL) {
-               int whidden = 0, wdebug = 0;
-               line++;
+       procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT);
+       if(!ff) return 1;
 
-               // if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", buffer);
+       procfile_set_quotes(ff, "'\"");
+
+       ff = procfile_readall(ff);
+       if(!ff) {
+               procfile_close(ff);
+               return 1;
+       }
 
-               char *s = buffer, *t, *p;
-               s = trim(s);
-               if(!s || !*s || *s == '#') continue;
+       unsigned long line, lines = procfile_lines(ff);
 
-               if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", s);
+       for(line = 0; line < lines ;line++) {
+               unsigned long word, words = procfile_linewords(ff, line);
+               struct target *w = NULL;
 
-               // the target name
-               t = strsep(&s, ":");
-               if(t) t = trim(t);
+               char *t = procfile_lineword(ff, line, 0);
                if(!t || !*t) continue;
 
-               while(t[0]) {
-                       int stop = 1;
+               for(word = 0; word < words ;word++) {
+                       char *s = procfile_lineword(ff, line, word);
+                       if(!s || !*s) continue;
+                       if(*s == '#') break;
 
-                       switch(t[0]) {
-                               case '-':
-                                       stop = 0;
-                                       whidden = 1;
-                                       t++;
-                                       break;
+                       if(t == s) continue;
 
-                               case '+':
-                                       stop = 0;
-                                       wdebug = 1;
-                                       t++;
-                                       break;
+                       struct target *n = get_apps_groups_target(s, w);
+                       if(!n) {
+                               error("Cannot create target '%s' (line %d, word %d)", s, line, word);
+                               continue;
                        }
 
-                       if(stop) break;
+                       if(!w) w = n;
                }
 
-               if(debug) fprintf(stderr, "apps.plugin: \t\ttarget %s\n", t);
-
-               struct target *w = NULL;
-               long count = 0;
-               int blen = 0;
-               char buffer[4097] = "";
-               buffer[4096] = '\0';
-
-               // the process names
-               while((p = strsep(&s, " "))) {
-                       p = trim(p);
-                       if(!p || !*p) continue;
-
-                       strncpy(&buffer[blen], p, 4096 - blen);
-                       blen = strlen(buffer);
-
-                       while(buffer[blen - 1] == '\\') {
-                               buffer[blen - 1] = ' ';
-
-                               if((p = strsep(&s, " ")))
-                                       p = trim(p);
+               if(w) {
+                       int tdebug = 0, thidden = 0;
 
-                               if(!p || !*p) p = " ";
-                               strncpy(&buffer[blen], p, 4096 - blen);
-                               blen = strlen(buffer);
+                       while(t[0] == '-' || t[0] == '+') {
+                               if(t[0] == '-') thidden = 1;
+                               if(t[0] == '+') tdebug = 1;
+                               t++;
                        }
 
-                       struct target *n = get_apps_groups_target(buffer, w);
-                       n->hidden = whidden;
-                       n->debug = wdebug;
-                       if(!w) w = n;
-
-                       buffer[0] = '\0';
-                       blen = 0;
-
-                       count++;
+                       strncpy(w->name, t, MAX_NAME);
+                       w->name[MAX_NAME] = '\0';
+                       w->hidden = thidden;
+                       w->debug = tdebug;
+
+                       if(unlikely(debug))
+                               fprintf(stderr, "apps.plugin: AGGREGATION TARGET NAME '%s' on ID '%s', process name '%s' (%s), aggregated on target '%s', options: %s %s\n"
+                                               , w->name
+                                               , w->id
+                                               , w->compare, (w->starts_with && w->ends_with)?"substring":((w->starts_with)?"prefix":((w->ends_with)?"suffix":"exact"))
+                                               , w->target?w->target->id:w->id
+                                               , (w->hidden)?"hidden":"-"
+                                               , (w->debug)?"debug":"-"
+                               );
                }
-
-               if(w) strncpy(w->name, t, MAX_NAME);
-               if(!count) error("The line %ld on file '%s', for group '%s' does not state any process names.", line, filename, t);
        }
-       fclose(fp);
 
-       apps_groups_default_target = get_apps_groups_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
-       strncpy(apps_groups_default_target->name, "other", MAX_NAME);
+       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
+               strncpy(apps_groups_default_target->name, "other", MAX_NAME);
 
        return 0;
 }
@@ -591,6 +619,8 @@ int read_apps_groups_conf(const char *name)
 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;
@@ -764,6 +794,33 @@ void del_pid_entry(pid_t pid)
 // ----------------------------------------------------------------------------
 // update pids from proc
 
+int read_proc_pid_cmdline(struct pid_stat *p) {
+       char filename[FILENAME_MAX + 1];
+       snprintf(filename, FILENAME_MAX, "%s/proc/%d/cmdline", host_prefix, p->pid);
+
+       int fd = open(filename, O_RDONLY, 0666);
+       if(unlikely(fd == -1)) return 1;
+
+       int i, bytes = read(fd, p->cmdline, MAX_CMDLINE);
+       close(fd);
+
+       if(bytes <= 0) {
+               // copy the command to the command line
+               strncpy(p->cmdline, p->comm, MAX_CMDLINE);
+               p->cmdline[MAX_CMDLINE] = '\0';
+               return 0;
+       }
+
+       p->cmdline[bytes] = '\0';
+       for(i = 0; i < bytes ; i++)
+               if(!p->cmdline[i]) p->cmdline[i] = ' ';
+
+       if(unlikely(debug))
+               fprintf(stderr, "Read file '%s' contents: %s\n", filename, p->cmdline);
+
+       return 0;
+}
+
 int read_proc_pid_ownership(struct pid_stat *p) {
        char filename[FILENAME_MAX + 1];
 
@@ -783,15 +840,22 @@ int read_proc_pid_ownership(struct pid_stat *p) {
 }
 
 int read_proc_pid_stat(struct pid_stat *p) {
+       static procfile *ff = NULL;
+
        char filename[FILENAME_MAX + 1];
 
        snprintf(filename, FILENAME_MAX, "%s/proc/%d/stat", host_prefix, p->pid);
 
        // ----------------------------------------
 
+       int set_quotes = (!ff)?1:0;
+
        ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
        if(!ff) return 1;
 
+       // if(set_quotes) procfile_set_quotes(ff, "()");
+       if(set_quotes) procfile_set_open_close(ff, "(", ")");
+
        ff = procfile_readall(ff);
        if(!ff) {
                // procfile_close(ff);
@@ -800,27 +864,10 @@ int read_proc_pid_stat(struct pid_stat *p) {
 
        file_counter++;
 
-       p->comm[0] = '\0';
-       p->comm[MAX_COMPARE_NAME] = '\0';
-       size_t blen = 0;
-
-       char *s = procfile_lineword(ff, 0, 1);
-       if(*s == '(') s++;
-       size_t len = strlen(s);
+       // parse the process name
        unsigned int i = 0;
-       while(len && s[len - 1] != ')') {
-               if(blen < MAX_COMPARE_NAME) {
-                       strncpy(&p->comm[blen], s, MAX_COMPARE_NAME - blen);
-                       blen = strlen(p->comm);
-               }
-
-               i++;
-               s = procfile_lineword(ff, 0, 1+i);
-               len = strlen(s);
-       }
-       if(len && s[len - 1] == ')') s[len - 1] = '\0';
-       if(blen < MAX_COMPARE_NAME)
-               strncpy(&p->comm[blen], s, MAX_COMPARE_NAME - blen);
+       strncpy(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
+       p->comm[MAX_COMPARE_NAME] = '\0';
 
        // p->pid                       = atol(procfile_lineword(ff, 0, 0+i));
        // comm is at 1
@@ -867,13 +914,16 @@ int read_proc_pid_stat(struct pid_stat *p) {
        // p->guest_time        = strtoull(procfile_lineword(ff, 0, 42+i), NULL, 10);
        // p->cguest_time       = strtoull(procfile_lineword(ff, 0, 43), NULL, 10);
 
-       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: VALUES: %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", p->comm, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
+       if(debug || (p->target && p->target->debug))
+               fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' VALUES: utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu, threads=%d\n", host_prefix, p->pid, p->comm, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads);
 
        // procfile_close(ff);
        return 0;
 }
 
 int read_proc_pid_statm(struct pid_stat *p) {
+       static procfile *ff = NULL;
+
        char filename[FILENAME_MAX + 1];
 
        snprintf(filename, FILENAME_MAX, "%s/proc/%d/statm", host_prefix, p->pid);
@@ -902,6 +952,8 @@ int read_proc_pid_statm(struct pid_stat *p) {
 }
 
 int read_proc_pid_io(struct pid_stat *p) {
+       static procfile *ff = NULL;
+
        char filename[FILENAME_MAX + 1];
 
        snprintf(filename, FILENAME_MAX, "%s/proc/%d/io", host_prefix, p->pid);
@@ -1250,8 +1302,6 @@ int read_pid_file_descriptors(struct pid_stat *p) {
 
 int collect_data_for_all_processes_from_proc(void)
 {
-       static long count_errors = 0;
-
        char dirname[FILENAME_MAX + 1];
 
        snprintf(dirname, FILENAME_MAX, "%s/proc", host_prefix);
@@ -1305,7 +1355,6 @@ int collect_data_for_all_processes_from_proc(void)
                // /proc/<pid>/stat
 
                if(unlikely(read_proc_pid_stat(p))) {
-                       if(!count_errors++ || debug || (p->target && p->target->debug))
                                error("Cannot process %s/proc/%d/stat", host_prefix, pid);
 
                        // there is no reason to proceed if we cannot get its status
@@ -1314,18 +1363,24 @@ int collect_data_for_all_processes_from_proc(void)
 
                // check its parent pid
                if(unlikely(p->ppid < 0 || p->ppid > pid_max)) {
-                       if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
                                error("Pid %d states invalid parent pid %d. Using 0.", pid, p->ppid);
 
                        p->ppid = 0;
                }
 
+               // --------------------------------------------------------------------
+               // /proc/<pid>/cmdline
+
+               if(proc_pid_cmdline_is_needed) {
+                       if(unlikely(read_proc_pid_cmdline(p))) {
+                                       error("Cannot process %s/proc/%d/cmdline", host_prefix, pid);
+                       }
+               }
 
                // --------------------------------------------------------------------
                // /proc/<pid>/statm
 
                if(unlikely(read_proc_pid_statm(p))) {
-                       if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
                                error("Cannot process %s/proc/%d/statm", host_prefix, pid);
 
                        // there is no reason to proceed if we cannot get its memory status
@@ -1337,7 +1392,6 @@ int collect_data_for_all_processes_from_proc(void)
                // /proc/<pid>/io
 
                if(unlikely(read_proc_pid_io(p))) {
-                       if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
                                error("Cannot process %s/proc/%d/io", host_prefix, pid);
 
                        // on systems without /proc/X/io
@@ -1349,7 +1403,6 @@ int collect_data_for_all_processes_from_proc(void)
                // <pid> ownership
 
                if(unlikely(read_proc_pid_ownership(p))) {
-                       if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
                                error("Cannot stat %s/proc/%d", host_prefix, pid);
                }
 
@@ -1361,12 +1414,22 @@ int collect_data_for_all_processes_from_proc(void)
                if(unlikely(p->new_entry)) {
                        if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
                        uint32_t hash = simple_hash(p->comm);
+                       size_t pclen = strlen(p->comm);
 
                        struct target *w;
                        for(w = apps_groups_root_target; w ; w = w->next) {
                                // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
 
-                               if(w->hash == hash && strcmp(w->compare, p->comm) == 0) {
+                               // 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;
 
@@ -1380,7 +1443,6 @@ int collect_data_for_all_processes_from_proc(void)
                // /proc/<pid>/fd
 
                if(unlikely(read_pid_file_descriptors(p))) {
-                       if(unlikely(!count_errors++ || debug || (p->target && p->target->debug)))
                                error("Cannot process entries in %s/proc/%d/fd", host_prefix, pid);
                }
 
@@ -1391,11 +1453,6 @@ int collect_data_for_all_processes_from_proc(void)
                p->updated = 1;
        }
 
-       if(unlikely(count_errors > 1000)) {
-               error("%ld more errors encountered\n", count_errors - 1);
-               count_errors = 0;
-       }
-
        closedir(dir);
 
        return 1;
@@ -2367,6 +2424,9 @@ int main(int argc, char **argv)
        // set the name for logging
        program_name = "apps.plugin";
 
+       // 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");
index e3c3afe3f421b3ac2199cbb80edeee2d85845776..cb74b6335c2d0dbd65d3af7dfea63b1cffb6e2a1 100644 (file)
 #include "log.h"
 #include "common.h"
 #include "appconfig.h"
+#include "../config.h"
 
 char *global_host_prefix = "";
 int enable_ksm = 1;
 
+unsigned char netdata_keys_map[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] = '_'  //
+};
+
+// make sure the supplied string
+// is good for a netdata chart/dimension ID/NAME
+void netdata_fix_id(char *s) {
+       while((*s = netdata_keys_map[(unsigned char)*s])) s++;
+}
+
 /*
 // http://stackoverflow.com/questions/7666509/hash-function-for-string
 uint32_t simple_hash(const char *name)
index 2d5ac59c09442133e29eafd7b8c3ff525eeb688f..e9987af72936eb1b08f9fd464b1ca84ee6922f3d 100644 (file)
@@ -15,6 +15,8 @@
 #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 void netdata_fix_id(char *s);
+
 extern uint32_t simple_hash(const char *name);
 extern void strreverse(char* begin, char* end);
 extern char *mystrsep(char **ptr, char *s);
index 2dd7b09c85ab6278e7b68b9e54fb95fec8da94ab..9dcf32f0ba6b7482c0840b29b405c4a43ba9d6aa 100644 (file)
@@ -82,9 +82,12 @@ int become_user(const char *username)
                return -1;
        }
 
-       if(pidfile[0] && getuid() != pw->pw_uid) {
+       uid_t uid = pw->pw_uid;
+       gid_t gid = pw->pw_gid;
+
+       if(pidfile[0] && getuid() != uid) {
                // we are dropping privileges
-               if(chown(pidfile, pw->pw_uid, pw->pw_gid) != 0)
+               if(chown(pidfile, uid, gid) != 0)
                        error("Cannot chown pidfile '%s' to user '%s'", pidfile, username);
 
                else if(pidfd != -1) {
@@ -99,30 +102,30 @@ int become_user(const char *username)
                pidfd = -1;
        }
 
-       if(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
-               error("Cannot switch to user's %s group (gid: %d).", username, pw->pw_gid);
+       if(setresgid(gid, gid, gid) != 0) {
+               error("Cannot switch to user's %s group (gid: %d).", username, gid);
                return -1;
        }
 
-       if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
-               error("Cannot switch to user %s (uid: %d).", username, pw->pw_uid);
+       if(setresuid(uid, uid, uid) != 0) {
+               error("Cannot switch to user %s (uid: %d).", username, uid);
                return -1;
        }
 
-       if(setgid(pw->pw_gid) != 0) {
-               error("Cannot switch to user's %s group (gid: %d).", username, pw->pw_gid);
+       if(setgid(gid) != 0) {
+               error("Cannot switch to user's %s group (gid: %d).", username, gid);
                return -1;
        }
-       if(setegid(pw->pw_gid) != 0) {
-               error("Cannot effectively switch to user's %s group (gid: %d).", username, pw->pw_gid);
+       if(setegid(gid) != 0) {
+               error("Cannot effectively switch to user's %s group (gid: %d).", username, gid);
                return -1;
        }
-       if(setuid(pw->pw_uid) != 0) {
-               error("Cannot switch to user %s (uid: %d).", username, pw->pw_uid);
+       if(setuid(uid) != 0) {
+               error("Cannot switch to user %s (uid: %d).", username, uid);
                return -1;
        }
-       if(seteuid(pw->pw_uid) != 0) {
-               error("Cannot effectively switch to user %s (uid: %d).", username, pw->pw_uid);
+       if(seteuid(uid) != 0) {
+               error("Cannot effectively switch to user %s (uid: %d).", username, uid);
                return -1;
        }
 
@@ -306,6 +309,7 @@ int become_daemon(int dont_fork, int close_all_files, const char *user, const ch
                        // don't close it, we might need it at exit
                        // close(pidfd);
                }
+               else error("Failed to open pidfile '%s'.", pidfile);
        }
 
        if(user && *user) {
index 02cdae2a8a7a1e9bf6bca9f46badc015c6cec7c9..e5e0ad2c49a8d48e22225f8680d7a737e4bed5d2 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -27,6 +27,85 @@ int access_log_syslog = 1;
 int error_log_syslog = 1;
 int output_log_syslog = 1;     // debug log
 
+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;
+}
+
 void log_date(FILE *out)
 {
                char outstr[200];
@@ -64,6 +143,9 @@ void info_int( const char *file, const char *function, const unsigned long line,
 {
        va_list args;
 
+       // prevent logging too much
+       if(error_log_limit(0)) return;
+
        log_date(stderr);
 
        va_start( args, fmt );
@@ -85,6 +167,9 @@ void error_int( const char *prefix, const char *file, const char *function, cons
 {
        va_list args;
 
+       // prevent logging too much
+       if(error_log_limit(0)) return;
+
        log_date(stderr);
 
        va_start( args, fmt );
index 08f3c4fe3f5cd68911edc92cf2f5140ab1d82e8b..e882af3861d79b39cd00bfb1c2269f9afa35fbe1 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -42,6 +42,12 @@ extern int access_log_syslog;
 extern int error_log_syslog;
 extern int output_log_syslog;
 
+extern time_t error_log_throttle_period;
+extern unsigned long error_log_errors_per_period;
+extern int error_log_limit(int reset);
+
+#define error_log_limit_reset() do { error_log_limit(1); } while(0)
+
 #define debug(type, args...) do { if(unlikely(!silent && (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)
index 4005516f85a84b81ed2ae7f0bf6d94571953f091..ad24debfaba419a648130074076f70b5e49771b6 100644 (file)
@@ -36,6 +36,7 @@
 #include "plugin_nfacct.h"
 
 #include "main.h"
+#include "../config.h"
 
 int netdata_exit = 0;
 
@@ -45,6 +46,9 @@ void netdata_cleanup_and_exit(int ret)
        rrdset_save_all();
        // kill_childs();
 
+       // let it log a few more error messages
+       error_log_limit_reset();
+
        if(pidfd != -1) {
                if(ftruncate(pidfd, 0) != 0)
                        error("Cannot truncate pidfile '%s'.", pidfile);
@@ -344,6 +348,12 @@ int main(int argc, char **argv)
                }
                else error_log_syslog = 0;
 
+               error_log_throttle_period = config_get_number("global", "errors flood protection period", error_log_throttle_period);
+               setenv("NETDATA_ERRORS_THROTTLE_PERIOD", config_get("global", "errors flood protection period"    , ""), 1);
+
+               error_log_errors_per_period = config_get_number("global", "errors to trigger flood protection", error_log_errors_per_period);
+               setenv("NETDATA_ERRORS_PER_PERIOD"     , config_get("global", "errors to trigger flood protection", ""), 1);
+
                // --------------------------------------------------------------------
 
                access_log_file = config_get("global", "access log", LOG_DIR "/access.log");
@@ -420,8 +430,13 @@ int main(int argc, char **argv)
 
                // --------------------------------------------------------------------
 
+               // 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:"");
-               web_files_uid();
+
+               // IMPORTANT: these have to run once, while single threaded
+               web_files_uid(); // IMPORTANT: web_files_uid() before web_files_gid()
+               web_files_gid();
 
                // --------------------------------------------------------------------
 
index 4a5d3e4f8ba55e0d180fe528963f68f7b7fef333..2c7a55ceea910c4c50e249baba5a9ff09a3d2753 100644 (file)
@@ -15,6 +15,7 @@
 #include "popen.h"
 #include "plugin_tc.h"
 #include "main.h"
+#include "../config.h"
 
 #define RRD_TYPE_TC                                    "tc"
 #define RRD_TYPE_TC_LEN                                strlen(RRD_TYPE_TC)
@@ -717,7 +718,8 @@ void *tc_main(void *ptr)
                        //      debug(D_TC_LOOP, "IGNORED line");
                        //}
                }
-               mypclose(fp, tc_child_pid);
+               // fgets() failed or loop broke
+               int code = mypclose(fp, tc_child_pid);
                tc_child_pid = 0;
 
                if(device) {
@@ -732,10 +734,19 @@ void *tc_main(void *ptr)
                        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 93dd29d6e2d5bbf7b584456305be13bb7072d9c0..b8524d99c77210404a6397e25aa6c1aacdbf9589 100644 (file)
@@ -16,6 +16,7 @@
 #include "rrd.h"
 #include "popen.h"
 #include "plugins_d.h"
+#include "../config.h"
 
 struct plugind *pluginsd_root = NULL;
 
index 9dd9a9a74a47e86b53d85c88fbb9b1ecfa10f488..30351322d46e251cc0e75d550855cd16a0900cea 100644 (file)
@@ -123,6 +123,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                        case 199: // veritas
                        case 201: // veritas
                        case 251: // dm
+                       case 253: // virtio
                                def_enabled = enable_new_disks;
                                break;
 
@@ -195,7 +196,6 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                        case 135: // scsi
                        case 153: // raid
                        case 202: // xen
-                       case 253: // virtio
                        case 256: // flash
                        case 257: // flash
                        case 259: // nvme0n1 issue #119
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index 7a48579593caceb6fdfccbab710f359acc5d1142..1fc33ef5f26625afe3acc4cb6db2e55791859011 100644 (file)
@@ -20,6 +20,7 @@
 #include "common.h"
 #include "log.h"
 #include "procfile.h"
+#include "../config.h"
 
 #define PF_PREFIX "PROCFILE"
 
@@ -142,6 +143,9 @@ void pflines_free(pflines *fl) {
 #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);
@@ -156,17 +160,82 @@ void procfile_close(procfile *ff) {
 procfile *procfile_parser(procfile *ff) {
        debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename);
 
-       char *s = ff->data, *e = ff->data, *t = ff->data;
+       char *s = ff->data, *e = &ff->data[ff->len], *t = ff->data, quote = 0;
        uint32_t l = 0, w = 0;
-       e += ff->len;
+       int opened = 0;
 
        ff->lines = pflines_add(ff->lines, w);
        if(unlikely(!ff->lines)) goto cleanup;
 
        while(likely(s < e)) {
+               // we are not at the end
+
                switch(ff->separators[(int)(*s)]) {
+                       case PF_CHAR_IS_OPEN:
+                               if(s == t) {
+                                       opened++;
+                                       t = ++s;
+                               }
+                               else if(opened) {
+                                       opened++;
+                                       s++;
+                               }
+                               else
+                                       s++;
+                               continue;
+
+                       case PF_CHAR_IS_CLOSE:
+                               if(opened) {
+                                       opened--;
+
+                                       if(!opened) {
+                                               *s = '\0';
+                                               ff->words = pfwords_add(ff->words, t);
+                                               if(unlikely(!ff->words)) goto cleanup;
+
+                                               ff->lines->lines[l].words++;
+                                               w++;
+
+                                               t = ++s;
+                                       }
+                                       else
+                                               s++;
+                               }
+                               else
+                                       s++;
+                               continue;
+
+                       case PF_CHAR_IS_QUOTE:
+                               if(unlikely(!quote && s == t)) {
+                                       // quote opened at the beginning
+                                       quote = *s;
+                                       t = ++s;
+                               }
+                               else if(unlikely(quote && quote == *s)) {
+                                       // quote closed
+                                       quote = 0;
+
+                                       *s = '\0';
+                                       ff->words = pfwords_add(ff->words, t);
+                                       if(unlikely(!ff->words)) goto cleanup;
+
+                                       ff->lines->lines[l].words++;
+                                       w++;
+
+                                       t = ++s;
+                               }
+                               else
+                                       s++;
+                               continue;
+
                        case PF_CHAR_IS_SEPARATOR:
-                               if(likely(s == t)) {
+                               if(unlikely(quote || opened)) {
+                                       // we are inside a quote
+                                       s++;
+                                       continue;
+                               }
+
+                               if(unlikely(s == t)) {
                                        // skip all leading white spaces
                                        t = ++s;
                                        continue;
@@ -209,9 +278,10 @@ procfile *procfile_parser(procfile *ff) {
                }
        }
 
-       if(likely(s != t)) {
+       if(likely(s > t && t < e)) {
                // the last word
-               if(likely(ff->len < ff->size)) *s = '\0';
+               if(likely(ff->len < ff->size))
+                       *s = '\0';
                else {
                        // we are going to loose the last byte
                        ff->data[ff->size - 1] = '\0';
@@ -309,10 +379,53 @@ static void procfile_set_separators(procfile *ff, const char *separators) {
        while(likely(ffd != ffe)) *ffs++ = *ffd++;
 
        // set the separators
-       if(unlikely(!separators)) separators = " \t=|";
+       if(unlikely(!separators))
+               separators = " \t=|";
+
        ffs = ff->separators;
        const char *s = separators;
-       while(likely(*s)) ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
+       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;
+}
+
+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;
 }
 
 procfile *procfile_open(const char *filename, const char *separators, uint32_t flags) {
index ce2f9bc927276fb09a141e6b32e5daa0df121404..122e153f1eb7c76bc320fa98ca90b38afaf8d34e 100644 (file)
@@ -86,6 +86,9 @@ extern procfile *procfile_reopen(procfile *ff, const char *filename, const char
 // example walk-through a procfile parsed file
 extern void procfile_print(procfile *ff);
 
+extern void procfile_set_quotes(procfile *ff, const char *quotes);
+extern void procfile_set_open_close(procfile *ff, const char *open, const char *close);
+
 // ----------------------------------------------------------------------------
 
 // set this to 1, to have procfile adapt its initial buffer allocation to the max allocation used so far
index 2dce02e669741df8de4d1a31d4a923fe13a15b0d..86ff396877910b98d9afd510a97af922e83b6752 100644 (file)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -682,6 +682,9 @@ void rrdset_save_all(void)
 {
        debug(D_RRD_CALLS, "rrdset_save_all()");
 
+       // let it log a few error messages
+       error_log_limit_reset();
+
        RRDSET *st;
        RRDDIM *rd;
 
index ad453cb687f957aa35a2a99f1928cc2af4bbc07b..88a75044339327f91f8d12ec80ab32aa3b95ea8d 100644 (file)
@@ -936,7 +936,7 @@ static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startlin
                        // 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, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+                       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)) {
index 639bb044cc2d2693cdba7c9f0b7c19c0228acf8e..6500a59b2e7446f59d7c323f9ebe9bbbe594a056 100644 (file)
@@ -28,6 +28,7 @@
 #include "rrd2json.h"
 
 #include "web_client.h"
+#include "../config.h"
 
 #define INITIAL_WEB_DATA_LENGTH 16384
 #define WEB_REQUEST_LENGTH 16384
@@ -232,10 +233,13 @@ uid_t web_files_uid(void)
        static uid_t owner_uid = 0;
 
        if(unlikely(!web_owner)) {
-               web_owner = config_get("global", "web files owner", NETDATA_USER);
+               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);
@@ -257,10 +261,13 @@ gid_t web_files_gid(void)
        static gid_t owner_gid = 0;
 
        if(unlikely(!web_group)) {
-               web_group = config_get("global", "web files group", config_get("global", "web files owner", NETDATA_USER));
+               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);
@@ -781,36 +788,53 @@ int web_client_api_request_v1(struct web_client *w, char *url)
 {
        // get the command
        char *tok = mystrsep(&url, "/?&");
-       debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
-
-       if(strcmp(tok, "data") == 0)
-               return web_client_api_request_v1_data(w, url);
-       else if(strcmp(tok, "chart") == 0)
-               return web_client_api_request_v1_chart(w, url);
-       else if(strcmp(tok, "charts") == 0)
-               return web_client_api_request_v1_charts(w, url);
-
-       buffer_flush(w->response.data);
-       buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
-       return 404;
+       if(tok && *tok) {
+               debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
+
+               if(strcmp(tok, "data") == 0)
+                       return web_client_api_request_v1_data(w, url);
+               else if(strcmp(tok, "chart") == 0)
+                       return web_client_api_request_v1_chart(w, url);
+               else if(strcmp(tok, "charts") == 0)
+                       return web_client_api_request_v1_charts(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, "/?&");
-       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);
-
-       buffer_flush(w->response.data);
-       buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
-       return 404;
+       if(tok && *tok) {
+               debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+               if(strcmp(tok, "v1") == 0)
+                       return web_client_api_request_v1(w, url);
+               else {
+                       buffer_flush(w->response.data);
+                       buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
+                       return 404;
+               }
+       }
+       else {
+               buffer_flush(w->response.data);
+               buffer_sprintf(w->response.data, "Which API version?");
+               return 400;
+       }
 }
 
 int web_client_data_request(struct web_client *w, char *url, int datasource_type)
 {
+       RRDSET *st = NULL;
+
        char *args = strchr(url, '?');
        if(args) {
                *args='\0';
@@ -819,11 +843,14 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
 
        // get the name of the data to show
        char *tok = mystrsep(&url, "/");
-       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(tok && *tok) {
+               debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+               st = rrdset_find_byname(tok);
+               if(!st) st = rrdset_find(tok);
+       }
+
        if(!st) {
                // we don't have it
                // try to send a file with that name
@@ -850,34 +877,36 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
        if(url) {
                // parse the group count required
                tok = mystrsep(&url, "/");
-               if(tok) group_count = atoi(tok);
+               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(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(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) after = strtoul(tok, NULL, 10);
+               if(tok && *tok) after = strtoul(tok, NULL, 10);
                if(after < 0) after = 0;
        }
        if(url) {
                // parse before time
                tok = mystrsep(&url, "/");
-               if(tok) before = strtoul(tok, NULL, 10);
+               if(tok && *tok) before = strtoul(tok, NULL, 10);
                if(before < 0) before = 0;
        }
        if(url) {
                // parse nonzero
                tok = mystrsep(&url, "/");
-               if(tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
+               if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
        }
 
        w->response.data->contenttype = CT_APPLICATION_JSON;
@@ -896,9 +925,9 @@ int web_client_data_request(struct web_client *w, char *url, int datasource_type
 
                while(args) {
                        tok = mystrsep(&args, "&");
-                       if(tok) {
+                       if(tok && *tok) {
                                char *name = mystrsep(&tok, "=");
-                               if(name && strcmp(name, "tqx") == 0) {
+                               if(name && *name && strcmp(name, "tqx") == 0) {
                                        char *key = mystrsep(&tok, ":");
                                        char *value = mystrsep(&tok, ";");
                                        if(key && value && *key && *value) {
@@ -1090,118 +1119,142 @@ void web_client_process(struct web_client *w) {
                        w->last_url[URL_MAX] = '\0';
 
                        tok = mystrsep(&url, "/?");
+                       if(tok && *tok) {
+                               debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
 
-                       debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
-
-                       if(strcmp(tok, "api") == 0) {
-                               // the client is requesting api access
-                               datasource_type = DATASOURCE_JSON;
-                               code = web_client_api_request(w, url);
-                       }
-#ifdef NETDATA_INTERNAL_CHECKS
-                       else if(strcmp(tok, "exit") == 0) {
-                               netdata_exit = 1;
-                               code = 200;
-                               w->response.data->contenttype = CT_TEXT_PLAIN;
-                               buffer_flush(w->response.data);
-                               buffer_strcat(w->response.data, "will do");
-                       }
-#endif
-                       else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
-                               // the client is requesting rrd data
-                               datasource_type = DATASOURCE_JSON;
-                               code = web_client_data_request(w, url, datasource_type);
-                       }
-                       else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
-                               // the client is requesting google datasource
-                               code = web_client_data_request(w, url, datasource_type);
-                       }
-                       else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
-                               // the client is requesting an rrd graph
-
-                               // get the name of the data to show
-                               tok = mystrsep(&url, "/?&");
-                               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);
+                               if(strcmp(tok, "api") == 0) {
+                                       // the client is requesting api access
+                                       datasource_type = DATASOURCE_JSON;
+                                       code = web_client_api_request(w, url);
                                }
-                               else {
+#ifdef NETDATA_INTERNAL_CHECKS
+                               else if(strcmp(tok, "exit") == 0) {
+                                       netdata_exit = 1;
                                        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;
+                                       w->response.data->contenttype = CT_TEXT_PLAIN;
                                        buffer_flush(w->response.data);
-                                       rrd_stats_graph_json(st, url, w->response.data);
+                                       buffer_strcat(w->response.data, "will do");
+                               }
+#endif
+                               else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
+                                       // the client is requesting rrd data
+                                       datasource_type = DATASOURCE_JSON;
+                                       code = web_client_data_request(w, url, datasource_type);
+                               }
+                               else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
+                                       // the client is requesting google datasource
+                                       code = web_client_data_request(w, url, datasource_type);
+                               }
+                               else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
+                                       // the client is requesting an rrd graph
+
+                                       // 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");
+                                       }
                                }
-                       }
 #ifdef NETDATA_INTERNAL_CHECKS
-                       else if(strcmp(tok, "debug") == 0) {
-                               buffer_flush(w->response.data);
+                               else if(strcmp(tok, "debug") == 0) {
+                                       buffer_flush(w->response.data);
 
-                               // get the name of the data to show
-                               tok = mystrsep(&url, "/?&");
-                               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);
+                                       // 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?0:1;
+                                                       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 {
+                               else if(strcmp(tok, "mirror") == 0) {
                                        code = 200;
-                                       debug_flags |= D_RRD_STATS;
-                                       st->debug = st->debug?0:1;
-                                       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 if(strcmp(tok, "mirror") == 0) {
-                               code = 200;
 
-                               debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
 
-                               // replace the zero bytes with spaces
-                               buffer_char_replace(w->response.data, '\0', ' ');
+                                       // 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
-                       }
+                                       // just leave the buffer as is
+                                       // it will be copied back to the client
+                               }
 #endif
-                       else if(strcmp(tok, "list") == 0) {
-                               code = 200;
+                               else if(strcmp(tok, "list") == 0) {
+                                       code = 200;
 
-                               debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
+                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
 
-                               buffer_flush(w->response.data);
-                               RRDSET *st = rrdset_root;
+                                       buffer_flush(w->response.data);
+                                       RRDSET *st = rrdset_root;
 
-                               for ( ; st ; st = st->next )
-                                       buffer_sprintf(w->response.data, "%s\n", st->name);
-                       }
-                       else if(strcmp(tok, "all.json") == 0) {
-                               code = 200;
-                               debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
+                                       for ( ; st ; st = st->next )
+                                               buffer_sprintf(w->response.data, "%s\n", st->name);
+                               }
+                               else if(strcmp(tok, "all.json") == 0) {
+                                       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);
-                       }
-                       else if(strcmp(tok, "netdata.conf") == 0) {
-                               code = 200;
-                               debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
+                                       w->response.data->contenttype = CT_APPLICATION_JSON;
+                                       buffer_flush(w->response.data);
+                                       rrd_stats_all_json(w->response.data);
+                               }
+                               else if(strcmp(tok, "netdata.conf") == 0) {
+                                       code = 200;
+                                       debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
 
-                               w->response.data->contenttype = CT_TEXT_PLAIN;
-                               buffer_flush(w->response.data);
-                               generate_config(w->response.data, 0);
+                                       w->response.data->contenttype = CT_TEXT_PLAIN;
+                                       buffer_flush(w->response.data);
+                                       generate_config(w->response.data, 0);
+                               }
+                               else {
+                                       char filename[FILENAME_MAX+1];
+                                       url = filename;
+                                       strncpy(filename, w->last_url, FILENAME_MAX);
+                                       filename[FILENAME_MAX] = '\0';
+                                       tok = mystrsep(&url, "?");
+                                       buffer_flush(w->response.data);
+                                       code = mysendfile(w, (tok && *tok)?tok:"/");
+                               }
                        }
                        else {
                                char filename[FILENAME_MAX+1];
@@ -1224,7 +1277,10 @@ void web_client_process(struct web_client *w) {
                }
 
                // free url_decode() buffer
-               if(pointer_to_free) free(pointer_to_free);
+               if(pointer_to_free) {
+                       free(pointer_to_free);
+                       pointer_to_free = NULL;
+               }
        }
        else if(w->response.data->len > TOO_BIG_REQUEST) {
                strcpy(w->last_url, "too big request");
index 14cd91bc6b84cd0af985bc9f5c268418a58f18bf..3823dbc9120c094ac87e3d77bb4cf6b5a812b1e5 100644 (file)
@@ -81,6 +81,7 @@ struct web_client {
 extern struct web_client *web_clients;
 
 extern uid_t web_files_uid(void);
+extern uid_t web_files_gid(void);
 
 extern struct web_client *web_client_create(int listener);
 extern struct web_client *web_client_free(struct web_client *w);
index ab3e4357a6c8ac6d7b8bfd028fca0a65e511b8f9..10bf39a783428ef4fba1c088d3a9ca6f886abee2 100644 (file)
@@ -24,6 +24,7 @@
 #include "global_statistics.h"
 #include "rrd.h"
 #include "rrd2json.h"
+#include "../config.h"
 
 int listen_backlog = LISTEN_BACKLOG;
 
index e20cdd74c7bf90f00e6d79f79c979e8790e68d8a..f16a720e2d2bdbfe4ff8179a121a3afe657b5e0f 100644 (file)
@@ -25,4 +25,5 @@ dist_noinst_DATA = \
        netdata.logrotate.in \
        netdata.service.in \
        netdata-init-d.in \
+       netdata.conf \
        $(NULL)
index 8c46433693d9a091f6ef1d2e4d0a149bd5654b57..c317d10211679e46243771e357b1803076b432da 100755 (executable)
@@ -13,17 +13,23 @@ STOP_TIMEOUT="10"
 
 service_start()
 {
-       printf "%-50s" "Starting $DAEMON..."
+       echo "Starting $DAEMON..."
        daemon $DAEMON_PATH/$DAEMON $DAEMONOPTS
+       RETVAL=$?
        echo
+       return $RETVAL
 }
 
 service_stop()
 {
        printf "%-50s" "Stopping $DAEMON..."
        killproc -p ${PIDFILE} -d ${STOP_TIMEOUT} $DAEMON
-       rm -f ${PIDFILE}
+       RETVAL=$?
+       if [ $RETVAL -eq 0 ]; then
+               rm -f ${PIDFILE}
+       fi
        echo
+       return $RETVAL
 }
 
 service_status()
index 06bb7ffb20dcd9e7fde31c86cddb4125ea1251af..299db5699ffe7bc624579e833a9943136cbecf8f 100755 (executable)
 : ${NETDATA_START_AFTER_SERVICES:=apache2 squid nginx mysql named opensips upsd hostapd postfix lm_sensors}
 
 extra_started_commands="getconf"
-pidfile="/run/netdata/netdata.pid"
+pidfile="/run/netdata.pid"
 command="${NETDATA_INSTALL_PATH}/usr/sbin/netdata"
 command_background="yes"
 command_args="-pidfile ${pidfile} ${NETDATA_EXTRA_ARGS}"
-start_stop_daemon_args="-u ${NETDATA_OWNER}"
+# start_stop_daemon_args="-u ${NETDATA_OWNER}"
+start_stop_daemon_args=""
 
 depend() {
        use logger
diff --git a/system/netdata.conf b/system/netdata.conf
new file mode 100644 (file)
index 0000000..df7ea1a
--- /dev/null
@@ -0,0 +1,11 @@
+# NetData Configuration
+#
+# To see defaults, grab one from your instance:
+# http://localhost:19999/netdata.conf
+
+# global netdata configuration
+
+[global]
+       run as user = netdata
+       web files owner = root
+       web files group = netdata
index 91e45a7463b81b916fbf0f3833edbcfb0e918c48..bc26cc9dcc83aec3df4adcf5e062af9f098cde3f 100644 (file)
@@ -1,5 +1,5 @@
 [Unit]
-Description=netdata
+Description=Linux real time system monitoring, over the web
 After=network.target httpd.service squid.service nfs-server.service mysqld.service named.service postfix.service
 
 [Service]
@@ -7,8 +7,8 @@ Type=forking
 WorkingDirectory=/tmp
 User=root
 Group=root
-PIDFile=@localstatedir_POST@/run/netdata/netdata.pid
-ExecStart=@sbindir_POST@/netdata -pidfile @localstatedir_POST@/run/netdata/netdata.pid
+PIDFile=@localstatedir_POST@/run/netdata.pid
+ExecStart=@sbindir_POST@/netdata -pidfile @localstatedir_POST@/run/netdata.pid
 ExecStop=/bin/kill -SIGTERM $MAINPID
 TimeoutStopSec=30
 
index b162ae1b09df195653ba77f7725a105bd1a176f1..1b6b918be97052567cdec7501ce86a6c602ba512 100644 (file)
@@ -4,75 +4,75 @@
 MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
 
 dist_web_DATA = \
-        robots.txt \
-        index.html \
+       robots.txt \
+       index.html \
        demo.html \
        demo2.html \
        tv.html \
-        dashboard.html \
-        dashboard.js \
-        dashboard.css \
-        dashboard.slate.css \
+       dashboard.html \
+       dashboard.js \
+       dashboard.css \
+       dashboard.slate.css \
        favicon.ico \
        netdata-swagger.yaml \
        netdata-swagger.json \
-        version.txt \
-        $(NULL)
+       version.txt \
+       $(NULL)
 
 webolddir=$(webdir)/old
 dist_webold_DATA = \
-        old/datasource.html \
-        old/index.html \
-        old/index.js \
-        old/netdata.js \
-        old/theme.css \
-        $(NULL)
+       old/datasource.html \
+       old/index.html \
+       old/index.js \
+       old/netdata.js \
+       old/theme.css \
+       $(NULL)
 
 weblibdir=$(webdir)/lib
 dist_weblib_DATA = \
-        lib/dygraph-combined.js \
-        lib/dygraph-smooth-plotter.js \
-        lib/jquery-1.12.0.min.js \
-        lib/jquery.peity.min.js \
-        lib/jquery.sparkline.min.js \
-        lib/morris.min.js \
-        lib/raphael-min.js \
+       lib/dygraph-combined.js \
+       lib/dygraph-smooth-plotter.js \
+       lib/jquery-1.12.0.min.js \
+       lib/jquery.peity.min.js \
+       lib/jquery.sparkline.min.js \
+       lib/morris.min.js \
+       lib/raphael-min.js \
        lib/jquery.easypiechart.min.js \
        lib/jquery.nanoscroller.min.js \
-        lib/bootstrap.min.js \
+       lib/bootstrap.min.js \
        lib/ElementQueries.js \
        lib/ResizeSensor.js \
-        lib/bootstrap-toggle.min.js \
-        lib/c3.min.js \
-        lib/d3.min.js \
-        lib/gauge.min.js \
-        $(NULL)
+       lib/bootstrap-toggle.min.js \
+       lib/c3.min.js \
+       lib/d3.min.js \
+       lib/gauge.min.js \
+       $(NULL)
 
 webcssdir=$(webdir)/css
 dist_webcss_DATA = \
-        css/morris.css \
-        css/bootstrap.min.css \
-        css/bootstrap-theme.min.css \
-        css/bootstrap.slate.min.css \
+       css/morris.css \
+       css/bootstrap.min.css \
+       css/bootstrap-theme.min.css \
+       css/bootstrap.slate.min.css \
        css/font-awesome.min.css \
-        css/bootstrap-toggle.min.css \
-        css/c3.min.css \
-        $(NULL)
+       css/bootstrap-toggle.min.css \
+       css/c3.min.css \
+       $(NULL)
 
 webfontsdir=$(webdir)/fonts
 dist_webfonts_DATA = \
-        fonts/glyphicons-halflings-regular.eot \
-        fonts/glyphicons-halflings-regular.svg \
-        fonts/glyphicons-halflings-regular.ttf \
-        fonts/glyphicons-halflings-regular.woff \
-        fonts/glyphicons-halflings-regular.woff2 \
+       fonts/glyphicons-halflings-regular.eot \
+       fonts/glyphicons-halflings-regular.svg \
+       fonts/glyphicons-halflings-regular.ttf \
+       fonts/glyphicons-halflings-regular.woff \
+       fonts/glyphicons-halflings-regular.woff2 \
        fonts/FontAwesome.otf \
        fonts/fontawesome-webfont.eot \
        fonts/fontawesome-webfont.svg \
        fonts/fontawesome-webfont.ttf \
        fonts/fontawesome-webfont.woff \
        fonts/fontawesome-webfont.woff2 \
-        $(NULL)
+       $(NULL)
 
 webimagesdir=$(webdir)/images
 dist_webimages_DATA = \
@@ -88,19 +88,13 @@ dist_webimages_DATA = \
        images/seo-performance-512.png \
        images/seo-performance-multi-size.ico \
        images/seo-performance-multi-size.icns \
-        $(NULL)
+       $(NULL)
 
 version.txt:
        if test -d "$(top_srcdir)/.git"; then \
-               if git --git-dir="$(top_srcdir)/.git" log -n 1 > v.tmp; then \
-                       grep ^commit\  v.tmp | cut -d" " -f2 > version.txt; \
-                       rm -f v.tmp; \
-               else \
-                       rm -f v.tmp; \
-                       echo "0" > version.txt; \
-               fi; \
-       else \
-               echo "0" > version.txt; \
-       fi
+               git --git-dir="$(top_srcdir)/.git" log -n 1 --format=%H; \
+       fi > $@.tmp
+       test -s $@.tmp || echo 0 > $@.tmp
+       mv $@.tmp $@
 
 .PHONY: version.txt
index 1135b39a7db4664ee4d009a006a1e31953818879..a7b090d665c186e110057f5e8b3b74c8d5afc27d 100644 (file)
@@ -66,6 +66,43 @@ body {
        margin: 0px;
 }
 
+.netdata-legend-toolbox {
+       display: block;
+       position: absolute;
+       bottom: 0px;
+       right: 30px;
+       height: 15px;
+       width: 110px;
+       background-color: White;
+       font-size: 12px;
+       vertical-align: middle;
+       line-height: 15px;
+       color: #DDDDDD;
+       text-align: center;
+       overflow: hidden;
+       z-index: 20;
+       padding: 0px;
+       margin: 0px;
+}
+
+.netdata-legend-toolbox-button {
+       display: inline-block;
+       position: relative;
+       height: 15px;
+       width: 18px;
+       background-color: White;
+       font-size: 12px;
+       vertical-align: middle;
+       line-height: 15px;
+       color: #CDCDCD;
+       text-align: center;
+       overflow: hidden;
+       z-index: 21;
+       padding: 0px;
+       margin: 0px;
+       cursor: pointer;
+}
+
 .netdata-message {
        display: inline-block;
        text-align: left;
@@ -453,3 +490,11 @@ body {
        color: #999999;
        font-weight: normal;
 }
+
+.popover-title {
+       font-weight: bold;
+       font-size: 12px;
+}
+.popover-content {
+       font-size: 11px;
+}
index c9725d87c84665620d83d9de3c6a207b51d7fc85..b6c62ae3c914ffa81bab6583c11c1735281b9420 100644 (file)
@@ -11,6 +11,7 @@
 // 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
 //
 // 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
                after: -600,                                    // panning
                pixels_per_point: 1,                    // the detail of the chart
                fill_luminance: 0.8                             // luminance of colors in solit areas
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // global options
 
                        destroy_on_hide: false,         // destroy charts when they are not visible
 
+                       show_help: true,                        // 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
                        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() { ; }
                },
 
                        libraries:                      false,
                        dygraph:                        false
                }
-       }
+       };
 
 
        // ----------------------------------------------------------------------------------------------------------------
 
                NETDATA.localStorage.current[key.toString()] = ret;
                return ret;
-       }
+       };
 
        NETDATA.localStorageSet = function(key, value, callback) {
                if(typeof value === 'undefined' || value === 'undefined') {
 
                NETDATA.localStorage.current[key.toString()] = value;
                return value;
-       }
+       };
 
        NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
                for(var i in obj) {
 
                        obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
                }
-       }
+       };
 
        NETDATA.setOption = function(key, value) {
                if(key.toString() === '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);
                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 }
+               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 }
        };
        NETDATA.errorLast = {
                code: 0,
 
                console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
 
-               if(NETDATA.errorCodes[code].alert)
+               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;
                                cache: false
                        })
                        .done(function(data) {
-                               var h = NETDATA.chartRegistry.fixid(host);
-                               //console.log('downloaded all charts from ' + host + ' (' + h + ')');
-                               self.charts[h] = data.charts;
+                               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);
                        });
 
                        return true;
                }
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // dimensions selection
                this.value_div = null;
                this.color = NETDATA.themes.current.foreground;
 
-               if(parent.selected === parent.unselected)
+               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;
 
                this.enabled = true;
                this.setHandler();
-       }
+       };
 
        dimensionStatus.prototype.setHandler = function() {
                if(this.enabled === false) return;
 
                        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);
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
 
                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.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;
                        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();
                }
 
                return ret;
-       }
+       };
 
 
        // ----------------------------------------------------------------------------------------------------------------
                 * show an error instead of the chart
                 */
                var error = function(msg) {
-                       that.element.innerHTML = that.id + ': ' + msg;
-                       that.enabled = false;
-                       that.current = that.pan;
-               }
+                       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();
                        that.tm.last_dom_created = new Date().getTime();
 
                        showLoading();
-               }
+               };
 
                /* init() private
-                * initialize state viariables
+                * initialize state variables
                 * destroy all (possibly) created state elements
                 * create the basic DOM for a chart
                 */
                                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
                        createDOM();
 
                        that.setMode('auto');
-               }
+               };
 
                var maxMessageFontSize = function() {
                        // normally we want a font size, as tall as the element
                        // 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;
-                       this.element_message.style.fontSize = 'x-small';
+                       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;
                                icon = '<i class="fa fa-area-chart"></i>';
 
                        showMessageIcon(icon + ' netdata');
-               }
+               };
 
                var showLoading = function() {
                        if(that.chart_created === false) {
                                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() {
                        }
 
                        that.___chartIsHidden___ = true;
-               }
+               };
 
                // unhide the chart, when it is visible - called from isVisible()
                var unhideChart = function() {
                                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) {
                        }
 
                        return true;
-               }
+               };
 
                // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
                var callChartLibraryCreateSafely = function(data) {
                        that.chart_created = true;
                        that.updates_since_last_creation = 0;
                        return true;
-               }
+               };
 
                // ----------------------------------------------------------------------------------------------------------------
                // Chart Resize
 
                                that.tm.last_resized = new Date().getTime();
                        }
-               }
+               };
 
                // this is the actual chart resize algorithm
                // it will:
                                                NETDATA.options.auto_refresher_stop_until = 0;
                                        };
                        }
-               }
+               };
 
 
                var noDataToShow = function() {
                        //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;
                        this.current.force_after_ms = null;
 
                        this.tm.last_mode_switch = new Date().getTime();
-               }
+               };
 
                // ----------------------------------------------------------------------------------------------------------------
                // global selection sync
                                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() {
                                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() {
                        }
 
                        // this.globalSelectionSyncDelay(100);
-               }
+               };
 
                // can the chart participate to the global selection sync as a slave?
                this.globalSelectionSyncIsEligible = function() {
                                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
                        $.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
                                st.setSelection(t);
                        });
-               }
+               };
 
                // stop syncing all charts to the given time
                this.globalSelectionSyncStop = function() {
                        }
 
                        this.clearSelection();
-               }
+               };
 
                this.setSelection = function(t) {
                        if(typeof this.library.setSelection === 'function') {
                                this.log('selection set to ' + t.toString());
 
                        return this.selected;
-               }
+               };
 
                this.clearSelection = function() {
                        if(this.selected === true) {
                        }
 
                        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) {
 
                                this.paused = true;
                        }
-               }
+               };
 
                this.unpauseChart = function() {
                        if(this.paused === true) {
 
                                this.paused = false;
                        }
-               }
+               };
 
                this.resetChart = function(dont_clear_master, dont_update) {
                        if(this.debug === true)
                        if(dont_update !== true && this.isVisible() === true) {
                                this.updateChart();
                        }
-               }
+               };
 
                this.updateChartPanOrZoom = function(after, before) {
                        var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
                                this.log(logme);
 
                        if(before < after) {
-                               this.log(logme + 'flipped parameters, rejecting it.');
+                               if(this.debug === true)
+                                       this.log(logme + 'flipped parameters, rejecting it.');
+
                                return false;
                        }
 
                        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(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(series.value !== null) series.value.innerHTML = s;
                        if(series.user !== null) series.user.innerHTML = r;
-               }
+               };
 
                this.legendSetDate = function(ms) {
                        if(typeof ms !== 'number') {
 
                        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.legendSetLabelValue(label, null);
                                }
                        }
-               }
+               };
 
                this.legendShowLatestValues = function() {
                        if(this.chart === null) return;
                                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) {
 
                        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);
                                        var added = 0;
 
                                        while(added < 20) {
-                                               for(var i = 0, len = c.length; i < len ; i++) {
+                                               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(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
+                       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;
                                if(user_id !== null) {
                                        user_element = document.getElementById(user_id) || null;
                                        if(user_element === null)
-                                               me.log('Cannot find element with id: ' + user_id);
+                                               state.log('Cannot find element with id: ' + user_id);
                                }
 
                                state.element_legend_childs.series[name] = {
                                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'),
 
                                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 =
 
                                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,
                                $(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
 
                        this.legendShowLatestValues();
-               }
+               };
 
                this.hasLegend = function() {
                        if(typeof this.___hasLegendCache___ !== 'undefined')
 
                        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
                                px = NETDATA.options.current.pixels_per_point;
 
                        return px;
-               }
+               };
 
                this.needsRecreation = function() {
                        return (
                                        && this.library.autoresize() === false
                                        && this.tm.last_resized < NETDATA.options.last_resized
                                );
-               }
+               };
 
                this.chartURL = function() {
                        var after, before, points_multiplier = 1;
 
                        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)
 
                        if(this.refresh_dt_element !== null)
                                this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
-               }
+               };
 
                this.updateChart = function(callback) {
                        if(this.debug === true)
                                error('data download failed for url: ' + that.data_url);
                        })
                        .always(function() {
-                               this._updating = false;
+                               that._updating = false;
                                if(typeof callback === 'function') callback();
                        });
 
                        return true;
-               }
+               };
 
                this.isVisible = function(nocache) {
                        if(typeof nocache === 'undefined')
                                this.___isVisible___ = true;
                                return this.___isVisible___;
                        }
-               }
+               };
 
                this.isAutoRefreshed = function() {
                        return (this.current.autorefresh);
-               }
+               };
 
                this.canBeAutoRefreshed = function() {
-                       now = new Date().getTime();
+                       var now = new Date().getTime();
 
                        if(this.enabled === false) {
                                if(this.debug === true)
                        }
 
                        return false;
-               }
+               };
 
                this.autoRefresh = function(callback) {
                        if(this.canBeAutoRefreshed() === true) {
                                if(typeof callback !== 'undefined')
                                        callback();
                        }
-               }
+               };
 
                this._defaultsFromDownloadedChart = function(chart) {
                        this.chart = chart;
 
                        if(this.units === null)
                                this.units = chart.units;
-               }
+               };
 
                // fetch the chart description from the netdata server
                this.getChart = function(callback) {
                                        if(typeof callback === 'function') callback();
                                });
                        }
-               }
+               };
 
                // ============================================================================================================
                // INITIALIZATION
 
                init();
-       }
+       };
 
        NETDATA.resetAllCharts = function(state) {
                // first clear the global selection sync
 
                // 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 autorefreshing
+               // 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) {
                        $(element).data('netdata-state-object', state);
                }
                return state;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // Library functions
                }
                else if(typeof callback === "function")
                        callback();
-       }
+       };
 
        NETDATA._loadCSS = function(filename) {
                // don't use jQuery here
 
                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")
                        g: parseInt(result[2], 16),
                        b: parseInt(result[3], 16)
                } : null;
-       }
+       };
 
        NETDATA.colorLuminance = function(hex, lum) {
                // validate hex string
                }
 
                return rgb;
-       }
+       };
 
        NETDATA.guid = function() {
                function s4() {
                        }
 
                        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;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
 
                                }, NETDATA.options.current.idle_between_charts);
                        }
                }
-       }
+       };
 
        // this is part of the parallel refresher
        // its cause is to refresh sequencially all the charts
        // it will call the parallel refresher back
        // as soon as it sees a chart that its chart library
        // is initialized
-       NETDATA.chartRefresher_unitialized = function() {
+       NETDATA.chartRefresher_uninitialized = function() {
                if(NETDATA.options.updated_dom === true) {
                        // the dom has been updated
                        // get the dom parts again
                        if(state.library.initialized === true)
                                NETDATA.chartRefresher();
                        else
-                               state.autoRefresh(NETDATA.chartRefresher_unitialized);
+                               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_unitialized()
+       // 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());
                }
-       }
+       };
 
        NETDATA.parseDom = function(callback) {
                NETDATA.options.last_page_scroll = new Date().getTime();
                }
 
                if(typeof callback === 'function') callback();
-       }
+       };
 
        // this is the main function - where everything starts
        NETDATA.start = function() {
                $('.modal').on('shown.bs.modal', NETDATA.onscroll);
 
                NETDATA.parseDom(NETDATA.chartRefresher);
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // peity
 
                $(state.peity_instance).peity('line', state.peity_options);
                return true;
-       }
+       };
 
        NETDATA.peityChartCreate = function(state, data) {
                state.peity_instance = document.createElement('div');
 
                NETDATA.peityChartUpdate(state, data);
                return true;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // sparkline
 
                $(state.element_chart).sparkline(data.result, state.sparkline_options);
                return true;
-       }
+       };
 
        NETDATA.sparklineChartCreate = function(state, data) {
                var self = $(state.element);
                var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
                var xvalues = self.data('sparkline-xvalues') || undefined;
                var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
-               var xvalues = self.data('sparkline-xvalues') || undefined;
                var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
                var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
                var disableInteraction = self.data('sparkline-disableinteraction') || false;
                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(typeof callback === "function")
                                callback();
                });
-       }
+       };
 
        NETDATA.dygraphInitialize = function(callback) {
                if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
                }
 
                return pcent;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // easy-pie-chart
                        state.easyPieChartEvent = {
                                timer: null,
                                value: 0,
-                               pcent: 0,
+                               pcent: 0
                        };
                }
 
 
                state.gauge_instance.animationSpeed = speed;
                state.___gaugeOld__.speed = speed;
-       }
+       };
 
        NETDATA.gaugeSet = function(state, value, min, max) {
                if(typeof value !== 'number') value = 0;
 
        NETDATA.gaugeChartCreate = function(state, data) {
                var self = $(state.element);
-               var chart = $(state.element_chart);
+               // var chart = $(state.element_chart);
 
                var value = data.result[0];
                var max = self.data('gauge-max-value') || null;
                        },
                        setSelection: NETDATA.dygraphSetSelection,
                        clearSelection:  NETDATA.dygraphClearSelection,
+                       toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'json'; },
                        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'; },
                        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'; },
                        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'; },
                        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'; },
                        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'; },
                        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'; },
                        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'; },
                        resize: null,
                        setSelection: NETDATA.easypiechartSetSelection,
                        clearSelection: NETDATA.easypiechartClearSelection,
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                        resize: null,
                        setSelection: NETDATA.gaugeSetSelection,
                        clearSelection: NETDATA.gaugeClearSelection,
+                       toolboxPanAndZoom: null,
                        initialized: false,
                        enabled: true,
                        format: function(state) { return 'array'; },
                NETDATA.chartLibraries[library].url = url;
                NETDATA.chartLibraries[library].initialized = true;
                NETDATA.chartLibraries[library].enabled = true;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // Start up
                .fail(function() {
                        alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
                })
-       }
+       };
 
        NETDATA.loadRequiredCSS = function(index) {
                if(index >= NETDATA.requiredCSS.length)
 
                NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
                NETDATA.loadRequiredCSS(++index);
-       }
+       };
 
        NETDATA.errorReset();
        NETDATA.loadRequiredCSS(0);
index 8a3adf321814271c8e5df14ddb6501029f515a93..662731061b55cb04d91f65a3f9e7c584f434b94d 100644 (file)
@@ -74,6 +74,43 @@ body {
        margin: 0px;
 }
 
+.netdata-legend-toolbox {
+       display: block;
+       position: absolute;
+       bottom: 0px;
+       right: 30px;
+       height: 15px;
+       width: 110px;
+       background-color: #272b30;
+       font-size: 12px;
+       vertical-align: middle;
+       line-height: 15px;
+       color: #373b40;
+       text-align: center;
+       overflow: hidden;
+       z-index: 20;
+       padding: 0px;
+       margin: 0px;
+}
+
+.netdata-legend-toolbox-button {
+       display: inline-block;
+       position: relative;
+       height: 15px;
+       width: 18px;
+       background-color: #272b30;
+       font-size: 12px;
+       vertical-align: middle;
+       line-height: 15px;
+       color: #474b50;
+       text-align: center;
+       overflow: hidden;
+       z-index: 21;
+       padding: 0px;
+       margin: 0px;
+       cursor: pointer;
+}
+
 .netdata-message {
        display: inline-block;
        text-align: left;
@@ -465,3 +502,11 @@ body {
        color: #676b70;
        font-weight: normal;
 }
+
+.popover-title {
+       font-weight: bold;
+       font-size: 12px;
+}
+.popover-content {
+       font-size: 11px;
+}
index f4afaad2d389a31321ec4c2213df834b803eeed7..546196b87d443a59118a424ec9bec910d8f44880 100644 (file)
@@ -28,7 +28,7 @@
 
        <style>
 
-       /* prevent body from hidding under the navbar */
+       /* prevent body from hiding under the navbar */
        body {
                padding-top: 50px;
        }
        </script>
 
        <!-- load the dashboard manager - it will do the rest -->
-       <script type="text/javascript" src="dashboard.js?v26"></script>
+       <script type="text/javascript" src="dashboard.js?v32"></script>
 </head>
 
 <body data-spy="scroll" data-target="#sidebar">
                                </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 realtime timeseries data, from CPU usage, disk activity, SQL queries, API calls, web site visitors, etc.
+                                               <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.
                                                                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 transfered from the netdata server, to refresh the chart with possibly more detail.</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>Legend Operations</h4>
-                                                               Click on the label or value of a dimension, will select / unselect this dimension.
+                                                               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 / unselect multiple dimensions.
+                                                               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">
                                                        <hr/>
                                                        <div class="p">
                                                                <h4>Legend Operations</h4>
-                                                               Tap on the label or value of a dimension, will select / unselect this dimension.
+                                                               Tap on the label or value of a dimension, will select / un-select this dimension.
                                                        </div>
                                                </div>
                                        </div>
                                                                                <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 transfered from the netdata server (except if all dimensions of the chart are zero, in which case this setting does nothing - all dimensions are transfered 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 transfered 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>
+                                                                                       <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">
                                                                <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="Sequencial" data-width="110px"></td>
+                                                                               <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>sequencial</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 sequencial if you work on an older/slower computer.</small>
+                                                                                       <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">
                                                                        <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>.</small>
+                                                                                       <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">
                                                                        <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.</small>
+                                                                                       <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>
 
 <script>
 
-var demo_hostname = 'netdata.firehol.org';
-// var demo_hostname = 'box';
+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')
+                       this_is_demo = document.location.hostname.endsWith('.firehol.org');
+       }
+       catch(error) {
+               ;
+       }
+
+       return this_is_demo;
+}
 
-if(document.location.hostname === demo_hostname) {
+if(isdemo()) {
        document.getElementById('masthead').style.display = 'block';
 }
 
@@ -935,19 +962,19 @@ var menuData = {
 
        '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 internaly 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).<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning childs continiously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited childs of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealisting utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
+               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).<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning children continuously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited children of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealistic utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
                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.<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning childs continiously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited childs of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealisting utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
+               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.<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning children continuously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited children of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealistic utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
                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.<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning childs continiously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited childs of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealisting utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
+               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.<br/><b>IMPORTANT</b>: The values shown here are not 100% accurate. They only include values for the processes running. If an application is spawning children continuously, which are terminated in just a few milliseconds (like shell scripts do), the values reported will be inaccurate. Linux does report the values for the exited children of a process. However, these values are reported to the parent process <b>only when the child exits</b>. If these values, of the exited child processes, were also aggregated in the charts below, the charts would have been full of spikes, presenting unrealistic utilization for each process group. So, we decided to ignore these values and present only the utilization of <b>the currently running processes</b>.',
                height: 1.5
        },
 
@@ -965,12 +992,12 @@ var menuData = {
 var submenuData = {
        'mem.ksm': {
                title: 'Memory Deduper',
-               info: 'Kernel Same-page Merging (KSM) is the kernel memory de-duper.'
+               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: 'The following information is taken 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.'
+               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': {
@@ -979,8 +1006,8 @@ var submenuData = {
        },
 
        'netfilter.synproxy': {
-               title: 'Anti-DDoS Protection',
-               info: 'The following information is taken 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.'
+               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.'
        }
 };
 
@@ -1043,7 +1070,7 @@ var chartData = {
 
        '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 realtime environments, where CPU jitter can affect the quality of the service (like VoIP media gateways).'
+               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': {
@@ -1781,6 +1808,21 @@ function notifyForUpdate(force) {
 
 // ----------------------------------------------------------------------------
 
+function getUrlParameter(sParam) {
+    var sPageURL = decodeURIComponent(window.location.search.substring(1)),
+        sURLVariables = sPageURL.split('&'),
+        sParameterName,
+        i;
+
+    for (i = 0; i < sURLVariables.length; i++) {
+        sParameterName = sURLVariables[i].split('=');
+
+        if (sParameterName[0] === sParam) {
+            return sParameterName[1] === undefined ? true : sParameterName[1];
+        }
+    }
+}
+
 function finalizePage() {
        // resize all charts - without starting the background thread
        // this has to be done while NETDATA is paused
@@ -1788,6 +1830,13 @@ function finalizePage() {
        // the Dom elements are initially zero-sized
        NETDATA.parseDom();
 
+       var before = 0, after = 0;
+       after = getUrlParameter('force_after_ms');
+       before = getUrlParameter('force_before_ms');
+
+       if(before > 0 && after > 0)
+               NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], after, before);
+
        // let it run (update the charts)
        NETDATA.unpause();
 
@@ -1804,7 +1853,7 @@ function finalizePage() {
        /* activate bootstrap sidebar (affix) */
        $('#sidebar').affix({
                offset: {
-                       top: (document.location.hostname === demo_hostname)?150:0,
+                       top: (isdemo())?150:0,
                        bottom: 0
                }
        });
@@ -1852,6 +1901,7 @@ function finalizePage() {
                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) {
@@ -1873,6 +1923,7 @@ function finalizePage() {
        $('#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()                       { NETDATA.setOption('show_help', $(this).prop('checked')); location.reload(); });
 
        // this has to be the last
        // it reloads the page
@@ -1881,7 +1932,7 @@ function finalizePage() {
                        location.reload();
        });
 
-       if(document.location.hostname === demo_hostname) {
+       if(isdemo()) {
                setTimeout(function() {
                        $('#welcomeModal').modal();
                }, 1000);
@@ -1891,13 +1942,18 @@ function finalizePage() {
 
        $('#updateModal').on('shown.bs.modal', function() {
                notifyForUpdate(true);
-       })
+       });
 }
 
 function resetDashboardOptions() {
+       var help = NETDATA.options.current.show_help;
+
        NETDATA.resetOptions();
        if(setTheme('slate'))
                location.reload();
+
+       if(help !== NETDATA.options.current.show_help)
+               location.reload();
 }
 
 downloadAllCharts();
index 3071c11362815ba9e2d4fee5ed31686b22401081..7dfef0aa8b801cd923547ee0c70fa933784d00c3 100644 (file)
@@ -206,7 +206,6 @@ definitions:
         type: number
         description: 'The update frequency of this chart, in seconds. One value every this amount of time is kept in the round robin database.'
       dimensions:
-        description: 'The dimensions of the chart.'
         type: object
         description: 'An object containing all the chart dimensions available for the chart. This is used as an indexed array. The key of the object the id of the dimension.'
         properties: