.cproject
.idea/
.project
-CMakeLists.txt
README
TODO.md
asan_symbolize.py
gmon.out
gmon.txt
-
apps.plugin-profiler.sh
+
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
--- /dev/null
+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
--- /dev/null
+
+# 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})
EXTRA_DIST = \
.gitignore \
autogen.sh \
- README \
+ README.md \
LICENSE.md \
COPYING \
autogen.sh \
web \
$(NULL)
-if GIT_TREE
-
-all-local: README
-
-README: README.md
- sed -e '/^## Features$$/p' -e '/^## Git/,/^## Features$$/d' $< > $@
-
-endif
-
dist_noinst_DATA = netdata.spec
# until integrated within build
# netdata
-#### 180.000+ views, 50.000+ visitors, 15.000+ downloads, 8.000+ github stars, 400+ forks, 7 days!
+#### 230.000+ views, 62.000+ visitors, 18.500+ downloads, 9.500+ github stars, 500+ forks, 14 days!
-And we still run with 1.000+ downloads... per day!
+And it still runs with 700+ git downloads... per day!
+
+**[Check what our users say about netdata](https://github.com/firehol/netdata/issues/148)**.
Thank you!
This is what you get:
-1. **Beautiful out of the box** bootstrap dashboards
-2. **Custom dashboards** that can be built using simple HTML (no javascript necessary)
-3. **Blazingly fast** and **super efficient**, written in C (for default installations, expect just 2% of a single core CPU usage and a few MB of RAM)
-3. **Zero configuration** - you just install it and it autodetects everything
-4. **Zero dependencies**, it is its own web server for its static web files and its web API
-4. **Extensible**, you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin - from BASH to node.js)
-7. **Embeddable**, it can run anywhere a Linux kernel runs
+- **Stunning bootstrap dashboards**, out of the box (themable: dark, light)
+- **Blazingly fast** and **super efficient**, mostly written in C (for default installations, expect just 2% of a single core CPU usage and a few MB of RAM)
+- **Zero configuration** - you just install it and it autodetects everything
+- **Zero dependencies**, it is its own web server for its static web files and its web API
+- **Zero maintenance**, you just run it, it does the rest
+- **Custom dashboards** that can be built using simple HTML (no javascript necessary)
+- **Extensible**, you can monitor anything you can get a metric for, using its Plugin API (anything can be a netdata plugin - from BASH to node.js, so you can easily monitor any application, any API)
+- **Embeddable**, it can run anywhere a Linux kernel runs and its charts can be embedded on your web pages too
---
This is what it currently monitors (most with zero configuration):
-1. **CPU usage, interrupts, softirqs and frequency** (total and per core)
-2. **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
-3. **Disk I/O** (per disk: bandwidth, operations, backlog, utilization, etc)
+- **CPU usage, interrupts, softirqs and frequency** (total and per core)
+
+- **RAM, swap and kernel memory usage** (including KSM and kernel memory deduper)
+
+- **Disks** (per disk: I/O, operations, backlog, utilization, etc)
![sda](https://cloud.githubusercontent.com/assets/2662304/14093195/c882bbf4-f554-11e5-8863-1788d643d2c0.gif)
-4. **Network interfaces** (per interface: bandwidth, packets, errors, drops, etc)
+- **Network interfaces** (per interface: bandwidth, packets, errors, drops, etc)
![dsl0](https://cloud.githubusercontent.com/assets/2662304/14093128/4d566494-f554-11e5-8ee4-5392e0ac51f0.gif)
-5. **IPv4 networking** (bandwidth, packets, errors, fragments, tcp: connections, packets, errors, handshake, udp: packets, errors, broadcast: bandwidth, packets, multicast: bandwidth, packets)
-6. **IPv6 networking** (bandwidth, packets, errors, fragments, ECT, udp: packets, errors, udplite: packets, errors, broadcast: bandwidth, multicast: bandwidth, packets, icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, break down by type)
-7. **netfilter / iptables Linux firewall** (connections, connection tracker events, errors, etc)
-8. **Processes** (running, blocked, forks, active, etc)
-9. **Entropy**
-10. **NFS file servers**, v2, v3, v4 (I/O, cache, read ahead, RPC calls)
-11. **Network QoS** (yes, the only tool that visualizes network `tc` classes in realtime)
+- **IPv4 networking** (bandwidth, packets, errors, fragments, tcp: connections, packets, errors, handshake, udp: packets, errors, broadcast: bandwidth, packets, multicast: bandwidth, packets)
- ![qos-tc-classes](https://cloud.githubusercontent.com/assets/2662304/14093004/68966020-f553-11e5-98fe-ffee2086fafd.gif)
+- **IPv6 networking** (bandwidth, packets, errors, fragments, ECT, udp: packets, errors, udplite: packets, errors, broadcast: bandwidth, multicast: bandwidth, packets, icmp: messages, errors, echos, router, neighbor, MLDv2, group membership, break down by type)
+
+- **netfilter / iptables Linux firewall** (connections, connection tracker events, errors, etc)
+
+- **Linux anti-DDoS protection** (SYNPROXY metrics)
+- **Processes** (running, blocked, forks, active, etc)
-12. **Applications**, by grouping the process tree (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
+- **Entropy** (random numbers pool, using in cryptography)
+
+- **NFS file servers**, v2, v3, v4 (I/O, cache, read ahead, RPC calls)
+
+- **Network QoS** (yes, the only tool that visualizes network `tc` classes in realtime)
+
+ ![qos-tc-classes](https://cloud.githubusercontent.com/assets/2662304/14093004/68966020-f553-11e5-98fe-ffee2086fafd.gif)
+
+- **Applications**, by grouping the process tree (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
![apps](https://cloud.githubusercontent.com/assets/2662304/14093565/67c4002c-f557-11e5-86bd-0154f5135def.gif)
-13. **Apache web server** mod-status (v2.2, v2.4)
-14. **Nginx web server** stub-status
-15. **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc)
-16. **ISC Bind name server** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics)
-17. **Postfix email server** message queue (entries, size)
-18. **Squid proxy server** (clients bandwidth and requests, servers bandwidth and requests)
-19. **Hardware sensors** (temperature, voltage, fans, power, humidity, etc)
-20. **NUT UPSes** (load, charge, battery voltage, temperature, utility metrics, output metrics)
+- **Users and User Groups resource usage**, by summarizing the process tree per user and group (CPU, memory, disk reads, disk writes, swap, threads, pipes, sockets, etc)
+
+- **Apache web server** mod-status (v2.2, v2.4)
-Any number of **SNMP devices** can be monitored, although you will need to configure these.
+- **Nginx web server** stub-status
+
+- **mySQL databases** (multiple servers, each showing: bandwidth, queries/s, handlers, locks, issues, tmp operations, connections, binlog metrics, threads, innodb metrics, etc)
+
+- **ISC Bind name server** (multiple servers, each showing: clients, requests, queries, updates, failures and several per view metrics)
+
+- **Postfix email server** message queue (entries, size)
+
+- **Squid proxy server** (clients bandwidth and requests, servers bandwidth and requests)
+
+- **Hardware sensors** (temperature, voltage, fans, power, humidity, etc)
+
+- **NUT UPSes** (load, charge, battery voltage, temperature, utility metrics, output metrics)
+
+- **SNMP devices** can be monitored too (although you will need to configure these)
And you can extend it, by writing plugins that collect data from any source, using any computer language.
Read **[Why netdata?](https://github.com/firehol/netdata/wiki/Why-netdata%3F)**
-Or **[check what our users say about netdata](https://github.com/firehol/netdata/issues/148)**.
-
---
## Installation
Use our **[automatic installer](https://github.com/firehol/netdata/wiki/Installation)** to build and install it on your system
-It should run on any Linux system. We have tested it on:
+It should run on **any Linux** system. It has been tested on:
- Gentoo
- ArchLinux
- Ubuntu / Debian
- CentOS
- Fedora
+- RedHat Enterprise Linux
+- SUSE
+- Alpine Linux
+- PLD Linux
---
## Documentation
Check the **[netdata wiki](https://github.com/firehol/netdata/wiki)**.
-
-
-## Git sources
-
-You are looking at a version of the sources extracted directly from git.
-If you want a version of the source package where `configure` and any
-documentation has been built for you, please get an official
-[netdata package download](https://firehol.org/download/netdata/).
-The `unsigned/master` folder tracks the head of the git tree and
-released packages are also available.
-
# 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
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*
AC_INIT([netdata], VERSION_NUMBER[]VERSION_SUFFIX)
-AM_CONDITIONAL([GIT_TREE], [test -f README.md])
-
AM_MAINTAINER_MODE([disable])
if test x"$USE_MAINTAINER_MODE" = xyes; then
AC_MSG_NOTICE(***************** MAINTAINER MODE *****************)
"
: ${NETDATA_USER:=netdata}
+: ${NETDATA_GROUP:=netdata}
pkg_setup() {
linux-info_pkg_setup
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
#!/bin/bash
+# reload the user profile
+[ -f /etc/profile ] && . /etc/profile
+
LC_ALL=C
# you can set CFLAGS before running installer
- plugins at ${NETDATA_PREFIX}/usr/libexec/netdata
- cache files at ${NETDATA_PREFIX}/var/cache/netdata
- log files at ${NETDATA_PREFIX}/var/log/netdata
+ - pid file at ${NETDATA_PREFIX}/var/run
This installer allows you to change the installation path.
Press Control-C and run the same command with --help for help.
BANNER
-if [ ! "${UID}" = 0 -o "$1" = "-h" -o "$1" = "--help" ]
+if [ "${UID}" -ne 0 ]
then
- echo >&2
- echo >&2 "You have to run netdata as root."
- echo >&2 "The netdata daemon will drop priviliges"
- echo >&2 "but you have to start it as root."
- echo >&2
- exit 1
+ if [ -z "${NETDATA_PREFIX}" ]
+ then
+ cat <<NONROOTNOPREFIX
+
+Sorry! This will wrong!
+
+You are attempting to install netdata as non-root, but you plan to install it
+in system paths.
+
+Please set an installation prefix, like this:
+
+ $0 ${@} --install /tmp
+
+or, run the installer as root:
+
+ sudo $0 ${@}
+
+We suggest to install it as root, or certain data collectors will not be able
+to work. Netdata drops root privileges when running. So, if you plan to keep
+it, install it as root to get the full functionality.
+
+NONROOTNOPREFIX
+ exit 1
+
+ else
+ cat <<NONROOT
+
+IMPORTANT:
+You are about to install netdata as a non-root user.
+Netdata will work, but a few data collection modules that
+require root access will fail.
+
+If you installing permanently on your system, run the
+installer like this:
+
+ sudo $0 ${@}
+
+NONROOT
+ fi
+fi
+
+have_autotools=
+if [ "$(type autoreconf 2> /dev/null)" ]
+then
+ autoconf_maj_min() {
+ local maj min IFS=.-
+
+ maj=$1
+ min=$2
+
+ set -- $(autoreconf -V | sed -ne '1s/.* \([^ ]*\)$/\1/p')
+ eval $maj=\$1 $min=\$2
+ }
+ autoconf_maj_min AMAJ AMIN
+
+ if [ "$AMAJ" -gt 2 ]
+ then
+ have_autotools=Y
+ elif [ "$AMAJ" -eq 2 -a "$AMIN" -ge 60 ]
+ then
+ have_autotools=Y
+ else
+ echo "Found autotools $AMAJ.$AMIN"
+ fi
+else
+ echo "No autotools found"
+fi
+
+if [ ! "$have_autotools" ]
+then
+ if [ -f configure ]
+ then
+ echo "Will skip autoreconf step"
+ else
+ cat <<-"EOF"
+
+ -------------------------------------------------------------------------------
+ autotools 2.60 or later is required
+
+ Sorry, you do not seem to have autotools 2.60 or later, which is
+ required to build from the git sources of netdata.
+
+ You can either install a suitable version of autotools and automake
+ or download a netdata package which does not have these dependencies.
+
+ Source packages where autotools have already been run are available
+ here:
+ https://firehol.org/download/netdata/
+
+ The unsigned/master folder tracks the head of the git tree and released
+ packages are also available.
+ EOF
+ exit 1
+ fi
fi
if [ ${DONOTWAIT} -eq 0 ]
fi
fi
-# reload the profile
-[ -f /etc/profile ] && . /etc/profile
-
build_error() {
cat <<EOF
gcc make autoconf automake pkg-config
+ Autoconf version 2.60 or higher is required
+
3. If your system cannot find ZLIB, although it is installed
run me with the option: --zlib-is-really-here
+
If you still cannot get it to build, ask for help at github:
https://github.com/firehol/netdata/issues
}
run() {
+ printf >&2 "\n"
printf >&2 ":-----------------------------------------------------------------------------\n"
printf >&2 "Running command:\n"
printf >&2 "\n"
trap build_error EXIT
-echo >&2 "Running ./autogen.sh ..."
-run ./autogen.sh || exit 1
+if [ "$have_autotools" ]
+then
+ run ./autogen.sh || exit 1
+fi
-echo >&2 "Running ./configure ..."
run ./configure \
--prefix="${NETDATA_PREFIX}/usr" \
--sysconfdir="${NETDATA_PREFIX}/etc" \
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
# -----------------------------------------------------------------------------
[ ! "${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} )"
NETDATA_WEB_DIR="$( config_option "web files directory" "${NETDATA_PREFIX}/usr/share/netdata/web" )"
NETDATA_LOG_DIR="$( config_option "log directory" "${NETDATA_PREFIX}/var/log/netdata" )"
NETDATA_CONF_DIR="$( config_option "config directory" "${NETDATA_PREFIX}/etc/netdata" )"
+NETDATA_RUN_DIR="${NETDATA_PREFIX}/var/run"
# -----------------------------------------------------------------------------
# prepare the directories
-echo >&2 "Fixing directory permissions for user ${NETDATA_USER}..."
+# this is needed if NETDATA_PREFIX is not empty
+if [ ! -d "${NETDATA_RUN_DIR}" ]
+ then
+ mkdir -p "${NETDATA_RUN_DIR}" || exit 1
+fi
+
+echo >&2 "Fixing directories (user: ${NETDATA_USER})..."
for x in "${NETDATA_WEB_DIR}" "${NETDATA_CONF_DIR}" "${NETDATA_CACHE_DIR}" "${NETDATA_LOG_DIR}"
do
if [ ! -d "${x}" ]
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
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."
count=0
while [ $ret -eq 0 ]
do
- if [ $count -gt 15 ]
+ if [ $count -gt 30 ]
then
echo >&2 "Cannot stop the running netdata."
exit 1
count=$((count + 1))
- pid=$(cat /var/run/netdata.pid 2>/dev/null)
+ pid=$(cat "${NETDATA_RUN_DIR}/netdata.pid" 2>/dev/null)
# backwards compatibility
+ [ -z "${pid}" ] && pid=$(cat /var/run/netdata.pid 2>/dev/null)
[ -z "${pid}" ] && pid=$(cat /var/run/netdata/netdata.pid 2>/dev/null)
isnetdata $pid || pid=
test $ret -eq 0 && printf >&2 "." && sleep 2
done
echo >&2
+echo >&2
# -----------------------------------------------------------------------------
# run netdata
echo >&2 "Starting netdata..."
-run ${NETDATA_PREFIX}/usr/sbin/netdata -pidfile /var/run/netdata.pid "${@}"
+run ${NETDATA_PREFIX}/usr/sbin/netdata -pidfile ${NETDATA_RUN_DIR}/netdata.pid "${@}"
if [ $? -ne 0 ]
then
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'"
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.
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
}
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
}
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
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"
+#
+# Conditional build:
+%bcond_without systemd # systemd
+%bcond_without nfacct # build with nfacct plugin
+
%if 0%{?fedora} || 0%{?rhel} >= 7
-%global do_systemd 1
%else
-%global do_systemd 0
+%undefine with_systemd
%endif
Summary: Linux real time system monitoring, over the web
Name: @PACKAGE_NAME@
Version: @PACKAGE_RPM_VERSION@
Release: @PACKAGE_RPM_RELEASE@%{?release_suffix}%{?dist}
-License: GPLv2+
-URL: http://firehol.org
-Source: %{name}-@PACKAGE_VERSION@.tar.bz2
-
-BuildRequires: libmnl-devel
+License: GPL v3+
+Group: Applications/System
+Source0: http://firehol.org/download/netdata/releases/v@PACKAGE_VERSION@/%{name}-@PACKAGE_VERSION@.tar.xz
+URL: http://netdata.firehol.org/
+BuildRequires: pkgconfig
+BuildRequires: xz
BuildRequires: zlib-devel
-Requires: libmnl
-Requires: zlib
-%if !0%{?rhel}
+%if %{with nfacct}
+BuildRequires: libmnl-devel
BuildRequires: libnetfilter_acct-devel
-Requires: libnetfilter_acct
%endif
-%if 0%{do_systemd}
+%if %{with systemd}
BuildRequires: systemd
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
%endif
-
-BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRoot: %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
%description
-Real-time performance monitoring, in the greatest possible detail!
-
-%pre
-getent group netdata > /dev/null || groupadd -r netdata
-getent passwd netdata > /dev/null || useradd -r -g netdata -c netdata -s /sbin/nologin -d / netdata
-
-%if 0%{do_systemd}
-%post
-%systemd_post netdata.service
-%preun
-%systemd_preun netdata.service
-%postun
-%systemd_postun_with_restart netdata.service
-%endif
-
-%global ovirt_create_user_engine \
-%_ovirt_create_user %{engine_user} %{engine_uid} %{engine_group} %{engine_gid} "%{ovirt_user_description}" %{engine_state}
+netdata is the fastest way to visualize metrics. It is a resource
+efficient, highly optimized system for collecting and visualizing any
+type of realtime timeseries data, from CPU usage, disk activity, SQL
+queries, API calls, web site visitors, etc.
+netdata tries to visualize the truth of now, in its greatest detail,
+so that you can get insights of what is happening now and what just
+happened, on your systems and applications.
%prep
%setup -q -n %{name}-@PACKAGE_VERSION@
%build
%configure \
- --docdir="%{_docdir}/%{name}-%{version}" \
+ --docdir=%{_docdir}/%{name}-%{version} \
--with-zlib \
--with-math \
- --with-user=netdata \
-%if !0%{?rhel}
- --enable-plugin-nfacct \
-%endif
- %{?conf}
-make %{?_smp_mflags}
+ %{?with_nfacct:--enable-plugin-nfacct} \
+ --with-user=netdata
+%{__make} %{?_smp_mflags}
%install
-rm -rf "%{buildroot}"
-make %{?_smp_mflags} install DESTDIR="%{buildroot}"
-find "%{buildroot}" -name .keep -exec rm {} \;
+rm -rf $RPM_BUILD_ROOT
+%{__make} %{?_smp_mflags} install \
+ DESTDIR=$RPM_BUILD_ROOT
-%if 0%{do_systemd}
-install -m 0755 -d "%{buildroot}/%{_unitdir}"
-install -m 0644 system/netdata.service "%{buildroot}/%{_unitdir}"
+install -m 644 -p system/netdata.conf $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/*.conf
+
+find $RPM_BUILD_ROOT -name .keep | xargs rm
+
+%if %{with systemd}
+install -d $RPM_BUILD_ROOT%{_unitdir}
+install -m 644 -p system/netdata.service $RPM_BUILD_ROOT%{_unitdir}/netdata.service
+%endif
+
+%pre
+getent group netdata > /dev/null || groupadd -r netdata
+getent passwd netdata > /dev/null || useradd -r -g netdata -c netdata -s /sbin/nologin -d / netdata
+
+%if %{with systemd}
+%post
+%systemd_post netdata.service
+
+%preun
+%systemd_preun netdata.service
+
+%postun
+%systemd_postun_with_restart netdata.service
%endif
+%clean
+rm -rf $RPM_BUILD_ROOT
+
%files
-%attr(-, netdata, netdata) %dir %{_localstatedir}/cache/%{name}/
-%attr(-, netdata, netdata) %dir %{_localstatedir}/log/%{name}/
-%config(noreplace) %{_sysconfdir}/%{name}/
-%{_datadir}/%{name}/
-%{_libexecdir}/%{name}/
+%attr(-,netdata,netdata) %dir %{_localstatedir}/cache/%{name}
+%attr(-,netdata,netdata) %dir %{_localstatedir}/log/%{name}
+%config(noreplace) %verify(not md5 mtime size) %{_sysconfdir}/%{name}/*.conf
+%dir %{_sysconfdir}/%{name}
+%{?with_systemd:%{_unitdir}/netdata.service}
+%{_libexecdir}/%{name}
%{_sbindir}/%{name}
+%dir %{_datadir}/%{name}
-%if 0%{do_systemd}
-%{_unitdir}/netdata.service
-%endif
+# override defattr for web files
+%defattr(755,root,netdata,644)
+%{_datadir}/%{name}/web
%changelog
* Tue Mar 22 2016 Costa Tsaousis <costa@tsaousis.gr> - 1.0.0-1
# the length of the multiplier - 1
l=$[ ${#m} - 1 ]
+ # check if the number is in scientific notation
+ if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]
+ then
+ # convert it to decimal
+ # unfortunately, this fork cannot be avoided
+ # if you know of a way to avoid it, please let me know
+ f=$(printf "%0.${l}f" ${f})
+ fi
+
# split the floating point number
# in integer (a) and decimal (b)
a=${f/.*/}
b=${f/*./}
- # prepend a zero to the integer part
- # if it is missing
+ # if the integer part is missing
+ # set it to zero
[ -z "${a}" ] && a="0"
- # strip leading zeros - base 10 convertion
+
+ # strip leading zeros from the integer part
+ # base 10 convertion
a=$[10#$a]
# check the length of the decimal part
then
# too many digits - take the most significant
b=${b:0:${l}}
+
elif [ ${#b} -lt ${l} ]
then
# too few digits - pad with zero on the right
local z="00000000000000000000000" r=$[l - ${#b}]
b="${b}${z:0:${r}}"
fi
- # strip leading zeros - base 10 convertion
+
+ # strip leading zeros from the decimal part
+ # base 10 convertion
b=$[10#$b]
# store the result
#!/bin/bash
+export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/sbin"
+
# default time function
now_ms=
current_time_ms() {
#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;
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;
#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;
}
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;
}
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;
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;
}
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)
else
snprintf(w->name, MAX_NAME, "%s", pw->pw_name);
+ netdata_fix_id(w->name);
+
w->uid = uid;
w->next = users_root_target;
}
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)
else
snprintf(w->name, MAX_NAME, "%s", gr->gr_name);
+ netdata_fix_id(w->name);
+
w->gid = gid;
w->next = groups_root_target;
// there are targets that are just aggregated to other target (the second argument)
struct target *get_apps_groups_target(const char *id, struct target *target)
{
+ int tdebug = 0, thidden = 0, ends_with = 0;
const char *nid = id;
- 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)) {
}
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;
}
// 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);
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;
}
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;
// ----------------------------------------------------------------------------
// 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];
}
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);
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
// 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);
}
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);
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);
// /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
// 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
// /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
// <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);
}
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;
// /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);
}
p->updated = 1;
}
- if(unlikely(count_errors > 1000)) {
- error("%ld more errors encountered\n", count_errors - 1);
- count_errors = 0;
- }
-
closedir(dir);
return 1;
// 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");
#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)
#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);
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) {
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;
}
// don't close it, we might need it at exit
// close(pidfd);
}
+ else error("Failed to open pidfile '%s'.", pidfile);
}
if(user && *user) {
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];
{
va_list args;
+ // prevent logging too much
+ if(error_log_limit(0)) return;
+
log_date(stderr);
va_start( args, fmt );
{
va_list args;
+ // prevent logging too much
+ if(error_log_limit(0)) return;
+
log_date(stderr);
va_start( args, fmt );
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)
#include "plugin_nfacct.h"
#include "main.h"
+#include "../config.h"
int netdata_exit = 0;
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);
}
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");
// --------------------------------------------------------------------
+ // 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();
// --------------------------------------------------------------------
#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)
// 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) {
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;
}
-
#include "rrd.h"
#include "popen.h"
#include "plugins_d.h"
+#include "../config.h"
struct plugind *pluginsd_root = NULL;
case 199: // veritas
case 201: // veritas
case 251: // dm
+ case 253: // virtio
def_enabled = enable_new_disks;
break;
case 135: // scsi
case 153: // raid
case 202: // xen
- case 253: // virtio
case 256: // flash
case 257: // flash
case 259: // nvme0n1 issue #119
#include "common.h"
#include "log.h"
#include "procfile.h"
+#include "../config.h"
#define PF_PREFIX "PROCFILE"
#define PF_CHAR_IS_SEPARATOR ' '
#define PF_CHAR_IS_NEWLINE 'N'
#define PF_CHAR_IS_WORD 'W'
+#define PF_CHAR_IS_QUOTE 'Q'
+#define PF_CHAR_IS_OPEN 'O'
+#define PF_CHAR_IS_CLOSE 'C'
void procfile_close(procfile *ff) {
debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
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;
}
}
- 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';
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) {
// 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
{
debug(D_RRD_CALLS, "rrdset_save_all()");
+ // let it log a few error messages
+ error_log_limit_reset();
+
RRDSET *st;
RRDDIM *rd;
// 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)) {
#include "rrd2json.h"
#include "web_client.h"
+#include "../config.h"
#define INITIAL_WEB_DATA_LENGTH 16384
#define WEB_REQUEST_LENGTH 16384
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);
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);
{
// get the command
char *tok = mystrsep(&url, "/?&");
- debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
-
- if(strcmp(tok, "data") == 0)
- return web_client_api_request_v1_data(w, url);
- else if(strcmp(tok, "chart") == 0)
- return web_client_api_request_v1_chart(w, url);
- else if(strcmp(tok, "charts") == 0)
- return web_client_api_request_v1_charts(w, url);
-
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
- return 404;
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
+
+ if(strcmp(tok, "data") == 0)
+ return web_client_api_request_v1_data(w, url);
+ else if(strcmp(tok, "chart") == 0)
+ return web_client_api_request_v1_chart(w, url);
+ else if(strcmp(tok, "charts") == 0)
+ return web_client_api_request_v1_charts(w, url);
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Unsupported v1 API command: %s", tok);
+ return 404;
+ }
+ }
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "API v1 command?");
+ return 400;
+ }
}
int web_client_api_request(struct web_client *w, char *url)
{
// get the api version
char *tok = mystrsep(&url, "/?&");
- debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
-
- if(strcmp(tok, "v1") == 0)
- return web_client_api_request_v1(w, url);
-
- buffer_flush(w->response.data);
- buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
- return 404;
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for API version '%s'.", w->id, tok);
+ if(strcmp(tok, "v1") == 0)
+ return web_client_api_request_v1(w, url);
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Unsupported API version: %s", tok);
+ return 404;
+ }
+ }
+ else {
+ buffer_flush(w->response.data);
+ buffer_sprintf(w->response.data, "Which API version?");
+ return 400;
+ }
}
int web_client_data_request(struct web_client *w, char *url, int datasource_type)
{
+ RRDSET *st = NULL;
+
char *args = strchr(url, '?');
if(args) {
*args='\0';
// get the name of the data to show
char *tok = mystrsep(&url, "/");
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
// do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+ st = rrdset_find_byname(tok);
+ if(!st) st = rrdset_find(tok);
+ }
+
if(!st) {
// we don't have it
// try to send a file with that name
if(url) {
// parse the group count required
tok = mystrsep(&url, "/");
- if(tok) group_count = atoi(tok);
+ if(tok && *tok) group_count = atoi(tok);
if(group_count < 1) group_count = 1;
//if(group_count > save_history / 20) group_count = save_history / 20;
}
if(url) {
// parse the grouping method required
tok = mystrsep(&url, "/");
- if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
- else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
- else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
- else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
+ if(tok && *tok) {
+ if(strcmp(tok, "max") == 0) group_method = GROUP_MAX;
+ else if(strcmp(tok, "average") == 0) group_method = GROUP_AVERAGE;
+ else if(strcmp(tok, "sum") == 0) group_method = GROUP_SUM;
+ else debug(D_WEB_CLIENT, "%llu: Unknown group method '%s'", w->id, tok);
+ }
}
if(url) {
// parse after time
tok = mystrsep(&url, "/");
- if(tok) after = strtoul(tok, NULL, 10);
+ if(tok && *tok) after = strtoul(tok, NULL, 10);
if(after < 0) after = 0;
}
if(url) {
// parse before time
tok = mystrsep(&url, "/");
- if(tok) before = strtoul(tok, NULL, 10);
+ if(tok && *tok) before = strtoul(tok, NULL, 10);
if(before < 0) before = 0;
}
if(url) {
// parse nonzero
tok = mystrsep(&url, "/");
- if(tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
+ if(tok && *tok && strcmp(tok, "nonzero") == 0) nonzero = 1;
}
w->response.data->contenttype = CT_APPLICATION_JSON;
while(args) {
tok = mystrsep(&args, "&");
- if(tok) {
+ if(tok && *tok) {
char *name = mystrsep(&tok, "=");
- if(name && strcmp(name, "tqx") == 0) {
+ if(name && *name && strcmp(name, "tqx") == 0) {
char *key = mystrsep(&tok, ":");
char *value = mystrsep(&tok, ";");
if(key && value && *key && *value) {
w->last_url[URL_MAX] = '\0';
tok = mystrsep(&url, "/?");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
- debug(D_WEB_CLIENT, "%llu: Processing command '%s'.", w->id, tok);
-
- if(strcmp(tok, "api") == 0) {
- // the client is requesting api access
- datasource_type = DATASOURCE_JSON;
- code = web_client_api_request(w, url);
- }
-#ifdef NETDATA_INTERNAL_CHECKS
- else if(strcmp(tok, "exit") == 0) {
- netdata_exit = 1;
- code = 200;
- w->response.data->contenttype = CT_TEXT_PLAIN;
- buffer_flush(w->response.data);
- buffer_strcat(w->response.data, "will do");
- }
-#endif
- else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
- // the client is requesting rrd data
- datasource_type = DATASOURCE_JSON;
- code = web_client_data_request(w, url, datasource_type);
- }
- else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
- // the client is requesting google datasource
- code = web_client_data_request(w, url, datasource_type);
- }
- else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
- // the client is requesting an rrd graph
-
- // get the name of the data to show
- tok = mystrsep(&url, "/?&");
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
- // do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- if(!st) {
- // we don't have it
- // try to send a file with that name
- buffer_flush(w->response.data);
- code = mysendfile(w, tok);
+ if(strcmp(tok, "api") == 0) {
+ // the client is requesting api access
+ datasource_type = DATASOURCE_JSON;
+ code = web_client_api_request(w, url);
}
- else {
+#ifdef NETDATA_INTERNAL_CHECKS
+ else if(strcmp(tok, "exit") == 0) {
+ netdata_exit = 1;
code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
- w->response.data->contenttype = CT_APPLICATION_JSON;
+ w->response.data->contenttype = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
- rrd_stats_graph_json(st, url, w->response.data);
+ buffer_strcat(w->response.data, "will do");
+ }
+#endif
+ else if(strcmp(tok, WEB_PATH_DATA) == 0) { // "data"
+ // the client is requesting rrd data
+ datasource_type = DATASOURCE_JSON;
+ code = web_client_data_request(w, url, datasource_type);
+ }
+ else if(strcmp(tok, WEB_PATH_DATASOURCE) == 0) { // "datasource"
+ // the client is requesting google datasource
+ code = web_client_data_request(w, url, datasource_type);
+ }
+ else if(strcmp(tok, WEB_PATH_GRAPH) == 0) { // "graph"
+ // the client is requesting an rrd graph
+
+ // get the name of the data to show
+ tok = mystrsep(&url, "/?&");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+ // do we have such a data set?
+ RRDSET *st = rrdset_find_byname(tok);
+ if(!st) st = rrdset_find(tok);
+ if(!st) {
+ // we don't have it
+ // try to send a file with that name
+ buffer_flush(w->response.data);
+ code = mysendfile(w, tok);
+ }
+ else {
+ code = 200;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending %s.json of RRD_STATS...", w->id, st->name);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_flush(w->response.data);
+ rrd_stats_graph_json(st, url, w->response.data);
+ }
+ }
+ else {
+ code = 400;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "Graph name?\r\n");
+ }
}
- }
#ifdef NETDATA_INTERNAL_CHECKS
- else if(strcmp(tok, "debug") == 0) {
- buffer_flush(w->response.data);
+ else if(strcmp(tok, "debug") == 0) {
+ buffer_flush(w->response.data);
- // get the name of the data to show
- tok = mystrsep(&url, "/?&");
- debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
-
- // do we have such a data set?
- RRDSET *st = rrdset_find_byname(tok);
- if(!st) st = rrdset_find(tok);
- if(!st) {
- code = 404;
- buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
- debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+ // get the name of the data to show
+ tok = mystrsep(&url, "/?&");
+ if(tok && *tok) {
+ debug(D_WEB_CLIENT, "%llu: Searching for RRD data with name '%s'.", w->id, tok);
+
+ // do we have such a data set?
+ RRDSET *st = rrdset_find_byname(tok);
+ if(!st) st = rrdset_find(tok);
+ if(!st) {
+ code = 404;
+ buffer_sprintf(w->response.data, "Chart %s is not found.\r\n", tok);
+ debug(D_WEB_CLIENT_ACCESS, "%llu: %s is not found.", w->id, tok);
+ }
+ else {
+ code = 200;
+ debug_flags |= D_RRD_STATS;
+ st->debug = st->debug?0:1;
+ buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
+ debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
+ }
+ }
+ else {
+ code = 500;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "debug which chart?\r\n");
+ }
}
- else {
+ else if(strcmp(tok, "mirror") == 0) {
code = 200;
- debug_flags |= D_RRD_STATS;
- st->debug = st->debug?0:1;
- buffer_sprintf(w->response.data, "Chart %s has now debug %s.\r\n", tok, st->debug?"enabled":"disabled");
- debug(D_WEB_CLIENT_ACCESS, "%llu: debug for %s is %s.", w->id, tok, st->debug?"enabled":"disabled");
- }
- }
- else if(strcmp(tok, "mirror") == 0) {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
- // replace the zero bytes with spaces
- buffer_char_replace(w->response.data, '\0', ' ');
+ // replace the zero bytes with spaces
+ buffer_char_replace(w->response.data, '\0', ' ');
- // just leave the buffer as is
- // it will be copied back to the client
- }
+ // just leave the buffer as is
+ // it will be copied back to the client
+ }
#endif
- else if(strcmp(tok, "list") == 0) {
- code = 200;
+ else if(strcmp(tok, "list") == 0) {
+ code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending list of RRD_STATS...", w->id);
- buffer_flush(w->response.data);
- RRDSET *st = rrdset_root;
+ buffer_flush(w->response.data);
+ RRDSET *st = rrdset_root;
- for ( ; st ; st = st->next )
- buffer_sprintf(w->response.data, "%s\n", st->name);
- }
- else if(strcmp(tok, "all.json") == 0) {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
+ for ( ; st ; st = st->next )
+ buffer_sprintf(w->response.data, "%s\n", st->name);
+ }
+ else if(strcmp(tok, "all.json") == 0) {
+ code = 200;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending JSON list of all monitors of RRD_STATS...", w->id);
- w->response.data->contenttype = CT_APPLICATION_JSON;
- buffer_flush(w->response.data);
- rrd_stats_all_json(w->response.data);
- }
- else if(strcmp(tok, "netdata.conf") == 0) {
- code = 200;
- debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
+ w->response.data->contenttype = CT_APPLICATION_JSON;
+ buffer_flush(w->response.data);
+ rrd_stats_all_json(w->response.data);
+ }
+ else if(strcmp(tok, "netdata.conf") == 0) {
+ code = 200;
+ debug(D_WEB_CLIENT_ACCESS, "%llu: Sending netdata.conf ...", w->id);
- w->response.data->contenttype = CT_TEXT_PLAIN;
- buffer_flush(w->response.data);
- generate_config(w->response.data, 0);
+ w->response.data->contenttype = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ generate_config(w->response.data, 0);
+ }
+ else {
+ char filename[FILENAME_MAX+1];
+ url = filename;
+ strncpy(filename, w->last_url, FILENAME_MAX);
+ filename[FILENAME_MAX] = '\0';
+ tok = mystrsep(&url, "?");
+ buffer_flush(w->response.data);
+ code = mysendfile(w, (tok && *tok)?tok:"/");
+ }
}
else {
char filename[FILENAME_MAX+1];
}
// free url_decode() buffer
- if(pointer_to_free) free(pointer_to_free);
+ if(pointer_to_free) {
+ free(pointer_to_free);
+ pointer_to_free = NULL;
+ }
}
else if(w->response.data->len > TOO_BIG_REQUEST) {
strcpy(w->last_url, "too big request");
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);
#include "global_statistics.h"
#include "rrd.h"
#include "rrd2json.h"
+#include "../config.h"
int listen_backlog = LISTEN_BACKLOG;
netdata.logrotate.in \
netdata.service.in \
netdata-init-d.in \
+ netdata.conf \
$(NULL)
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()
: ${NETDATA_START_AFTER_SERVICES:=apache2 squid nginx mysql named opensips upsd hostapd postfix lm_sensors}
extra_started_commands="getconf"
-pidfile="/run/netdata/netdata.pid"
+pidfile="/run/netdata.pid"
command="${NETDATA_INSTALL_PATH}/usr/sbin/netdata"
command_background="yes"
command_args="-pidfile ${pidfile} ${NETDATA_EXTRA_ARGS}"
-start_stop_daemon_args="-u ${NETDATA_OWNER}"
+# start_stop_daemon_args="-u ${NETDATA_OWNER}"
+start_stop_daemon_args=""
depend() {
use logger
--- /dev/null
+# 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
[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]
WorkingDirectory=/tmp
User=root
Group=root
-PIDFile=@localstatedir_POST@/run/netdata/netdata.pid
-ExecStart=@sbindir_POST@/netdata -pidfile @localstatedir_POST@/run/netdata/netdata.pid
+PIDFile=@localstatedir_POST@/run/netdata.pid
+ExecStart=@sbindir_POST@/netdata -pidfile @localstatedir_POST@/run/netdata.pid
ExecStop=/bin/kill -SIGTERM $MAINPID
TimeoutStopSec=30
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 = \
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
margin: 0px;
}
+.netdata-legend-toolbox {
+ display: block;
+ position: absolute;
+ bottom: 0px;
+ right: 30px;
+ height: 15px;
+ width: 110px;
+ background-color: White;
+ font-size: 12px;
+ vertical-align: middle;
+ line-height: 15px;
+ color: #DDDDDD;
+ text-align: center;
+ overflow: hidden;
+ z-index: 20;
+ padding: 0px;
+ margin: 0px;
+}
+
+.netdata-legend-toolbox-button {
+ display: inline-block;
+ position: relative;
+ height: 15px;
+ width: 18px;
+ background-color: White;
+ font-size: 12px;
+ vertical-align: middle;
+ line-height: 15px;
+ color: #CDCDCD;
+ text-align: center;
+ overflow: hidden;
+ z-index: 21;
+ padding: 0px;
+ margin: 0px;
+ cursor: pointer;
+}
+
.netdata-message {
display: inline-block;
text-align: left;
color: #999999;
font-weight: normal;
}
+
+.popover-title {
+ font-weight: bold;
+ font-size: 12px;
+}
+.popover-content {
+ font-size: 11px;
+}
// var netdataNoC3 = true; // do not use C3
// var netdataNoBootstrap = true; // do not load bootstrap
// var netdataDontStart = true; // do not start the thread to process the charts
+// var netdataErrorCallback = null; // Callback function that will be invoked upon error
//
// You can also set the default netdata server, using the following.
// When this variable is not set, we assume the page is hosted on your
after: -600, // panning
pixels_per_point: 1, // the detail of the chart
fill_luminance: 0.8 // luminance of colors in solit areas
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// global options
destroy_on_hide: false, // destroy charts when they are not visible
+ show_help: true, // when enabled the charts will show some help
+ show_help_delay_show_ms: 500,
+ show_help_delay_hide_ms: 0,
+
eliminate_zero_dimensions: true, // do not show dimensions with just zeros
stop_updates_when_focus_is_lost: true, // boolean - shall we stop auto-refreshes when document does not have user focus
color_fill_opacity_area: 0.2,
color_fill_opacity_stacked: 0.8,
+ pan_and_zoom_factor: 0.25, // the increment when panning and zooming with the toolbox
+ pan_and_zoom_factor_multiplier_control: 2.0,
+ pan_and_zoom_factor_multiplier_shift: 3.0,
+ pan_and_zoom_factor_multiplier_alt: 4.0,
+
setOptionCallback: function() { ; }
},
libraries: false,
dygraph: false
}
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
NETDATA.localStorage.current[key.toString()] = ret;
return ret;
- }
+ };
NETDATA.localStorageSet = function(key, value, callback) {
if(typeof value === 'undefined' || value === 'undefined') {
NETDATA.localStorage.current[key.toString()] = value;
return value;
- }
+ };
NETDATA.localStorageGetRecursive = function(obj, prefix, callback) {
for(var i in obj) {
obj[i] = NETDATA.localStorageGet(prefix + '.' + i.toString(), obj[i], callback);
}
- }
+ };
NETDATA.setOption = function(key, value) {
if(key.toString() === 'setOptionCallback') {
}
return true;
- }
+ };
NETDATA.getOption = function(key) {
return NETDATA.options.current[key.toString()];
- }
+ };
// read settings from local storage
NETDATA.localStorageGetRecursive(NETDATA.options.current, 'options', null);
101: { message: "Cannot load jQuery", alert: true },
402: { message: "Chart library not found", alert: false },
403: { message: "Chart library not enabled/is failed", alert: false },
- 404: { message: "Chart not found", alert: false }
+ 404: { message: "Chart not found", alert: false },
+ 405: { message: "Cannot download charts index from server", alert: true },
+ 406: { message: "Invalid charts index downloaded from server", alert: true }
};
NETDATA.errorLast = {
code: 0,
console.log("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
- if(NETDATA.errorCodes[code].alert)
+ var ret = true;
+ if(typeof netdataErrorCallback === 'function') {
+ ret = netdataErrorCallback('system', code, msg);
+ }
+
+ if(ret && NETDATA.errorCodes[code].alert)
alert("ERROR " + code + ": " + NETDATA.errorCodes[code].message + ": " + msg);
- }
+ };
NETDATA.errorReset = function() {
NETDATA.errorLast.code = 0;
cache: false
})
.done(function(data) {
- var h = NETDATA.chartRegistry.fixid(host);
- //console.log('downloaded all charts from ' + host + ' (' + h + ')');
- self.charts[h] = data.charts;
+ if(data !== null) {
+ var h = NETDATA.chartRegistry.fixid(host);
+ self.charts[h] = data.charts;
+ }
+ else NETDATA.error(406, host + '/api/v1/charts');
+
if(typeof callback === 'function')
callback(data);
})
.fail(function() {
+ NETDATA.error(405, host + '/api/v1/charts');
+
if(typeof callback === 'function')
callback(null);
});
return true;
}
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// dimensions selection
this.value_div = null;
this.color = NETDATA.themes.current.foreground;
- if(parent.selected === parent.unselected)
+ if(parent.selected_count > parent.unselected_count)
this.selected = true;
else
this.selected = false;
this.setOptions(name_div, value_div, color);
- }
+ };
dimensionStatus.prototype.invalidate = function() {
this.name_div = null;
this.value_div = null;
this.enabled = false;
- }
+ };
dimensionStatus.prototype.setOptions = function(name_div, value_div, color) {
this.color = color;
this.enabled = true;
this.setHandler();
- }
+ };
dimensionStatus.prototype.setHandler = function() {
if(this.enabled === false) return;
ds.parent.state.redrawChart();
}
- }
+ };
dimensionStatus.prototype.select = function() {
if(this.enabled === false) return;
this.name_div.className = 'netdata-legend-name selected';
this.value_div.className = 'netdata-legend-value selected';
this.selected = true;
- }
+ };
dimensionStatus.prototype.unselect = function() {
if(this.enabled === false) return;
this.name_div.className = 'netdata-legend-name not-selected';
this.value_div.className = 'netdata-legend-value hidden';
this.selected = false;
- }
+ };
dimensionStatus.prototype.isSelected = function() {
return(this.enabled === true && this.selected === true);
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
this.dimensions = {};
this.selected_count = 0;
this.unselected_count = 0;
- }
+ };
dimensionsVisibility.prototype.dimensionAdd = function(label, name_div, value_div, color) {
if(typeof this.dimensions[label] === 'undefined') {
this.dimensions[label].setOptions(name_div, value_div, color);
return this.dimensions[label];
- }
+ };
dimensionsVisibility.prototype.dimensionGet = function(label) {
return this.dimensions[label];
- }
+ };
dimensionsVisibility.prototype.invalidateAll = function() {
for(var d in this.dimensions)
this.dimensions[d].invalidate();
- }
+ };
dimensionsVisibility.prototype.selectAll = function() {
for(var d in this.dimensions)
this.dimensions[d].select();
- }
+ };
dimensionsVisibility.prototype.countSelected = function() {
var i = 0;
if(this.dimensions[d].isSelected()) i++;
return i;
- }
+ };
dimensionsVisibility.prototype.selectNone = function() {
for(var d in this.dimensions)
this.dimensions[d].unselect();
- }
+ };
dimensionsVisibility.prototype.selected2BooleanArray = function(array) {
var ret = new Array();
}
return ret;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
* show an error instead of the chart
*/
var error = function(msg) {
- that.element.innerHTML = that.id + ': ' + msg;
- that.enabled = false;
- that.current = that.pan;
- }
+ var ret = true;
+
+ if(typeof netdataErrorCallback === 'function') {
+ ret = netdataErrorCallback('chart', that.id, msg);
+ }
+
+ if(ret) {
+ that.element.innerHTML = that.id + ': ' + msg;
+ that.enabled = false;
+ that.current = that.pan;
+ }
+ };
// GUID - a unique identifier for the chart
this.uuid = NETDATA.guid();
that.tm.last_dom_created = new Date().getTime();
showLoading();
- }
+ };
/* init() private
- * initialize state viariables
+ * initialize state variables
* destroy all (possibly) created state elements
* create the basic DOM for a chart
*/
last_hidden: 0, // the time the chart was hidden
last_unhidden: 0, // the time the chart was unhidden
last_autorefreshed: 0 // the time the chart was last refreshed
- },
+ };
that.data = null; // the last data as downloaded from the netdata server
that.data_url = 'invalid://'; // string - the last url used to update the chart
createDOM();
that.setMode('auto');
- }
+ };
var maxMessageFontSize = function() {
// normally we want a font size, as tall as the element
// set it
that.element_message.style.fontSize = h.toString() + 'px';
that.element_message.style.paddingTop = paddingTop.toString() + 'px';
- }
+ };
var showMessage = function(msg) {
that.element_message.className = 'netdata-message';
that.element_message.innerHTML = msg;
- this.element_message.style.fontSize = 'x-small';
+ that.element_message.style.fontSize = 'x-small';
that.element_message.style.paddingTop = '0px';
that.___messageHidden___ = undefined;
- }
+ };
var showMessageIcon = function(icon) {
that.element_message.innerHTML = icon;
that.element_message.className = 'netdata-message icon';
maxMessageFontSize();
that.___messageHidden___ = undefined;
- }
+ };
var hideMessage = function() {
if(typeof that.___messageHidden___ === 'undefined') {
that.___messageHidden___ = true;
that.element_message.className = 'netdata-message hidden';
}
- }
+ };
var showRendering = function() {
var icon;
icon = '<i class="fa fa-area-chart"></i>';
showMessageIcon(icon + ' netdata');
- }
+ };
var showLoading = function() {
if(that.chart_created === false) {
return true;
}
return false;
- }
+ };
var isHidden = function() {
if(typeof that.___chartIsHidden___ !== 'undefined')
return true;
return false;
- }
+ };
// hide the chart, when it is not visible - called from isVisible()
var hideChart = function() {
}
that.___chartIsHidden___ = true;
- }
+ };
// unhide the chart, when it is visible - called from isVisible()
var unhideChart = function() {
resizeChart();
hideMessage();
}
- }
+ };
var canBeRendered = function() {
if(isHidden() === true || that.isVisible(true) === false)
return false;
return true;
- }
+ };
// https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
var callChartLibraryUpdateSafely = function(data) {
}
return true;
- }
+ };
// https://github.com/petkaantonov/bluebird/wiki/Optimization-killers
var callChartLibraryCreateSafely = function(data) {
that.chart_created = true;
that.updates_since_last_creation = 0;
return true;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// Chart Resize
that.tm.last_resized = new Date().getTime();
}
- }
+ };
// this is the actual chart resize algorithm
// it will:
NETDATA.options.auto_refresher_stop_until = 0;
};
}
- }
+ };
var noDataToShow = function() {
//that.element_chart.style.display = 'none';
//if(that.element_legend !== null) that.element_legend.style.display = 'none';
//that.___chartIsHidden___ = true;
- }
+ };
// ============================================================================================================
// PUBLIC FUNCTIONS
this.error = function(msg) {
error(msg);
- }
+ };
this.setMode = function(m) {
if(this.current !== null && this.current.name === m) return;
this.current.force_after_ms = null;
this.tm.last_mode_switch = new Date().getTime();
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// global selection sync
NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + ms;
else
NETDATA.globalSelectionSync.dont_sync_before = new Date().getTime() + NETDATA.options.current.sync_selection_delay;
- }
+ };
// can we globally apply selection sync?
this.globalSelectionSyncAbility = function() {
return false;
return true;
- }
+ };
this.globalSelectionSyncIsMaster = function() {
if(NETDATA.globalSelectionSync.state === this)
return true;
else
return false;
- }
+ };
// this chart is the master of the global selection sync
this.globalSelectionSyncBeMaster = function() {
}
// this.globalSelectionSyncDelay(100);
- }
+ };
// can the chart participate to the global selection sync as a slave?
this.globalSelectionSyncIsEligible = function() {
return true;
return false;
- }
+ };
// this chart becomes a slave of the global selection sync
this.globalSelectionSyncBeSlave = function() {
if(NETDATA.globalSelectionSync.state !== this)
NETDATA.globalSelectionSync.slaves.push(this);
- }
+ };
// sync all the visible charts to the given time
// this is to be called from the chart libraries
$.each(NETDATA.globalSelectionSync.slaves, function(i, st) {
st.setSelection(t);
});
- }
+ };
// stop syncing all charts to the given time
this.globalSelectionSyncStop = function() {
}
this.clearSelection();
- }
+ };
this.setSelection = function(t) {
if(typeof this.library.setSelection === 'function') {
this.log('selection set to ' + t.toString());
return this.selected;
- }
+ };
this.clearSelection = function() {
if(this.selected === true) {
}
return this.selected;
- }
+ };
// find if a timestamp (ms) is shown in the current chart
this.timeIsVisible = function(t) {
if(t >= this.data_after && t <= this.data_before)
return true;
return false;
- },
+ };
this.calculateRowForTime = function(t) {
if(this.timeIsVisible(t) === false) return -1;
return Math.floor((t - this.data_after) / this.data_update_every);
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// console logging
this.log = function(msg) {
console.log(this.id + ' (' + this.library_name + ' ' + this.uuid + '): ' + msg);
- }
+ };
this.pauseChart = function() {
if(this.paused === false) {
this.paused = true;
}
- }
+ };
this.unpauseChart = function() {
if(this.paused === true) {
this.paused = false;
}
- }
+ };
this.resetChart = function(dont_clear_master, dont_update) {
if(this.debug === true)
if(dont_update !== true && this.isVisible() === true) {
this.updateChart();
}
- }
+ };
this.updateChartPanOrZoom = function(after, before) {
var logme = 'updateChartPanOrZoom(' + after + ', ' + before + '): ';
this.log(logme);
if(before < after) {
- this.log(logme + 'flipped parameters, rejecting it.');
+ if(this.debug === true)
+ this.log(logme + 'flipped parameters, rejecting it.');
+
return false;
}
this.current.force_before_ms = before;
NETDATA.globalPanAndZoom.setMaster(this, after, before);
return ret;
- }
+ };
this.legendFormatValue = function(value) {
if(value === null || value === 'undefined') return '-';
if(abs >= 1 ) return (Math.round(value * 100) / 100).toLocaleString();
if(abs >= 0.1 ) return (Math.round(value * 1000) / 1000).toLocaleString();
return (Math.round(value * 10000) / 10000).toLocaleString();
- }
+ };
this.legendSetLabelValue = function(label, value) {
var series = this.element_legend_childs.series[label];
if(series.value !== null) series.value.innerHTML = s;
if(series.user !== null) series.user.innerHTML = r;
- }
+ };
this.legendSetDate = function(ms) {
if(typeof ms !== 'number') {
if(this.element_legend_childs.title_units)
this.element_legend_childs.title_units.innerHTML = this.units;
- }
+ };
this.legendShowUndefined = function() {
if(this.element_legend_childs.title_date)
this.legendSetLabelValue(label, null);
}
}
- }
+ };
this.legendShowLatestValues = function() {
if(this.chart === null) return;
else
this.legendSetLabelValue(label, this.data.view_latest_values[i]);
}
- }
+ };
this.legendReset = function() {
this.legendShowLatestValues();
- }
+ };
// this should be called just ONCE per dimension per chart
this._chartDimensionColor = function(label) {
this.colors.push(this.colors_assigned[label]);
return this.colors_assigned[label];
- }
+ };
this.chartColors = function() {
if(this.colors !== null) return this.colors;
this.colors = new Array();
this.colors_available = new Array();
+ var i, len;
var c = $(this.element).data('colors');
// this.log('read colors: ' + c);
var added = 0;
while(added < 20) {
- for(var i = 0, len = c.length; i < len ; i++) {
+ for(i = 0, len = c.length; i < len ; i++) {
added++;
this.colors_available.push(c[i]);
// this.log('adding color: ' + c[i]);
}
// push all the standard colors too
- for(var i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
+ for(i = 0, len = NETDATA.themes.current.colors.length; i < len ; i++)
this.colors_available.push(NETDATA.themes.current.colors[i]);
return this.colors;
- }
+ };
this.legendUpdateDOM = function() {
var needed = false;
if(user_id !== null) {
user_element = document.getElementById(user_id) || null;
if(user_element === null)
- me.log('Cannot find element with id: ' + user_id);
+ state.log('Cannot find element with id: ' + user_id);
}
state.element_legend_childs.series[name] = {
this.element_legend_childs = {
content: content,
resize_handler: document.createElement('div'),
+ toolbox: document.createElement('div'),
+ toolbox_left: document.createElement('div'),
+ toolbox_right: document.createElement('div'),
+ toolbox_reset: document.createElement('div'),
+ toolbox_zoomin: document.createElement('div'),
+ toolbox_zoomout: document.createElement('div'),
+ toolbox_volume: document.createElement('div'),
title_date: document.createElement('span'),
title_time: document.createElement('span'),
title_units: document.createElement('span'),
this.element_legend.innerHTML = '';
+ if(this.library.toolboxPanAndZoom !== null) {
+
+ function get_pan_and_zoom_step(event) {
+ if (event.ctrlKey)
+ return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_control;
+
+ else if (event.shiftKey)
+ return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_shift;
+
+ else if (event.altKey)
+ return NETDATA.options.current.pan_and_zoom_factor * NETDATA.options.current.pan_and_zoom_factor_multiplier_alt;
+
+ else
+ return NETDATA.options.current.pan_and_zoom_factor;
+ }
+
+ this.element_legend_childs.toolbox.className += ' netdata-legend-toolbox';
+ this.element.appendChild(this.element_legend_childs.toolbox);
+
+ this.element_legend_childs.toolbox_left.className += ' netdata-legend-toolbox-button';
+ this.element_legend_childs.toolbox_left.innerHTML = '<i class="fa fa-backward"></i>';
+ this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_left);
+ this.element_legend_childs.toolbox_left.onclick = function(e) {
+ e.preventDefault();
+
+ var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
+ var before = that.view_before - step;
+ var after = that.view_after - step;
+ if(after >= that.netdata_first)
+ that.library.toolboxPanAndZoom(that, after, before);
+ };
+ if(NETDATA.options.current.show_help === true)
+ $(this.element_legend_childs.toolbox_left).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ title: 'Pan Left',
+ content: 'Pan the chart to the left. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+ });
+
+
+ this.element_legend_childs.toolbox_reset.className += ' netdata-legend-toolbox-button';
+ this.element_legend_childs.toolbox_reset.innerHTML = '<i class="fa fa-play"></i>';
+ this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_reset);
+ this.element_legend_childs.toolbox_reset.onclick = function(e) {
+ e.preventDefault();
+ NETDATA.resetAllCharts(that);
+ };
+ if(NETDATA.options.current.show_help === true)
+ $(this.element_legend_childs.toolbox_reset).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ title: 'Chart Reset',
+ content: 'Reset all the charts to their default auto-refreshing state. You can also <b>double click</b> the chart contents with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+ });
+
+ this.element_legend_childs.toolbox_right.className += ' netdata-legend-toolbox-button';
+ this.element_legend_childs.toolbox_right.innerHTML = '<i class="fa fa-forward"></i>';
+ this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_right);
+ this.element_legend_childs.toolbox_right.onclick = function(e) {
+ e.preventDefault();
+ var step = (that.view_before - that.view_after) * get_pan_and_zoom_step(e);
+ var before = that.view_before + step;
+ var after = that.view_after + step;
+ if(before <= that.netdata_last)
+ that.library.toolboxPanAndZoom(that, after, before);
+ };
+ if(NETDATA.options.current.show_help === true)
+ $(this.element_legend_childs.toolbox_right).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ title: 'Pan Right',
+ content: 'Pan the chart to the right. You can also <b>drag it</b> with your mouse or your finger (on touch devices).<br/><small>Help, can be disabled from the settings.</small>'
+ });
+
+
+ this.element_legend_childs.toolbox_zoomin.className += ' netdata-legend-toolbox-button';
+ this.element_legend_childs.toolbox_zoomin.innerHTML = '<i class="fa fa-plus"></i>';
+ this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomin);
+ this.element_legend_childs.toolbox_zoomin.onclick = function(e) {
+ e.preventDefault();
+ var dt = ((that.view_before - that.view_after) * (get_pan_and_zoom_step(e) * 0.8) / 2);
+ var before = that.view_before - dt;
+ var after = that.view_after + dt;
+ that.library.toolboxPanAndZoom(that, after, before);
+ };
+ if(NETDATA.options.current.show_help === true)
+ $(this.element_legend_childs.toolbox_zoomin).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ title: 'Chart Zoom In',
+ content: 'Zoom in the chart. You can also press SHIFT and select an area of the chart to zoom in. On Chrome and Opera, you can press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
+ });
+
+ this.element_legend_childs.toolbox_zoomout.className += ' netdata-legend-toolbox-button';
+ this.element_legend_childs.toolbox_zoomout.innerHTML = '<i class="fa fa-minus"></i>';
+ this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_zoomout);
+ this.element_legend_childs.toolbox_zoomout.onclick = function(e) {
+ e.preventDefault();
+ var dt = (((that.view_before - that.view_after) / (1.0 - (get_pan_and_zoom_step(e) * 0.8)) - (that.view_before - that.view_after)) / 2);
+ var before = that.view_before + dt;
+ var after = that.view_after - dt;
+
+ that.library.toolboxPanAndZoom(that, after, before);
+ };
+ if(NETDATA.options.current.show_help === true)
+ $(this.element_legend_childs.toolbox_zoomout).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ title: 'Chart Zoom Out',
+ content: 'Zoom out the chart. On Chrome and Opera, you can also press the SHIFT or the ALT keys and then use the mouse wheel to zoom in or out.<br/><small>Help, can be disabled from the settings.</small>'
+ });
+
+ //this.element_legend_childs.toolbox_volume.className += ' netdata-legend-toolbox-button';
+ //this.element_legend_childs.toolbox_volume.innerHTML = '<i class="fa fa-sort-amount-desc"></i>';
+ //this.element_legend_childs.toolbox_volume.title = 'Visible Volume';
+ //this.element_legend_childs.toolbox.appendChild(this.element_legend_childs.toolbox_volume);
+ //this.element_legend_childs.toolbox_volume.onclick = function(e) {
+ //e.preventDefault();
+ //alert('clicked toolbox_volume on ' + that.id);
+ //}
+ }
+ else {
+ this.element_legend_childs.toolbox = null;
+ this.element_legend_childs.toolbox_left = null;
+ this.element_legend_childs.toolbox_reset = null;
+ this.element_legend_childs.toolbox_right = null;
+ this.element_legend_childs.toolbox_zoomin = null;
+ this.element_legend_childs.toolbox_zoomout = null;
+ this.element_legend_childs.toolbox_volume = null;
+ }
+
this.element_legend_childs.resize_handler.className += " netdata-legend-resize-handler";
this.element_legend_childs.resize_handler.innerHTML = '<i class="fa fa-chevron-up"></i><i class="fa fa-chevron-down"></i>';
this.element.appendChild(this.element_legend_childs.resize_handler);
+ if(NETDATA.options.current.show_help === true)
+ $(this.element_legend_childs.resize_handler).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ title: 'Chart Resize',
+ content: 'Drag this point with your mouse or your finger (on touch devices), to resize the chart vertically. You can also <b>double click it</b> or <b>double tap it</b> to reset between 2 states: the default and the one that fits all the values.<br/><small>Help, can be disabled from the settings.</small>'
+ });
// mousedown event
this.element_legend_childs.resize_handler.onmousedown =
content.className = 'netdata-legend-series-content';
this.element_legend_childs.nano.appendChild(content);
+
+ if(NETDATA.options.current.show_help === true)
+ $(content).popover({
+ container: "body",
+ animation: false,
+ html: true,
+ trigger: 'hover',
+ placement: 'bottom',
+ title: 'Chart Legend',
+ delay: { show: NETDATA.options.current.show_help_delay_show_ms, hide: NETDATA.options.current.show_help_delay_hide_ms },
+ content: 'You can click or tap on the values or the labels to select dimentions. By pressing SHIFT or CONTROL, you can enable or disable multiple dimensions.<br/><small>Help, can be disabled from the settings.</small>'
+ });
}
else {
this.element_legend_childs = {
content: content,
resize_handler: null,
+ toolbox: null,
+ toolbox_left: null,
+ toolbox_right: null,
+ toolbox_reset: null,
+ toolbox_zoomin: null,
+ toolbox_zoomout: null,
+ toolbox_volume: null,
title_date: null,
title_time: null,
title_units: null,
$(this.element_legend_childs.nano).nanoScroller(this.element_legend_childs.nano_options);
this.legendShowLatestValues();
- }
+ };
this.hasLegend = function() {
if(typeof this.___hasLegendCache___ !== 'undefined')
this.___hasLegendCache___ = leg;
return leg;
- }
+ };
this.legendWidth = function() {
return (this.hasLegend())?140:0;
- }
+ };
this.legendHeight = function() {
return $(this.element).height();
- }
+ };
this.chartWidth = function() {
return $(this.element).width() - this.legendWidth();
- }
+ };
this.chartHeight = function() {
return $(this.element).height();
- }
+ };
this.chartPixelsPerPoint = function() {
// force an options provided detail
px = NETDATA.options.current.pixels_per_point;
return px;
- }
+ };
this.needsRecreation = function() {
return (
&& this.library.autoresize() === false
&& this.tm.last_resized < NETDATA.options.last_resized
);
- }
+ };
this.chartURL = function() {
var after, before, points_multiplier = 1;
if(NETDATA.options.debug.chart_data_url === true || this.debug === true)
this.log('chartURL(): ' + this.data_url + ' WxH:' + this.chartWidth() + 'x' + this.chartHeight() + ' points: ' + this.data_points + ' library: ' + this.library_name);
- }
+ };
this.redrawChart = function() {
if(this.data !== null)
this.updateChartWithData(this.data);
- }
+ };
this.updateChartWithData = function(data) {
if(this.debug === true)
if(this.refresh_dt_element !== null)
this.refresh_dt_element.innerHTML = this.refresh_dt_ms.toString();
- }
+ };
this.updateChart = function(callback) {
if(this.debug === true)
error('data download failed for url: ' + that.data_url);
})
.always(function() {
- this._updating = false;
+ that._updating = false;
if(typeof callback === 'function') callback();
});
return true;
- }
+ };
this.isVisible = function(nocache) {
if(typeof nocache === 'undefined')
this.___isVisible___ = true;
return this.___isVisible___;
}
- }
+ };
this.isAutoRefreshed = function() {
return (this.current.autorefresh);
- }
+ };
this.canBeAutoRefreshed = function() {
- now = new Date().getTime();
+ var now = new Date().getTime();
if(this.enabled === false) {
if(this.debug === true)
}
return false;
- }
+ };
this.autoRefresh = function(callback) {
if(this.canBeAutoRefreshed() === true) {
if(typeof callback !== 'undefined')
callback();
}
- }
+ };
this._defaultsFromDownloadedChart = function(chart) {
this.chart = chart;
if(this.units === null)
this.units = chart.units;
- }
+ };
// fetch the chart description from the netdata server
this.getChart = function(callback) {
if(typeof callback === 'function') callback();
});
}
- }
+ };
// ============================================================================================================
// INITIALIZATION
init();
- }
+ };
NETDATA.resetAllCharts = function(state) {
// first clear the global selection sync
// if we were not the master, reset our status too
// this is required because most probably the mouse
- // is over this chart, blocking it from autorefreshing
+ // is over this chart, blocking it from auto-refreshing
if(master === false && (state.paused === true || state.selected === true))
state.resetChart();
- }
+ };
// get or create a chart state, given a DOM element
NETDATA.chartState = function(element) {
$(element).data('netdata-state-object', state);
}
return state;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// Library functions
}
else if(typeof callback === "function")
callback();
- }
+ };
NETDATA._loadCSS = function(filename) {
// don't use jQuery here
if (typeof fileref !== 'undefined')
document.getElementsByTagName("head")[0].appendChild(fileref);
- }
+ };
NETDATA.colorHex2Rgb = function(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
- }
+ };
NETDATA.colorLuminance = function(hex, lum) {
// validate hex string
}
return rgb;
- }
+ };
NETDATA.guid = function() {
function s4() {
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
- }
+ };
NETDATA.zeropad = function(x) {
if(x > -10 && x < 10) return '0' + x.toString();
else return x.toString();
- }
+ };
// user function to signal us the DOM has been
// updated.
NETDATA.updatedDom = function() {
NETDATA.options.updated_dom = true;
- }
+ };
NETDATA.ready = function(callback) {
NETDATA.options.pauseCallback = callback;
- }
+ };
NETDATA.pause = function(callback) {
if(NETDATA.options.pause === true)
callback();
else
NETDATA.options.pauseCallback = callback;
- }
+ };
NETDATA.unpause = function() {
NETDATA.options.pauseCallback = null;
NETDATA.options.updated_dom = true;
NETDATA.options.pause = false;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
}, NETDATA.options.current.idle_between_charts);
}
}
- }
+ };
// this is part of the parallel refresher
// its cause is to refresh sequencially all the charts
// it will call the parallel refresher back
// as soon as it sees a chart that its chart library
// is initialized
- NETDATA.chartRefresher_unitialized = function() {
+ NETDATA.chartRefresher_uninitialized = function() {
if(NETDATA.options.updated_dom === true) {
// the dom has been updated
// get the dom parts again
if(state.library.initialized === true)
NETDATA.chartRefresher();
else
- state.autoRefresh(NETDATA.chartRefresher_unitialized);
+ state.autoRefresh(NETDATA.chartRefresher_uninitialized);
}
- }
+ };
NETDATA.chartRefresherWaitTime = function() {
return NETDATA.options.current.idle_parallel_loops;
- }
+ };
// the default refresher
// it will create 2 sets of charts:
// - the ones that can be refreshed in parallel
// - the ones that depend on something else
// the first set will be executed in parallel
- // the second will be given to NETDATA.chartRefresher_unitialized()
+ // the second will be given to NETDATA.chartRefresher_uninitialized()
NETDATA.chartRefresher = function() {
if(NETDATA.options.pause === true) {
// console.log('auto-refresher is paused');
setTimeout(NETDATA.chartRefresher,
NETDATA.chartRefresherWaitTime());
}
- }
+ };
NETDATA.parseDom = function(callback) {
NETDATA.options.last_page_scroll = new Date().getTime();
}
if(typeof callback === 'function') callback();
- }
+ };
// this is the main function - where everything starts
NETDATA.start = function() {
$('.modal').on('shown.bs.modal', NETDATA.onscroll);
NETDATA.parseDom(NETDATA.chartRefresher);
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// peity
$(state.peity_instance).peity('line', state.peity_options);
return true;
- }
+ };
NETDATA.peityChartCreate = function(state, data) {
state.peity_instance = document.createElement('div');
NETDATA.peityChartUpdate(state, data);
return true;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// sparkline
$(state.element_chart).sparkline(data.result, state.sparkline_options);
return true;
- }
+ };
NETDATA.sparklineChartCreate = function(state, data) {
var self = $(state.element);
var drawNormalOnTop = self.data('sparkline-drawnormalontop') || undefined;
var xvalues = self.data('sparkline-xvalues') || undefined;
var chartRangeClip = self.data('sparkline-chartrangeclip') || undefined;
- var xvalues = self.data('sparkline-xvalues') || undefined;
var chartRangeMinX = self.data('sparkline-chartrangeminx') || undefined;
var chartRangeMaxX = self.data('sparkline-chartrangemaxx') || undefined;
var disableInteraction = self.data('sparkline-disableinteraction') || false;
smooth: false
};
+ NETDATA.dygraphToolboxPanAndZoom = function(state, after, before) {
+ if(after < state.netdata_first)
+ after = state.netdata_first;
+
+ if(before > state.netdata_last)
+ before = state.netdata_last;
+
+ state.setMode('zoom');
+ state.globalSelectionSyncStop();
+ state.globalSelectionSyncDelay();
+ state.dygraph_user_action = true;
+ state.dygraph_force_zoom = true;
+ state.updateChartPanOrZoom(after, before);
+ NETDATA.globalPanAndZoom.setMaster(state, after, before);
+ };
+
NETDATA.dygraphSetSelection = function(state, t) {
if(typeof state.dygraph_instance !== 'undefined') {
var r = state.calculateRowForTime(t);
if(typeof callback === "function")
callback();
});
- }
+ };
NETDATA.dygraphInitialize = function(callback) {
if(typeof netdataNoDygraphs === 'undefined' || !netdataNoDygraphs) {
}
return pcent;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// easy-pie-chart
state.easyPieChartEvent = {
timer: null,
value: 0,
- pcent: 0,
+ pcent: 0
};
}
state.gauge_instance.animationSpeed = speed;
state.___gaugeOld__.speed = speed;
- }
+ };
NETDATA.gaugeSet = function(state, value, min, max) {
if(typeof value !== 'number') value = 0;
NETDATA.gaugeChartCreate = function(state, data) {
var self = $(state.element);
- var chart = $(state.element_chart);
+ // var chart = $(state.element_chart);
var value = data.result[0];
var max = self.data('gauge-max-value') || null;
},
setSelection: NETDATA.dygraphSetSelection,
clearSelection: NETDATA.dygraphClearSelection,
+ toolboxPanAndZoom: NETDATA.dygraphToolboxPanAndZoom,
initialized: false,
enabled: true,
format: function(state) { return 'json'; },
resize: null,
setSelection: undefined, // function(state, t) { return true; },
clearSelection: undefined, // function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'array'; },
resize: null,
setSelection: undefined, // function(state, t) { return true; },
clearSelection: undefined, // function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'ssvcomma'; },
resize: null,
setSelection: undefined, // function(state, t) { return true; },
clearSelection: undefined, // function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'json'; },
resize: null,
setSelection: undefined, //function(state, t) { return true; },
clearSelection: undefined, //function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'datatable'; },
resize: null,
setSelection: undefined, // function(state, t) { return true; },
clearSelection: undefined, // function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'json'; },
resize: null,
setSelection: undefined, // function(state, t) { return true; },
clearSelection: undefined, // function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'csvjsonarray'; },
resize: null,
setSelection: undefined, // function(state, t) { return true; },
clearSelection: undefined, // function(state) { return true; },
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'json'; },
resize: null,
setSelection: NETDATA.easypiechartSetSelection,
clearSelection: NETDATA.easypiechartClearSelection,
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'array'; },
resize: null,
setSelection: NETDATA.gaugeSetSelection,
clearSelection: NETDATA.gaugeClearSelection,
+ toolboxPanAndZoom: null,
initialized: false,
enabled: true,
format: function(state) { return 'array'; },
NETDATA.chartLibraries[library].url = url;
NETDATA.chartLibraries[library].initialized = true;
NETDATA.chartLibraries[library].enabled = true;
- }
+ };
// ----------------------------------------------------------------------------------------------------------------
// Start up
.fail(function() {
alert('Cannot load required JS library: ' + NETDATA.requiredJs[index].url);
})
- }
+ };
NETDATA.loadRequiredCSS = function(index) {
if(index >= NETDATA.requiredCSS.length)
NETDATA._loadCSS(NETDATA.requiredCSS[index].url);
NETDATA.loadRequiredCSS(++index);
- }
+ };
NETDATA.errorReset();
NETDATA.loadRequiredCSS(0);
margin: 0px;
}
+.netdata-legend-toolbox {
+ display: block;
+ position: absolute;
+ bottom: 0px;
+ right: 30px;
+ height: 15px;
+ width: 110px;
+ background-color: #272b30;
+ font-size: 12px;
+ vertical-align: middle;
+ line-height: 15px;
+ color: #373b40;
+ text-align: center;
+ overflow: hidden;
+ z-index: 20;
+ padding: 0px;
+ margin: 0px;
+}
+
+.netdata-legend-toolbox-button {
+ display: inline-block;
+ position: relative;
+ height: 15px;
+ width: 18px;
+ background-color: #272b30;
+ font-size: 12px;
+ vertical-align: middle;
+ line-height: 15px;
+ color: #474b50;
+ text-align: center;
+ overflow: hidden;
+ z-index: 21;
+ padding: 0px;
+ margin: 0px;
+ cursor: pointer;
+}
+
.netdata-message {
display: inline-block;
text-align: left;
color: #676b70;
font-weight: normal;
}
+
+.popover-title {
+ font-weight: bold;
+ font-size: 12px;
+}
+.popover-content {
+ font-size: 11px;
+}
<style>
- /* prevent body from hidding under the navbar */
+ /* prevent body from hiding under the navbar */
body {
padding-top: 50px;
}
</script>
<!-- load the dashboard manager - it will do the rest -->
- <script type="text/javascript" src="dashboard.js?v26"></script>
+ <script type="text/javascript" src="dashboard.js?v32"></script>
</head>
<body data-spy="scroll" data-target="#sidebar">
</div>
<div class="modal-body">
<div class="p">
- <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any type of realtime timeseries data, from CPU usage, disk activity, SQL queries, API calls, web site visitors, etc.
+ <b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> is the fastest way to visualize metrics. It is a resource efficient, highly optimized system for collecting and visualizing any type of real-time time series data, from CPU usage, disk activity, SQL queries, API calls, web site visitors, etc.
</div>
<div class="p">
<b><a href="https://github.com/firehol/netdata/wiki" target="_blank">netdata</a></b> tries to visualize the truth of <b>now</b>, in its <b>greatest detail</b>, so that you can get insights of what is happening now and what just happened, on your systems and applications.
While pressing the shift key, click on the contents of a chart and move the mouse to select an area, to zoom in. The other charts will follow too. Zooming is performed in two phases:
<ul>
<li>The already loaded chart contents are zoomed (low resolution)</li>
- <li>New data are transfered from the netdata server, to refresh the chart with possibly more detail.</li>
+ <li>New data are transferred from the netdata server, to refresh the chart with possibly more detail.</li>
</ul>
Once a chart is zoomed, auto refreshing stops for all charts. To enable it again, <b>double click</b> a zoomed chart.
</div>
<hr/>
<div class="p">
<h4>Legend Operations</h4>
- Click on the label or value of a dimension, will select / unselect this dimension.
+ Click on the label or value of a dimension, will select / un-select this dimension.
<br/>
- You can press any of the SHIFT or CONTROL keys and then click on legend labels or values, to select / unselect multiple dimensions.
+ You can press any of the SHIFT or CONTROL keys and then click on legend labels or values, to select / un-select multiple dimensions.
</div>
</div>
<div role="tabpanel" class="tab-pane" id="help_touch">
<hr/>
<div class="p">
<h4>Legend Operations</h4>
- Tap on the label or value of a dimension, will select / unselect this dimension.
+ Tap on the label or value of a dimension, will select / un-select this dimension.
</div>
</div>
</div>
<input id="eliminate_zero_dimensions" type="checkbox" checked data-toggle="toggle" data-on="Non Zero" data-off="All" data-width="110px">
</td>
<td class="option-info"><strong>Which dimensions to show?</strong><br/>
- <small>When set to <b>Non Zero</b>, dimensions that have all their values (within the current view) set to zero will not be transfered from the netdata server (except if all dimensions of the chart are zero, in which case this setting does nothing - all dimensions are transfered and shown). When set to <b>All</b>, all dimensions will always be shown. Set it to <b>Non Zero</b> to lower the data transfered between netdata and your browser, lower the CPU requirements of your browser (fewer lines to draw) and increase the focus on the legends (fewer entries at the legends).</small>
+ <small>When set to <b>Non Zero</b>, dimensions that have all their values (within the current view) set to zero will not be transferred from the netdata server (except if all dimensions of the chart are zero, in which case this setting does nothing - all dimensions are transferred and shown). When set to <b>All</b>, all dimensions will always be shown. Set it to <b>Non Zero</b> to lower the data transferred between netdata and your browser, lower the CPU requirements of your browser (fewer lines to draw) and increase the focus on the legends (fewer entries at the legends).</small>
</td>
</tr>
<tr class="option-row">
<div class="form-group">
<table>
<tr class="option-row">
- <td class="option-control"><input id="parallel_refresher" type="checkbox" checked data-toggle="toggle" data-on="Parallel" data-off="Sequencial" data-width="110px"></td>
+ <td class="option-control"><input id="parallel_refresher" type="checkbox" checked data-toggle="toggle" data-on="Parallel" data-off="Sequential" data-width="110px"></td>
<td class="option-info"><strong>Which chart refresh policy to use?</strong><br/>
- <small>When set to <b>parallel</b>, visible charts are refreshed in parallel (all queries are sent to netdata server in parallel) and are rendered asynchronously. When set to <b>sequencial</b> charts are refreshed one after another. Set it to parallel if your browser can cope with it (most modern browsers do), set it to sequencial if you work on an older/slower computer.</small>
+ <small>When set to <b>parallel</b>, visible charts are refreshed in parallel (all queries are sent to netdata server in parallel) and are rendered asynchronously. When set to <b>sequential</b> charts are refreshed one after another. Set it to parallel if your browser can cope with it (most modern browsers do), set it to sequential if you work on an older/slower computer.</small>
</td>
</tr>
<tr class="option-row" id="concurrent_refreshes_row">
<tr class="option-row">
<td class="option-control"><input id="netdata_theme_control" type="checkbox" checked data-toggle="toggle" data-offstyle="danger" data-onstyle="success" data-on="Dark" data-off="White" data-width="110px"></td>
<td class="option-info"><strong>Which theme to use?</strong><br/>
- <small>Netdata comes with two themes: <b>Dark</b> (the default) and <b>White</b>.</small>
+ <small>Netdata comes with two themes: <b>Dark</b> (the default) and <b>White</b>.
+ <br/>
+ <b>Switching this will reload the dashboard</b>.
+ </small>
+ </td>
+ </tr>
+ <tr class="option-row">
+ <td class="option-control"><input id="show_help" type="checkbox" checked data-toggle="toggle" data-on="Help Me" data-off="No Help" data-width="110px"></td>
+ <td class="option-info"><strong>Do you need help?</strong><br/>
+ <small>Netdata can show some help in some areas to help you use the dashboard. If all these balloons bother you, disable them using this switch.
+ <br/>
+ <b>Switching this will reload the dashboard</b>.
+ </small>
</td>
</tr>
<tr class="option-row">
<tr class="option-row">
<td class="option-control"><input id="smooth_plot" type="checkbox" checked data-toggle="toggle" data-on="Smooth" data-off="Rough" data-width="110px"></td>
<td class="option-info"><strong>Enable Bézier lines on charts?</strong><br/>
- <small>When set to <b>Smooth</b> the charts libraries that support it, will plot smooth curves instead of simple straight lines to connect the points.</small>
+ <small>When set to <b>Smooth</b> the charts libraries that support it, will plot smooth curves instead of simple straight lines to connect the points.
+ <br/>
+ Keep in mind <a href="http://dygraphs.com" target="_blank">dygraphs</a>, the main charting library in netdata dashboards, can only smooth line charts. It cannot smooth area or stacked charts. When set to <b>Rough</b>, this setting can lower the CPU resources consumed by your browser.</small>
</td>
</tr>
</table>
<script>
-var demo_hostname = 'netdata.firehol.org';
-// var demo_hostname = 'box';
+var this_is_demo = null;
+function isdemo() {
+ if(this_is_demo !== null) return this_is_demo;
+ this_is_demo = false;
+
+ try {
+ if(typeof document.location.hostname === 'string')
+ this_is_demo = document.location.hostname.endsWith('.firehol.org');
+ }
+ catch(error) {
+ ;
+ }
+
+ return this_is_demo;
+}
-if(document.location.hostname === demo_hostname) {
+if(isdemo()) {
document.getElementById('masthead').style.display = 'block';
}
'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
},
var submenuData = {
'mem.ksm': {
title: 'Memory Deduper',
- info: 'Kernel Same-page Merging (KSM) is the kernel memory de-duper.'
+ info: 'Kernel Same-page Merging (KSM) performance monitoring, read from several files in <code>/sys/kernel/mm/ksm/</code>. KSM is a memory-saving de-duplication feature in the Linux kernel (since version 2.6.32). The KSM daemon ksmd periodically scans those areas of user memory which have been registered with it, looking for pages of identical content which can be replaced by a single write-protected page (which is automatically copied if a process later wants to update its content). KSM was originally developed for use with KVM (where it was known as Kernel Shared Memory), to fit more virtual machines into physical memory, by sharing the data common between them. But it can be useful to any application which generates many instances of the same data.'
},
'netfilter.conntrack': {
title: 'Connection Tracker',
- info: 'The following information is taken from <code>/proc/net/stat/nf_conntrack</code>. The connection tracker keeps track of all connections of the machine, inbound and outbound. It works by keeping a database with all open connections, tracking network and address translation and connection expectations.'
+ info: 'Netfilter Connection Tracker performance monitoring, read from <code>/proc/net/stat/nf_conntrack</code>. The connection tracker keeps track of all connections of the machine, inbound and outbound. It works by keeping a database with all open connections, tracking network and address translation and connection expectations.'
},
'netfilter.nfacct': {
},
'netfilter.synproxy': {
- title: 'Anti-DDoS Protection',
- info: 'The following information is taken from <code>/proc/net/stat/synproxy</code>. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.'
+ title: 'DDoS Protection',
+ info: 'DDoS Protection performance monitoring read from <code>/proc/net/stat/synproxy</code>. <a href="https://github.com/firehol/firehol/wiki/Working-with-SYNPROXY" target="_blank">SYNPROXY</a> is a TCP SYN packets proxy. It is used to protect any TCP server (like a web server) from SYN floods and similar DDoS attacks. It is a netfilter module, in the Linux kernel (since version 3.12). It is optimized to handle millions of packets per second utilizing all CPUs available without any concurrency locking between the connections. It can be used for any kind of TCP traffic (even encrypted), since it does not interfere with the content itself.'
}
};
'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': {
// ----------------------------------------------------------------------------
+function getUrlParameter(sParam) {
+ var sPageURL = decodeURIComponent(window.location.search.substring(1)),
+ sURLVariables = sPageURL.split('&'),
+ sParameterName,
+ i;
+
+ for (i = 0; i < sURLVariables.length; i++) {
+ sParameterName = sURLVariables[i].split('=');
+
+ if (sParameterName[0] === sParam) {
+ return sParameterName[1] === undefined ? true : sParameterName[1];
+ }
+ }
+}
+
function finalizePage() {
// resize all charts - without starting the background thread
// this has to be done while NETDATA is paused
// the Dom elements are initially zero-sized
NETDATA.parseDom();
+ var before = 0, after = 0;
+ after = getUrlParameter('force_after_ms');
+ before = getUrlParameter('force_before_ms');
+
+ if(before > 0 && after > 0)
+ NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], after, before);
+
// let it run (update the charts)
NETDATA.unpause();
/* activate bootstrap sidebar (affix) */
$('#sidebar').affix({
offset: {
- top: (document.location.hostname === demo_hostname)?150:0,
+ top: (isdemo())?150:0,
bottom: 0
}
});
sync_option('stop_updates_when_focus_is_lost');
sync_option('smooth_plot');
sync_option('pan_and_zoom_data_padding');
+ sync_option('show_help');
theme_sync_option('netdata_theme_control');
if(NETDATA.getOption('parallel_refresher') === false) {
$('#stop_updates_when_focus_is_lost').change(function() { NETDATA.setOption('stop_updates_when_focus_is_lost', $(this).prop('checked')); });
$('#smooth_plot').change(function() { NETDATA.setOption('smooth_plot', $(this).prop('checked')); });
$('#pan_and_zoom_data_padding').change(function() { NETDATA.setOption('pan_and_zoom_data_padding', $(this).prop('checked')); });
+ $('#show_help').change(function() { NETDATA.setOption('show_help', $(this).prop('checked')); location.reload(); });
// this has to be the last
// it reloads the page
location.reload();
});
- if(document.location.hostname === demo_hostname) {
+ if(isdemo()) {
setTimeout(function() {
$('#welcomeModal').modal();
}, 1000);
$('#updateModal').on('shown.bs.modal', function() {
notifyForUpdate(true);
- })
+ });
}
function resetDashboardOptions() {
+ var help = NETDATA.options.current.show_help;
+
NETDATA.resetOptions();
if(setTheme('slate'))
location.reload();
+
+ if(help !== NETDATA.options.current.show_help)
+ location.reload();
}
downloadAllCharts();
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: