]> arthur.barton.de Git - netdata.git/commitdiff
Merge pull request #277 from jgeromero/tomcat.chart.sh
authorCosta Tsaousis <costa@tsaousis.gr>
Wed, 20 Apr 2016 22:56:31 +0000 (01:56 +0300)
committerCosta Tsaousis <costa@tsaousis.gr>
Wed, 20 Apr 2016 22:56:31 +0000 (01:56 +0300)
tomcat.chart.sh plugin

64 files changed:
.gitignore
.travis.yml [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0755]
ChangeLog
Makefile.am
README.md
charts.d/ap.chart.sh
charts.d/apache.chart.sh
charts.d/example.chart.sh
charts.d/mysql.chart.sh
charts.d/nginx.chart.sh
charts.d/phpfpm.chart.sh [new file with mode: 0644]
conf.d/apps_groups.conf
configure.ac
contrib/Makefile.am [new file with mode: 0644]
contrib/README.md [new file with mode: 0644]
contrib/debian/changelog [new file with mode: 0644]
contrib/debian/compat [new file with mode: 0644]
contrib/debian/control [new file with mode: 0644]
contrib/debian/copyright [new file with mode: 0644]
contrib/debian/netdata.conf [new file with mode: 0644]
contrib/debian/netdata.default [new file with mode: 0644]
contrib/debian/netdata.docs [new file with mode: 0644]
contrib/debian/netdata.init [new file with mode: 0755]
contrib/debian/netdata.install [new file with mode: 0644]
contrib/debian/netdata.lintian-overrides [new file with mode: 0644]
contrib/debian/netdata.postinst.in [new file with mode: 0644]
contrib/debian/netdata.postrm [new file with mode: 0644]
contrib/debian/netdata.service [new file with mode: 0644]
contrib/debian/rules [new file with mode: 0755]
contrib/debian/source/format [new file with mode: 0644]
netdata-9999.ebuild
netdata-installer.sh
netdata.spec.in
packaging/tar-compare
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.conf [new file with mode: 0644]
system/netdata.logrotate.in [new file with mode: 0644]
system/netdata.service.in
web/Makefile.am
web/dashboard.js
web/index.html
web/netdata-swagger.yaml

index d3d8aaf802627cab5b58d9eb7994ae47eeb19d5f..72c79d863305dbcb8a0c616dd36016cf75223820 100644 (file)
@@ -32,7 +32,6 @@ netdata.spec
 .cproject
 .idea/
 .project
-CMakeLists.txt
 README
 TODO.md
 asan_symbolize.py
@@ -61,11 +60,15 @@ web/version.txt
 
 system/netdata-openrc
 system/netdata-init-d
+system/netdata.logrotate
 
 netdata-installer.log
 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 244034862e5ad241e5fd59515654ad85ea69a3ab..eae63fd9ea22a2edff8b7c9d88dc1142d72b7dc1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+netdata (1.1.0) - 2016-04-20
+
+ Dozens of commits that improve netdata in several ways:
+
+ - Data collection: added IPv6 monitoring
+ - Data collection: added SYNPROXY DDoS protection monitoring
+ - Data collection: apps.plugin: added charts for users and user groups
+ - Data collection: apps.plugin: grouping of processes now support patterns
+ - Data collection: apps.plugin: now it is faster, after the new features added
+ - Data collection: better auto-detection of partitions for disk monitoring
+ - Data collection: better fireqos intergation for QoS monitoring
+ - Data collection: squid monitoring now uses squidclient
+ - Data collection: SNMP monitoring now supports 64bit counters
+ - API: fixed issues in CSV output generation
+ - API: netdata can now be restricted to listen on a specific IP
+ - Core and apps.plugin: error log flood protection
+ - Dashboard: better error handling when the netdata server is unreachable
+ - Dashboard: each chart now has a toolbox
+ - Dashboard: on-line help support
+ - Dashboard: check for netdata updates button
+ - Dashboard: added example /tv.html dashboard
+ - Packaging: now compiles with musl libc (alpine linux)
+ - Packaging: added debian packaging
+ - Packaging: support non-root installations
+ - Packaging: the installer generates uninstall script
+
 netdata (1.0.0) - 2016-03-22
 
  - first public release
index 8c7e1949bb6cd4ecdcaaa010b5e78fdec91915d9..dc109cbd1ad1d040a47f025cd977ad834ea6140c 100644 (file)
@@ -35,6 +35,7 @@ SUBDIRS = \
        src \
        system \
        web \
+       contrib \
        $(NULL)
 
 dist_noinst_DATA = netdata.spec
index 1dd2bb086455414a64a56bd667b3feacf4de2d4f..59360d455f1f082ae8bd8c466ec9eef80559c878 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,10 +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 it still runs with 1.000+ git 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)**.
+**[Check what our users say about netdata](https://github.com/firehol/netdata/issues/148)**.
 
 Thank you!
 
index 4704b89de3bc11af8190da820a757dae3b9fef62..10a65688c23c0fed9d5eee0aab1bde4613ca8336 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # _update_every is a special variable - it holds the number of seconds
 # between the calls of the _update() function
index efa559ddbc952d4ead96321a899d55b6a755ebd8..9b6d53b539bf4607ea27d40eaa54150e7daa2668 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # the URL to download apache status info
 apache_url="http://127.0.0.1:80/server-status?auto"
index 641d03e5de3f889125e0280880dd4f5bd6066ad1..34a3bc1bfab43b2f8ba1c88de770708174ddc9a8 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # if this chart is called X.chart.sh, then all functions and global variables
 # must start with X_
index 2839052898c4defc2ae32171b156641a0ebd8673..e037aed5df216a48c49660949611bff45b492e1c 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # http://dev.mysql.com/doc/refman/5.0/en/server-status-variables.html
 #
index bc8293c5dfa4c0387166d167d9b7f35323db7d04..a6795415b4f9a7870d508160e86a1d8589bd7dc6 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 # if this chart is called X.chart.sh, then all functions and global variables
 # must start with X_
diff --git a/charts.d/phpfpm.chart.sh b/charts.d/phpfpm.chart.sh
new file mode 100644 (file)
index 0000000..4c54aa5
--- /dev/null
@@ -0,0 +1,175 @@
+#!/bin/bash
+
+# if this chart is called X.chart.sh, then all functions and global variables
+# must start with X_
+
+# first, you need open php-fpm status in php-fpm.conf 
+# second, you need add status location in nginx.conf
+# you can see, https://easyengine.io/tutorials/php/fpm-status-page/
+
+declare -A phpfpm_urls=()
+
+# _update_every is a special variable - it holds the number of seconds
+# between the calls of the _update() function
+phpfpm_update_every=
+phpfpm_priority=60000
+
+declare -a phpfpm_response=()
+phpfpm_pool=""
+phpfpm_start_time=""
+phpfpm_start_since=0
+phpfpm_accepted_conn=0
+phpfpm_listen_queue=0
+phpfpm_max_listen_queue=0
+phpfpm_listen_queue_len=0
+phpfpm_idle_processes=0
+phpfpm_active_processes=0
+phpfpm_total_processes=0
+phpfpm_max_active_processes=0
+phpfpm_max_children_reached=0
+phpfpm_slow_requests=0
+phpfpm_get() {
+       url=$1
+       phpfpm_response=($(curl -s "${url}"))
+       [ $? -ne 0 -o "${#phpfpm_response[@]}" -eq 0 ] && return 1
+
+       if [[ "${phpfpm_response[0]}" != "pool:" \
+               || "${phpfpm_response[2]}" != "process" \
+               || "${phpfpm_response[5]}" != "start" \
+               || "${phpfpm_response[12]}" != "accepted" \
+               || "${phpfpm_response[15]}" != "listen" \
+               || "${phpfpm_response[16]}" != "queue:" \
+               || "${phpfpm_response[26]}" != "idle" \
+               || "${phpfpm_response[29]}" != "active" \
+               || "${phpfpm_response[32]}" != "total" \
+               || "${phpfpm_response[43]}" != "slow" \
+       ]]
+               then
+               echo >&2 "phpfpm: invalid response from phpfpm status server: ${phpfpm_response[*]}"
+               return 1
+       fi
+
+       phpfpm_pool="${phpfpm_response[1]}"
+       phpfpm_start_time="${phpfpm_response[7]} ${phpfpm_response[8]}"
+       phpfpm_start_since="${phpfpm_response[11]}"
+       phpfpm_accepted_conn="${phpfpm_response[14]}"
+       phpfpm_listen_queue="${phpfpm_response[17]}"
+       phpfpm_max_listen_queue="${phpfpm_response[21]}"
+       phpfpm_listen_queue_len="${phpfpm_response[25]}"
+       phpfpm_idle_processes="${phpfpm_response[28]}"
+       phpfpm_active_processes="${phpfpm_response[31]}"
+       phpfpm_total_processes="${phpfpm_response[34]}"
+       phpfpm_max_active_processes="${phpfpm_response[38]}"
+       phpfpm_max_children_reached="${phpfpm_response[42]}"
+       phpfpm_slow_requests="${phpfpm_response[45]}"
+       
+       if [[ -z "${phpfpm_pool}" \
+               || -z "${phpfpm_start_time}" \
+               || -z "${phpfpm_start_since}" \
+               || -z "${phpfpm_accepted_conn}" \
+               || -z "${phpfpm_listen_queue}" \
+               || -z "${phpfpm_max_listen_queue}" \
+               || -z "${phpfpm_listen_queue_len}" \
+               || -z "${phpfpm_idle_processes}" \
+               || -z "${phpfpm_active_processes}" \
+               || -z "${phpfpm_total_processes}" \
+               || -z "${phpfpm_max_active_processes}" \
+               || -z "${phpfpm_max_children_reached}" \
+               || -z "${phpfpm_slow_requests}" \
+       ]]
+               then
+               echo >&2 "phpfpm: empty values got from phpfpm status server: ${phpfpm_response[*]}"
+               return 1
+       fi
+
+       return 0
+}
+
+# _check is called once, to find out if this chart should be enabled or not
+phpfpm_check() {
+       if [ ${#phpfpm_urls[@]} -eq 0 ]; then
+               phpfpm_urls[local]="http://localhost/status"
+       fi
+       
+       local m
+       for m in "${!phpfpm_urls[@]}"
+       do
+               phpfpm_get "${phpfpm_urls[$m]}"
+               if [ $? -ne 0 ]; then
+                       echo >&2 "phpfpm: cannot find status on URL '${phpfpm_url[$m]}'. Please set phpfpm_urls[$m]='http://localhost/status' in $confd/phpfpm.conf"
+                       unset phpfpm_urls[$m]
+                       continue
+               fi
+       done
+       
+       if [ ${#phpfpm_urls[@]} -eq 0 ]; then
+               echo >&2 "phpfpm: no phpfpm servers found. Please set phpfpm_urls[name]='url' to whatever needed to get status to the phpfpm server, in $confd/phpfpm.conf"
+               return 1
+       fi
+       
+       # this should return:
+       #  - 0 to enable the chart
+       #  - 1 to disable the chart
+
+       return 0
+}
+
+# _create is called once, to create the charts
+phpfpm_create() {
+       local m
+       for m in "${!phpfpm_urls[@]}"
+       do
+               cat <<EOF
+CHART phpfpm_$m.connections '' "PHP-FPM Active Connections" "connections" phpfpm phpfpm.connections line $[phpfpm_priority + 1] $phpfpm_update_every
+DIMENSION active '' absolute 1 1
+DIMENSION maxActive 'max active' absolute 1 1
+DIMENSION idle '' absolute 1 1
+
+CHART phpfpm_$m.requests '' "PHP-FPM Requests" "requests/s" phpfpm phpfpm.requests line $[phpfpm_priority + 2] $phpfpm_update_every
+DIMENSION requests '' incremental 1 1
+
+CHART phpfpm_$m.performance '' "PHP-FPM Performance" "status" phpfpm phpfpm.performance line $[phpfpm_priority + 3] $phpfpm_update_every
+DIMENSION reached 'max children reached' absolute 1 1
+DIMENSION slow 'slow requests' absolute 1 1
+EOF
+       done
+       
+       return 0
+}
+
+# _update is called continiously, to collect the values
+phpfpm_update() {
+       # the first argument to this function is the microseconds since last update
+       # pass this parameter to the BEGIN statement (see bellow).
+
+       # do all the work to collect / calculate the values
+       # for each dimension
+       # remember: KEEP IT SIMPLE AND SHORT
+
+       local m
+       for m in "${!phpfpm_urls[@]}"
+       do
+               phpfpm_get "${phpfpm_urls[$m]}"
+               if [ $? -ne 0 ]; then
+                       continue
+               fi
+       
+               # write the result of the work.
+               cat <<EOF
+BEGIN phpfpm_$m.connections $1
+SET active = $[phpfpm_active_processes]
+SET maxActive = $[phpfpm_max_active_processes]
+SET idle = $[phpfpm_idle_processes]
+END
+BEGIN phpfpm_$m.requests $1
+SET requests = $[phpfpm_accepted_conn]
+END
+BEGIN phpfpm_$m.performance $1
+SET reached = $[phpfpm_max_children_reached]
+SET slow = $[phpfpm_slow_requests]
+END
+EOF
+       done
+       
+       return 0
+}
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 1474bffdbaeaae7192fd9fbfe32e380bfb45ee3a..177b6ddbd54266624aa2c78a96687742c138d1a7 100644 (file)
@@ -4,13 +4,13 @@
 AC_PREREQ(2.60)
 
 define([VERSION_MAJOR], [1])
-define([VERSION_MINOR], [0])
+define([VERSION_MINOR], [1])
 define([VERSION_FIX], [1])
 define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX)
 define([VERSION_SUFFIX], [_master])
 
 dnl Set to "1" for a first RPM release of a new version
-PACKAGE_RPM_RELEASE="0.0.$(echo VERSION_SUFFIX | sed 's/^_//')"
+PACKAGE_RPM_RELEASE="0.0.$(echo VERSION_SUFFIX | sed s/^_//)"
 
 AC_INIT([netdata], VERSION_NUMBER[]VERSION_SUFFIX)
 
@@ -164,6 +164,7 @@ AC_CONFIG_FILES([
        src/Makefile
        system/Makefile
        web/Makefile
+       contrib/Makefile
 ])
 AC_OUTPUT
 
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
new file mode 100644 (file)
index 0000000..5eef884
--- /dev/null
@@ -0,0 +1,23 @@
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+       README.md \
+       debian/copyright \
+       debian/netdata.conf \
+       debian/source/format \
+       debian/compat \
+       debian/netdata.install \
+       debian/netdata.lintian-overrides \
+       debian/rules \
+       debian/netdata.docs \
+       debian/netdata.default \
+       debian/control \
+       debian/netdata.postinst.in \
+       debian/netdata.service \
+       debian/changelog \
+       debian/netdata.postrm \
+       $(NULL)
+
+dist_noinst_SCRIPTS = \
+       debian/netdata.init \
+       $(NULL)
diff --git a/contrib/README.md b/contrib/README.md
new file mode 100644 (file)
index 0000000..4578989
--- /dev/null
@@ -0,0 +1,49 @@
+# netdata contrib
+
+## Building .deb packages
+
+The `contrib/debian/` directory contains basic rules to build a
+Debian package.  It has been tested on Debian Jessie and Wheezy,
+but should work, possibly with minor changes, if you have other
+dpkg-based systems such as Ubuntu or Mint.
+
+To build netdata for a Debian Jessie system, the debian directory
+has to be available in the root of the netdata source. The easiest
+way to do this is with a symlink:
+
+    ~/netdata$ ln -s contrib/debian
+
+Then build the debian package:
+
+    ~/netdata$ dpkg-buildpackage -us -uc -rfakeroot
+
+This should give a package that can be installed in the parent
+directory, which you can install manually with dpkg.
+
+    ~/netdata$ ls ../*.deb
+    ../netdata_1.0.0_amd64.deb
+    ~/netdata$ sudo dpkg -i ../netdata_1.0.0_amd64.deb
+
+
+### Building for a Debian system without systemd
+
+The included packaging is designed for modern Debian systems that
+are based on systemd. To build non-systemd packages (for example,
+for Debian wheezy), you will need to make a couple of minor
+updates first.
+
+* edit `contrib/debian/rules` and adjust the `dh` rule near the
+  top to remove systemd (see comments in that file).
+
+* edit `contrib/debian/control`: remove `dh-systemd` from the
+  Build-Depends list, and add `pkg-config` to it.
+
+Then proceed as the main instructions above.
+
+### Reinstalling netdata
+
+The recommended way to upgrade netdata packages built from this
+source is to remove the current package from your system, then
+install the new package. Upgrading on wheezy is known to not
+work cleanly; Jessie may behave as expected.
+
diff --git a/contrib/debian/changelog b/contrib/debian/changelog
new file mode 100644 (file)
index 0000000..795eaad
--- /dev/null
@@ -0,0 +1,5 @@
+netdata (1.0.0) UNRELEASED; urgency=medium
+
+  * Initial release.
+
+ -- Matthew Newton <mcn4@leicester.ac.uk>  Fri, 01 Apr 2016 17:24:11 +0100
diff --git a/contrib/debian/compat b/contrib/debian/compat
new file mode 100644 (file)
index 0000000..ec63514
--- /dev/null
@@ -0,0 +1 @@
+9
diff --git a/contrib/debian/control b/contrib/debian/control
new file mode 100644 (file)
index 0000000..c0312c6
--- /dev/null
@@ -0,0 +1,24 @@
+Source: netdata
+Build-Depends: debhelper (>= 9),
+               dh-autoreconf,
+               dh-systemd (>= 1.5),
+               dpkg-dev (>= 1.13.19),
+               zlib1g-dev
+Section: net
+Priority: optional
+Maintainer: Costa Tsaousis <costa@tsaousis.gr>
+Standards-Version: 3.9.6
+Homepage: https://github.com/firehol/netdata/wiki
+
+Package: netdata
+Architecture: any
+Depends: adduser,
+         libcap2-bin (>= 1:2.0),
+         lsb-base (>= 3.1-23.2),
+         ${misc:Depends},
+         ${shlibs:Depends}
+Description: real-time charts for system monitoring
+ Netdata is a daemon that collects data in realtime (per second)
+ and presents a web site to view and analyze them. The presentation
+ is also real-time and full of interactive charts that precisely
+ render all collected values.
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
new file mode 100644 (file)
index 0000000..11a3d63
--- /dev/null
@@ -0,0 +1,10 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Netdata
+Upstream-Contact: Costa Tsaousis <costa@tsaousis.gr>
+Source: https://github.com/firehol/netdata
+
+Files: *
+Copyright: 2014-2016, Costa Tsaousis
+License: GPL-3+
+ On Debian systems, the complete text of the GNU General Public
+ License version 3 can be found in /usr/share/common-licenses/GPL-3.
diff --git a/contrib/debian/netdata.conf b/contrib/debian/netdata.conf
new file mode 100644 (file)
index 0000000..a963d80
--- /dev/null
@@ -0,0 +1,16 @@
+# NetData Configuration
+
+# The current full configuration can be retrieved from the running
+# server at the URL
+#
+#   http://localhost:19999/netdata.conf
+#
+# for example:
+#
+#   wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
+#
+
+[global]
+       run as user = netdata
+       web files owner = root
+       web files group = netdata
diff --git a/contrib/debian/netdata.default b/contrib/debian/netdata.default
new file mode 100644 (file)
index 0000000..eb1a690
--- /dev/null
@@ -0,0 +1,4 @@
+# Extra arguments to pass to netdata
+#
+#EXTRA_OPTS=""
+
diff --git a/contrib/debian/netdata.docs b/contrib/debian/netdata.docs
new file mode 100644 (file)
index 0000000..56631ab
--- /dev/null
@@ -0,0 +1 @@
+ChangeLog
diff --git a/contrib/debian/netdata.init b/contrib/debian/netdata.init
new file mode 100755 (executable)
index 0000000..7403b45
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Start/stop the netdata daemon.
+#
+### BEGIN INIT INFO
+# Provides:          netdata
+# Required-Start:    $remote_fs
+# Required-Stop:     $remote_fs
+# Should-Start:      $network
+# Should-Stop:       $network
+# Default-Start:     2 3 4 5
+# Default-Stop:
+# Short-Description: Real-time charts for system monitoring
+# Description:       Netdata is a daemon that collects data in realtime (per second)
+#                    and presents a web site to view and analyze them. The presentation
+#                    is also real-time and full of interactive charts that precisely
+#                    render all collected values.
+### END INIT INFO
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+DESC="netdata daemon"
+NAME=netdata
+DAEMON=/usr/sbin/netdata
+PIDFILE=/var/run/netdata/netdata.pid
+SCRIPTNAME=/etc/init.d/"$NAME"
+
+test -f $DAEMON || exit 0
+
+. /lib/lsb/init-functions
+
+[ -r /etc/default/netdata ] && . /etc/default/netdata
+
+case "$1" in
+start) log_daemon_msg "Starting real-time system monitoring" "netdata"
+        start_daemon -p $PIDFILE $DAEMON $EXTRA_OPTS
+        log_end_msg $?
+       ;;
+stop)  log_daemon_msg "Stopping real-time system monitoring" "netdata"
+        killproc -p $PIDFILE $DAEMON
+        RETVAL=$?
+        [ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE
+        log_end_msg $RETVAL
+       # wait for plugins to exit
+       sleep 1
+        ;;
+restart|force-reload) log_daemon_msg "Restarting real-time system monitoring" "netdata"
+        $0 stop
+        $0 start
+        ;;
+status)
+        status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
+        ;;
+*)     log_action_msg "Usage: /etc/init.d/cron {start|stop|status|restart|force-reload}"
+        exit 2
+        ;;
+esac
+exit 0
diff --git a/contrib/debian/netdata.install b/contrib/debian/netdata.install
new file mode 100644 (file)
index 0000000..45d42b6
--- /dev/null
@@ -0,0 +1 @@
+debian/netdata.conf /etc/netdata/
diff --git a/contrib/debian/netdata.lintian-overrides b/contrib/debian/netdata.lintian-overrides
new file mode 100644 (file)
index 0000000..45b2d86
--- /dev/null
@@ -0,0 +1,16 @@
+
+# See Debian policy 10.9. apps.plugin has extra capabilities, so don't let
+# normal users run it.
+netdata: non-standard-executable-perm usr/lib/*/netdata/plugins.d/apps.plugin 0754 != 0755
+
+
+# FontAwesome is at least in the fonts-font-awesome package, but this is
+# not available in wheezy. glyphicons-halflings-regular isn't currently in
+# a Debian package. Therefore don't complain about shipping them with netdata
+# for the time being.
+netdata: duplicate-font-file usr/share/netdata/fonts/*
+netdata: font-in-non-font-package usr/share/netdata/fonts/*
+
+# Files here are marked as conffiles so that local updates to the html files
+# isn't clobbered on upgrade.
+netdata: non-etc-file-marked-as-conffile var/lib/netdata/www/*
diff --git a/contrib/debian/netdata.postinst.in b/contrib/debian/netdata.postinst.in
new file mode 100644 (file)
index 0000000..5a4e41a
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/sh
+
+set -e
+
+case "$1" in
+  configure)
+        if [ -z "$2" ]; then
+          if ! getent group netdata >/dev/null; then
+              addgroup --quiet --system netdata
+          fi
+
+          if ! getent passwd netdata >/dev/null; then
+              adduser --quiet --system --ingroup netdata --home /var/lib/netdata --no-create-home netdata
+          fi
+
+          if ! dpkg-statoverride --list /var/lib/netdata >/dev/null 2>&1; then
+              dpkg-statoverride --update --add root netdata 0755 /var/lib/netdata
+          fi
+
+          if ! dpkg-statoverride --list /var/lib/netdata/www >/dev/null 2>&1; then
+              dpkg-statoverride --update --add root netdata 0755 /var/lib/netdata/www
+          fi
+
+          if ! dpkg-statoverride --list /var/cache/netdata >/dev/null 2>&1; then
+              dpkg-statoverride --update --add netdata netdata 0755 /var/cache/netdata
+          fi
+
+        fi
+
+        chown -R root:netdata /usr/share/netdata/*
+        chown -R root:netdata /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d
+        setcap cap_dac_read_search,cap_sys_ptrace+ep /usr/lib/@DEB_HOST_MULTIARCH@/netdata/plugins.d/apps.plugin
+
+#PERMS#
+        ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
diff --git a/contrib/debian/netdata.postrm b/contrib/debian/netdata.postrm
new file mode 100644 (file)
index 0000000..4ab4eea
--- /dev/null
@@ -0,0 +1,43 @@
+#! /bin/sh
+
+set -e
+
+case "$1" in
+       remove)
+               ;;
+
+       purge)
+               if dpkg-statoverride --list | grep -qw /var/cache/netdata; then
+                       dpkg-statoverride --remove /var/cache/netdata
+               fi
+
+               if dpkg-statoverride --list | grep -qw /var/lib/netdata/www; then
+                       dpkg-statoverride --remove /var/lib/netdata/www
+               fi
+
+               if dpkg-statoverride --list | grep -qw /var/lib/netdata; then
+                       dpkg-statoverride --remove /var/lib/netdata
+               fi
+
+               if getent passwd netdata >/dev/null; then
+                       if [ -x /usr/sbin/deluser ]; then
+                               deluser --quiet --system netdata || echo "Unable to remove netdata user"
+                       fi
+               fi
+
+               if getent group netdata >/dev/null; then
+                       if [ -x /usr/sbin/delgroup ]; then
+                               delgroup --quiet --system netdata || echo "Unable to remove netdata group"
+                       fi
+               fi
+
+               ;;
+
+       *)
+               ;;
+esac
+
+#DEBHELPER#
+
+exit 0
+
diff --git a/contrib/debian/netdata.service b/contrib/debian/netdata.service
new file mode 100644 (file)
index 0000000..e62ce4e
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=netdata real-time system monitoring
+After=network.target
+
+[Service]
+Type=simple
+EnvironmentFile=-/etc/default/netdata
+ExecStart=/usr/sbin/netdata -nd $EXTRA_OPTS
+TimeoutStopSec=30
+Restart=always
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/debian/rules b/contrib/debian/rules
new file mode 100755 (executable)
index 0000000..ec4ec41
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/make -f
+
+# Find the arch we are building for, as this determines
+# the location of plugins in /usr/lib
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+TOP = $(CURDIR)/debian/netdata
+
+%:
+       # For jessie and beyond
+       #
+       dh $@ --with autoreconf,systemd
+
+       # For wheezy or other non-systemd distributions use the following. You
+       # should also see contrib/README.md which gives details of updates to
+       # make to debian/control.
+       #
+       #dh $@ --with autoreconf
+
+override_dh_auto_configure:
+       dh_auto_configure -- --with-math --with-webdir=/var/lib/netdata/www
+
+debian/%.postinst: debian/%.postinst.in
+       sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' $< > $@
+
+override_dh_install: debian/netdata.postinst
+       dh_install
+
+       # Remove unneeded .keep files
+       #
+       find "$(TOP)" -name .keep -exec rm '{}' ';'
+
+       # Move files that local user shouldn't be editing to /usr/share/netdata
+       #
+       mkdir -p "$(TOP)/usr/share/netdata"
+       for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type d -printf '%f '); do \
+               echo Relocating $$D; \
+               mv "$(TOP)/var/lib/netdata/www/$$D" "$(TOP)/usr/share/netdata/$$D"; \
+               ln -s "/usr/share/netdata/$$D" "$(TOP)/var/lib/netdata/www/$$D"; \
+       done
+
+       # Update postinst to set correct group for www files on installation.
+       # Should probably be dpkg-statoverride really, but that gets *really*
+       # messy. We also set all web files in /var as conffiles so an upgrade
+       # doesn't splat them.
+       #
+       for D in $$(find "$(TOP)/var/lib/netdata/www/" -maxdepth 1 -type f -printf '%f '); do \
+               echo Updating postinst for $$D; \
+               sed -i "s/^#PERMS#/chgrp netdata \/var\/lib\/netdata\/www\/$$D\n#PERMS#/g" \
+                       $(CURDIR)/debian/netdata.postinst; \
+               echo "/var/lib/netdata/www/$$D" >> $(CURDIR)/debian/netdata.conffiles; \
+       done
+       sed -i "/^#PERMS#/d" $(CURDIR)/debian/netdata.postinst
+
+override_dh_installdocs:
+       dh_installdocs
+
+       # Docs should not be under /usr/lib
+       #
+       mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/README.md \
+               $(TOP)/usr/share/doc/netdata/README.plugins.md
+       mv $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/charts.d/README.md \
+               $(TOP)/usr/share/doc/netdata/README.charts.md
+
+       # This doc is currently empty, so no point installing it.
+       #
+       rm $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/node.d/README.md
+
+override_dh_fixperms:
+       dh_fixperms
+
+       # apps.plugin should only be runnable by the netdata user. It will be
+       # given extra capabilities in the postinst script.
+       #
+       chmod 0754 $(TOP)/usr/lib/$(DEB_HOST_MULTIARCH)/netdata/plugins.d/apps.plugin
+
+override_dh_installlogrotate:
+       cp system/netdata.logrotate debian/netdata.logrotate
+       dh_installlogrotate
+
+override_dh_clean:
+       dh_clean
+
+       # Tidy up copied/generated files
+       #
+       -[ -r $(CURDIR)/debian/netdata.logrotate ] && rm $(CURDIR)/debian/netdata.logrotate
+       -[ -r $(CURDIR)/debian/netdata.postinst ] && rm $(CURDIR)/debian/netdata.postinst
+       -[ -r $(CURDIR)/debian/netdata.conffiles ] && rm $(CURDIR)/debian/netdata.conffiles
diff --git a/contrib/debian/source/format b/contrib/debian/source/format
new file mode 100644 (file)
index 0000000..89ae9db
--- /dev/null
@@ -0,0 +1 @@
+3.0 (native)
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 caa428085cd20653e8c78556643d95df2e158b1f..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
@@ -118,14 +121,47 @@ 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=
@@ -163,6 +199,9 @@ then
        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.
 
@@ -190,9 +229,6 @@ if [ ${DONOTWAIT} -eq 0 ]
        fi
 fi
 
-# reload the profile
-[ -f /etc/profile ] && . /etc/profile
-
 build_error() {
        cat <<EOF
 
@@ -213,6 +249,7 @@ You many need to check these:
 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
@@ -224,6 +261,7 @@ EOF
 }
 
 run() {
+       printf >&2 "\n"
        printf >&2 ":-----------------------------------------------------------------------------\n"
        printf >&2 "Running command:\n"
        printf >&2 "\n"
@@ -246,11 +284,9 @@ trap build_error EXIT
 
 if [ "$have_autotools" ]
 then
-       echo >&2 "Running ./autogen.sh ..."
        run ./autogen.sh || exit 1
 fi
 
-echo >&2 "Running ./configure ..."
 run ./configure \
        --prefix="${NETDATA_PREFIX}/usr" \
        --sysconfdir="${NETDATA_PREFIX}/etc" \
@@ -270,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
 
 
 # -----------------------------------------------------------------------------
@@ -305,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} )"
@@ -330,7 +389,7 @@ if [ ! -d "${NETDATA_RUN_DIR}" ]
        mkdir -p "${NETDATA_RUN_DIR}" || exit 1
 fi
 
-echo >&2 "Fixing directory permissions for user ${NETDATA_USER}..."
+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}" ]
@@ -338,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
@@ -356,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."
@@ -454,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'"
@@ -462,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.
@@ -501,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
 }
@@ -509,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
 }
@@ -538,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
 
@@ -623,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..7d63650ec8e0d547c4f57a2aeabaf6ccf8f0f0d9 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
+Summary:       Real-time performance monitoring, done right
 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
+* Wed Apr 20 2016 Costa Tsaousis <costa@tsaousis.gr> - 1.1.0-1
+- Several new features (IPv6, SYNPROXY, Users, Users Groups).
+- A lot of bug fixes and optimizations.
 * Tue Mar 22 2016 Costa Tsaousis <costa@tsaousis.gr> - 1.0.0-1
 - First public release.
 * Sun Nov 15 2015 Alon Bar-Lev <alonbl@redhat.com> - 0.0.0-1
index 02ec3c293d2283704b0b49940a35fc070e16757b..4e51e84c90c2499d04bfc08095a5065cf9d2bfaa 100755 (executable)
@@ -54,6 +54,8 @@ diff -r "$1" $MYTMP/unpack/* | grep "^Only" | sed \
        -e '/: unittest$/d' \
        -e '/: iprange$/d' \
        -e '/: .*\.o$/d' \
+       -e '/: CMakeLists.txt/d' \
+       -e '/: .travis.yml/d' \
        -e '/sbin: \(firehol\|fireqos\|link-balancer\)$/d' \
        -e '/sbin: \(update-ipsets\|vnetbuild\|commands.sed\)$/d' > $MYTMP/out
 
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 d091f7730216b8ae13354a599ed58ef5511e7b7e..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;
        }
 
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..c2b84aae10d11c65519d77a581372346ec8a2461 100644 (file)
@@ -4,6 +4,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include "common.h"
 #include "log.h"
 
 #define RRD_TYPE_DISK "disk"
 
+struct disk {
+       unsigned long major;
+       unsigned long minor;
+       int partition_id;       // -1 = this is not a partition
+       struct disk *next;
+} *disk_root = NULL;
+
+struct disk *get_disk(unsigned long major, unsigned long minor) {
+       static char path_find_block_device_partition[FILENAME_MAX + 1] = "";
+       struct disk *d;
+
+       // search for it in our RAM list.
+       // this is sequential, but since we just walk through
+       // and the number of disks / partitions in a system
+       // should not be that many, it should be acceptable
+       for(d = disk_root; d ; d = d->next)
+               if(unlikely(d->major == major && d->minor == minor))
+                       break;
+
+       // if we found it, return it
+       if(likely(d))
+               return d;
+
+       if(unlikely(!path_find_block_device_partition[0])) {
+               char filename[FILENAME_MAX + 1];
+               snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/dev/block/%lu:%lu/partition");
+               snprintf(path_find_block_device_partition, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get block device partition", filename));
+       }
+
+       // not found
+       // create a new disk structure
+       d = (struct disk *)malloc(sizeof(struct disk));
+       if(!d) fatal("Cannot allocate memory for struct disk in proc_diskstats.");
+
+       d->major = major;
+       d->minor = minor;
+       d->partition_id = -1;
+       d->next = NULL;
+
+       // append it to the list
+       if(!disk_root)
+               disk_root = d;
+       else {
+               struct disk *last;
+               for(last = disk_root; last->next ;last = last->next);
+               last->next = d;
+       }
+
+       // find if it is a partition
+       // by reading /sys/dev/block/MAJOR:MINOR/partition
+       char buffer[FILENAME_MAX + 1];
+       snprintf(buffer, FILENAME_MAX, path_find_block_device_partition, major, minor);
+
+       int fd = open(buffer, O_RDONLY, 0666);
+       if(likely(fd != -1)) {
+               // we opened it
+               int bytes = read(fd, buffer, FILENAME_MAX);
+               close(fd);
+
+               if(bytes > 0)
+                       d->partition_id = strtoul(buffer, NULL, 10);
+       }
+       // if the /partition file does not exist, it is a disk, not a partition
+
+       return d;
+}
+
 int do_proc_diskstats(int update_every, unsigned long long dt) {
        static procfile *ff = NULL;
        static char path_to_get_hw_sector_size[FILENAME_MAX + 1] = "";
@@ -40,7 +110,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
        if(!path_to_get_hw_sector_size[0]) {
                char filename[FILENAME_MAX + 1];
                snprintf(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/sys/block/%s/queue/hw_sector_size");
-               snprintf(path_to_get_hw_sector_size, FILENAME_MAX, "%s%s", global_host_prefix, config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", filename));
+               snprintf(path_to_get_hw_sector_size, FILENAME_MAX, "%s", config_get("plugin:proc:/proc/diskstats", "path to get h/w sector size", filename));
        }
 
        ff = procfile_readall(ff);
@@ -107,13 +177,19 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                // I/O completion time and the backlog that may be accumulating.
                backlog_ms              = strtoull(procfile_lineword(ff, l, 13), NULL, 10);     // rq_ticks
 
-
                int def_enabled = 0;
 
                // remove slashes from disk names
                char *s;
                for(s = disk; *s ;s++) if(*s == '/') *s = '_';
 
+               struct disk *d = get_disk(major, minor);
+               if(d->partition_id == -1)
+                       def_enabled = enable_new_disks;
+               else
+                       def_enabled = 0;
+
+/*
                switch(major) {
                        case 9: // MDs
                        case 43: // network block
@@ -123,6 +199,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 +272,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                        case 135: // scsi
                        case 153: // raid
                        case 202: // xen
-                       case 253: // virtio
+                       case 254: // virtio3
                        case 256: // flash
                        case 257: // flash
                        case 259: // nvme0n1 issue #119
@@ -232,6 +309,7 @@ int do_proc_diskstats(int update_every, unsigned long long dt) {
                                def_enabled = 0;
                                break;
                }
+*/
 
                int ddo_io = do_io, ddo_ops = do_ops, ddo_mops = do_mops, ddo_iotime = do_iotime, ddo_qops = do_qops, ddo_util = do_util, ddo_backlog = do_backlog;
 
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 9d7e9487d0ed653180fbb1ab562296594f9c205d..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);
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 49cb5f7bc1b481f30f33c64674a865e554ce5c7f..f16a720e2d2bdbfe4ff8179a121a3afe657b5e0f 100644 (file)
@@ -4,6 +4,7 @@
 MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
 CLEANFILES = \
        netdata-openrc \
+       netdata.logrotate \
        netdata.service \
        netdata-init-d \
        $(NULL)
@@ -14,12 +15,15 @@ SUFFIXES = .in
 
 nodist_noinst_DATA = \
        netdata-openrc \
+       netdata.logrotate \
        netdata.service \
        netdata-init-d \
        $(NULL)
 
 dist_noinst_DATA = \
        netdata-openrc.in \
+       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()
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
diff --git a/system/netdata.logrotate.in b/system/netdata.logrotate.in
new file mode 100644 (file)
index 0000000..763eb09
--- /dev/null
@@ -0,0 +1,15 @@
+@localstatedir_POST@/log/netdata/*.log {
+       daily
+       missingok
+       rotate 14
+       compress
+       delaycompress
+       notifempty
+       sharedscripts
+       postrotate
+               if service netdata status > /dev/null ; then \
+                       service netdata restart > /dev/null; \
+               fi;
+       endscript
+}
+
index e26068d289cc37f46d799ea500ba002cfcb6582c..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]
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 5731d55e1651ae4afaafe4aa09105bbdef8dccb8..a2089d412521321c9bd2b1af6de9748805c32c30 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
 
                        color_fill_opacity_area: 0.2,
                        color_fill_opacity_stacked: 0.8,
 
-                       pan_and_zoom_step: 0.1,         // the increment when panning and zooming with the toolbox
+                       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.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.appendChild(this.element_legend_childs.toolbox_left);
                                        this.element_legend_childs.toolbox_left.onclick = function(e) {
                                                e.preventDefault();
-                                               var dt = (that.view_before - that.view_after) * NETDATA.options.current.pan_and_zoom_step;
-                                               var before = that.view_before - dt;
-                                               var after = that.view_after - dt;
+
+                                               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",
                                                html: true,
                                                trigger: 'hover',
                                                placement: 'bottom',
-                                               delay: 100,
+                                               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.onclick = function(e) {
                                                e.preventDefault();
                                                NETDATA.resetAllCharts(that);
-                                       }
+                                       };
                                        if(NETDATA.options.current.show_help === true)
                                                $(this.element_legend_childs.toolbox_reset).popover({
                                                container: "body",
                                                html: true,
                                                trigger: 'hover',
                                                placement: 'bottom',
-                                               delay: 100,
+                                               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.appendChild(this.element_legend_childs.toolbox_right);
                                        this.element_legend_childs.toolbox_right.onclick = function(e) {
                                                e.preventDefault();
-                                               var dt = (that.view_before - that.view_after) * NETDATA.options.current.pan_and_zoom_step;
-                                               var before = that.view_before + dt;
-                                               var after = that.view_after + dt;
+                                               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",
                                                html: true,
                                                trigger: 'hover',
                                                placement: 'bottom',
-                                               delay: 100,
+                                               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.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) * NETDATA.options.current.pan_and_zoom_step;
+                                               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",
                                                html: true,
                                                trigger: 'hover',
                                                placement: 'bottom',
-                                               delay: 100,
+                                               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.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) * NETDATA.options.current.pan_and_zoom_step;
+                                               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",
                                                html: true,
                                                trigger: 'hover',
                                                placement: 'bottom',
-                                               delay: 100,
+                                               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>'
                                        });
                                        html: true,
                                        trigger: 'hover',
                                        placement: 'bottom',
-                                       delay: 100,
+                                       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>'
                                });
                                        trigger: 'hover',
                                        placement: 'bottom',
                                        title: 'Chart Legend',
-                                       delay: 100,
+                                       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>'
                                });
                        }
                                $(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;
                        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;
                NETDATA.chartLibraries[library].url = url;
                NETDATA.chartLibraries[library].initialized = true;
                NETDATA.chartLibraries[library].enabled = true;
-       }
+       };
 
        // ----------------------------------------------------------------------------------------------------------------
        // Start up
                {
                        url: NETDATA.serverDefault + 'lib/bootstrap.min.js',
                        isAlreadyLoaded: function() {
+                               // check if bootstrap is loaded
                                if(typeof $().emulateTransitionEnd == 'function')
                                        return true;
                                else {
                .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);
 
        NETDATA._loadjQuery(function() {
                NETDATA.loadRequiredJs(0, function() {
+                       if(typeof $().emulateTransitionEnd == 'function') {
+                               // bootstrap is not available
+                               NETDATA.options.current.show_help = false;
+                       }
+
                        if(typeof netdataDontStart === 'undefined' || !netdataDontStart) {
                                if(NETDATA.options.debug.main_loop === true)
                                        console.log('starting chart refresh thread');
index ec545c821d7fbad1c72e547c8d22c43554eaa886..6f6013da1d6a93f0ac701d58b17349c7caa7f9bf 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?v27"></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">
 
 <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;
 
-if(document.location.hostname === demo_hostname) {
+       try {
+               if(typeof document.location.hostname === 'string')
+                       this_is_demo = document.location.hostname.endsWith('.firehol.org');
+       }
+       catch(error) {
+               ;
+       }
+
+       return this_is_demo;
+}
+
+if(isdemo()) {
        document.getElementById('masthead').style.display = 'block';
 }
 
@@ -949,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
        },
 
@@ -993,8 +1006,8 @@ var submenuData = {
        },
 
        'netfilter.synproxy': {
-               title: 'Anti-DDoS Protection',
-               info: 'Anti-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.'
+               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.'
        }
 };
 
@@ -1057,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': {
@@ -1344,11 +1357,22 @@ function enrichChartData(chart) {
                case 'tc':
                        chart.menu = tmp;
 
-                       // find the interface name from the name
-                       if(typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family)
-                               options.submenu_names[chart.family] = chart.name.split('.')[1].split('_')[0];
+                       // find a name for this device from fireqos info
+                       // we strip '_(in|out)' or '(in|out)_'
+                       if(typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family) {
+                               var n = chart.name.split('.')[1];
+                               if(n.endsWith('_in'))
+                                       options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in'));
+                               else if(n.endsWith('_out'))
+                                       options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_out'));
+                               else if(n.startsWith('in_'))
+                                       options.submenu_names[chart.family] = n.slice(3, n.length);
+                               else if(n.startsWith('out_'))
+                                       options.submenu_names[chart.family] = n.slice(4, n.length);
+                       }
 
                        // increase the priority of IFB devices
+                       // to have inbound appear before outbound
                        if(chart.id.match(/.*-ifb$/))
                                chart.priority--;
 
@@ -1840,7 +1864,7 @@ function finalizePage() {
        /* activate bootstrap sidebar (affix) */
        $('#sidebar').affix({
                offset: {
-                       top: (document.location.hostname === demo_hostname)?150:0,
+                       top: (isdemo())?150:0,
                        bottom: 0
                }
        });
@@ -1919,7 +1943,7 @@ function finalizePage() {
                        location.reload();
        });
 
-       if(document.location.hostname === demo_hostname) {
+       if(isdemo()) {
                setTimeout(function() {
                        $('#welcomeModal').modal();
                }, 1000);
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: