]> arthur.barton.de Git - netdata.git/commitdiff
build: migrate to autotools
authorAlon Bar-Lev <alon.barlev@gmail.com>
Sun, 15 Nov 2015 16:40:17 +0000 (18:40 +0200)
committerAlon Bar-Lev <alon.barlev@gmail.com>
Sun, 15 Nov 2015 21:43:24 +0000 (23:43 +0200)
Signed-off-by: Alon Bar-Lev <alon.barlev@gmail.com>
59 files changed:
.gitignore
Makefile [deleted file]
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
charts.d/Makefile.am [new file with mode: 0644]
conf.d/Makefile.am [new file with mode: 0644]
configure.ac [new file with mode: 0644]
m4/ax_pthread.m4 [new file with mode: 0644]
netdata.spec.in [new file with mode: 0644]
plugins.d/Makefile.am [new file with mode: 0644]
plugins.d/loopsleepms.sh.inc [changed mode: 0755->0644]
src/.keep [new file with mode: 0644]
src/Makefile [deleted file]
src/Makefile.am [new file with mode: 0644]
src/appconfig.c [new file with mode: 0755]
src/appconfig.h [new file with mode: 0755]
src/apps_plugin.c [new file with mode: 0755]
src/avl.c
src/common.c
src/common.h
src/config.c [deleted file]
src/config.h [deleted file]
src/daemon.c
src/dictionary.c
src/global_statistics.c
src/log.c
src/main.c
src/plugin_checks.c
src/plugin_idlejitter.c
src/plugin_nfacct.c
src/plugin_proc.c
src/plugin_tc.c
src/plugins.d/Makefile [deleted file]
src/plugins.d/apps_plugin.c [deleted file]
src/plugins_d.c
src/popen.c
src/proc_diskstats.c
src/proc_interrupts.c
src/proc_meminfo.c
src/proc_net_dev.c
src/proc_net_ip_vs_stats.c
src/proc_net_netstat.c
src/proc_net_rpc_nfsd.c
src/proc_net_snmp.c
src/proc_net_stat_conntrack.c
src/proc_stat.c
src/proc_sys_kernel_random_entropy_avail.c
src/proc_vmstat.c
src/procfile.c
src/rrd.c
src/rrd2json.c
src/storage_number.c
src/storage_number.h
src/unit_test.c
src/url.c
src/web_buffer.c
src/web_client.c
src/web_server.c
web/Makefile.am [new file with mode: 0644]

index c4c9fb9dc2239099c1f2ccd6b3dbcf82068260c1..81989e7a2a89c1238b2442795d3b1fa245967079 100644 (file)
@@ -1,7 +1,27 @@
-conf.d/*
-log/*
-cache/*
+.deps
+.libs
+
 *.o
-*.old
+*.in
+*~
+
+Makefile
+aclocal.m4
+autom4te.cache
+compile
+config.guess
+config.h
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+
 netdata
 apps.plugin
+netdata.spec
diff --git a/Makefile b/Makefile
deleted file mode 100755 (executable)
index 052dff9..0000000
--- a/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-ifndef BIN_DIR\r
-BIN_DIR = "$(PWD)"\r
-endif\r
-\r
-ifndef CONFIG_DIR\r
-CONFIG_DIR = "$(PWD)/conf.d"\r
-endif\r
-\r
-ifndef LOG_DIR\r
-LOG_DIR = "$(PWD)/log"\r
-endif\r
-\r
-ifndef PLUGINS_DIR\r
-PLUGINS_DIR = "$(PWD)/plugins.d"\r
-endif\r
-\r
-COMMON_FLAGS = BIN_DIR='$(BIN_DIR)' CONFIG_DIR='$(CONFIG_DIR)' LOG_DIR='$(LOG_DIR)' PLUGINS_DIR='$(PLUGINS_DIR)'\r
-\r
-ifdef debug\r
-COMMON_FLAGS += debug=1\r
-endif\r
-\r
-ifdef nozlib\r
-COMMON_FLAGS += nozlib=1\r
-endif\r
-\r
-all:\r
-       $(MAKE) -C src $(COMMON_FLAGS) all\r
-\r
-clean:\r
-       $(MAKE) -C src clean\r
-       -rm -f netdata netdata.old plugins.d/apps.plugin plugins.d/apps.plugin.old\r
-\r
-install:\r
-       $(MAKE) -C src $(COMMON_FLAGS) install\r
-\r
-getconf:\r
-       @wget -O conf.d/netdata.conf.new "http://localhost:19999/netdata.conf"; \\r
-       if [ $$? -eq 0 -a -s conf.d/netdata.conf.new ]; \\r
-       then \\r
-               mv conf.d/netdata.conf conf.d/netdata.conf.old; \\r
-               mv conf.d/netdata.conf.new conf.d/netdata.conf; \\r
-       fi\r
-\r
-.PHONY: all clean install getconf\r
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..5f7e787
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+AUTOMAKE_OPTIONS=foreign dist-bzip2 1.10
+ACLOCAL_AMFLAGS = -I m4
+
+MAINTAINERCLEANFILES= \
+       config.log config.status \
+       $(srcdir)/Makefile.in \
+       $(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \
+       $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \
+       $(srcdir)/compile $(srcdir)/depcomp $(srcdir)/aclocal.m4 \
+       $(srcdir)/config.guess $(srcdir)/config.sub \
+       $(srcdir)/m4/ltsugar.m4 $(srcdir)/m4/libtool.m4 \
+       $(srcdir)/m4/ltversion.m4 $(srcdir)/m4/lt~obsolete.m4 \
+       $(srcdir)/m4/ltoptions.m4 \
+       $(srcdir)/pkcs11-helper.spec $(srcdir)/config-w32-vc.h
+
+EXTRA_DIST = README.md
+
+SUBDIRS = \
+       charts.d \
+       conf.d \
+       plugins.d \
+       src \
+       web \
+       $(NULL)
+
+dist_noinst_DATA = netdata.spec
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..3b076d1
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+autoreconf -ivf
diff --git a/charts.d/Makefile.am b/charts.d/Makefile.am
new file mode 100644 (file)
index 0000000..431b0b8
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+dist_charts_SCRIPTS = \
+       airsearches.chart.sh \
+       cpu_apps.chart.sh \
+       cpufreq.chart.sh \
+       crsproxy.chart.sh \
+       example.chart.sh \
+       load_average.chart.sh \
+       mem_apps.chart.sh \
+       mysql.chart.sh \
+       nut.chart.sh \
+       opensips.chart.sh \
+       postfix.chart.sh \
+       sensors.chart.sh \
+       squid.chart.sh \
+       $(NULL)
diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am
new file mode 100644 (file)
index 0000000..73e45c1
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+dist_config_DATA = \
+       apps_groups.conf \
+       $(NULL)
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..fbfbec6
--- /dev/null
@@ -0,0 +1,163 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+AC_PREREQ(2.60)
+
+define([VERSION_MAJOR], [1])
+define([VERSION_MINOR], [0])
+define([VERSION_FIX], [0])
+define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX)
+define([VERSION_SUFFIX], [_master])
+
+AC_INIT([netdata], VERSION_NUMBER[]VERSION_SUFFIX)
+PACKAGE_RPM_VERSION="VERSION_NUMBER"
+PACKAGE_RPM_RELEASE="0.0.$(echo VERSION_SUFFIX | sed 's/^_//')"
+AC_SUBST([PACKAGE_RPM_VERSION])
+AC_SUBST([PACKAGE_RPM_RELEASE])
+
+AC_CONFIG_AUX_DIR([.])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([src/main.c])
+AM_INIT_AUTOMAKE
+AC_CANONICAL_HOST
+AC_PROG_CC
+AC_PROG_INSTALL
+PKG_PROG_PKG_CONFIG
+AC_USE_SYSTEM_EXTENSIONS
+
+AC_ARG_ENABLE(
+       [daemon],
+       [AS_HELP_STRING([--disable-daemon], [disable daemon])],
+       ,
+       [enable_daemon="yes"]
+)
+AC_ARG_ENABLE(
+       [plugin-plugin-nfacct],
+       [AS_HELP_STRING([--enable-plugin-plugin-nfacct], [enable nfacct plugin, requires root])],
+       ,
+       [enable_plugin_plugin_nfacct="no"]
+)
+AC_ARG_ENABLE(
+       [pedantic],
+       [AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings])],
+       ,
+       [enable_pedantic="no"]
+)
+AC_ARG_WITH(
+       [webdir],
+       [AS_HELP_STRING([--with-webdir], [location of webdir @<:@PKGDATADIR/web@:>@])],
+       [webdir="${withval}"],
+       [webdir="\$(pkgdatadir)/web"]
+)
+AC_ARG_WITH(
+       [zlib],
+       [AS_HELP_STRING([--with-zlib], [build with zlib])],
+       ,
+       [with_zlib="yes"]
+)
+AC_ARG_WITH(
+       [math],
+       [AS_HELP_STRING([--with-math], [build with math])],
+       ,
+       [with_math="yes"]
+)
+AC_ARG_WITH(
+       [user],
+       [AS_HELP_STRING([--with-user], [use this user to drop privilege])],
+       ,
+       [with_user="nobody"]
+)
+
+ACX_PTHREAD(, [AC_MSG_ERROR([Cannot initialize pthread environment])])
+LIBS="${PTHREAD_LIBS} ${LIBS}"
+CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}"
+CC="${PTHREAD_CC}"
+
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_C_INLINE
+
+PKG_CHECK_MODULES(
+       [NFACCT],
+       [libnetfilter_acct],
+)
+PKG_CHECK_MODULES(
+       [LIBMNL],
+       [libmnl],
+)
+PKG_CHECK_MODULES(
+       [ZLIB],
+       [zlib],
+)
+AC_ARG_VAR([MATH_CFLAGS], [C compiler flags for math])
+AC_ARG_VAR([MATH_LIBS], [linker flags for math])
+if test -z "${MATH_LIBS}"; then
+       AC_CHECK_LIB(
+               [m],
+               [sin],
+               [MATH_LIBS="-lm"]
+       )
+fi
+
+test "${enable_daemon}" = "yes" && AC_DEFINE([NETDATA_DAEMON], [1], [daemon settings])
+if test "${enable_plugin_plugin_nfacct}" = "yes"; then
+       test -z "${NFACCT_LIBS}" && AC_MSG_ERROR([netfilter_acct required but not found])
+       test -z "${LIBMNL_LIBS}" && AC_MSG_ERROR([libmnl required but not found])
+       AC_DEFINE([INTERNAL_PLUGIN_NFACCT], [1], [nfacct plugin settings])
+       OPTIONAL_NFACCT_CLFAGS="${NFACCT_CFLAGS} ${LIBMNL_CFLAGS}"
+       OPTIONAL_NFACCT_LIBS="${NFACCT_LIBS} ${LIBMNL_LIBS}"
+fi
+if test "${with_zlib}" = "yes"; then
+       test -z "${ZLIB_LIBS}" && AC_MSG_ERROR([zlib required but not found])
+       AC_DEFINE([NETDATA_WITH_ZLIB], [1], [zlib settings])
+       OPTIONAL_ZLIB_CLFAGS="${ZLIB_CFLAGS}"
+       OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}"
+fi
+if test "${with_math}" = "yes"; then
+       test -z "${MATH_LIBS}" && AC_MSG_ERROR([math required but not found])
+       AC_DEFINE([STORAGE_WITH_MATH], [1], [math settings])
+       OPTIONAL_MATH_CFLAGS="${MATH_CFLAGS}"
+       OPTIONAL_MATH_LIBS="${MATH_LIBS}"
+fi
+
+if test "${GCC}" = "yes"; then
+       AC_DEFINE_UNQUOTED([likely(x)], [__builtin_expect(!!(x), 1)], [gcc branch optimization])
+       AC_DEFINE_UNQUOTED([unlikely(x)], [__builtin_expect(!!(x), 0)], [gcc branch optimization])
+else
+       AC_DEFINE_UNQUOTED([likely(x)], [(x)], [gcc branch optimization])
+       AC_DEFINE_UNQUOTED([unlikely(x)], [(x)], [gcc branch optimization])
+fi
+
+if test "${enable_pedantic}" = "yes"; then
+       enable_strict="yes"
+       CFLAGS="${CFLAGS} -pedantic -Wall -Wextra -Wno-long-long"
+fi
+
+AC_DEFINE_UNQUOTED([NETDATA_USER], ["${with_user}"], [use this user to drop privileged])
+
+AC_SUBST([cachedir], ["\$(localstatedir)/cache/netdata"])
+AC_SUBST([chartsdir], ["\$(libexecdir)/netdata/charts.d"])
+AC_SUBST([configdir], ["\$(sysconfdir)/netdata"])
+AC_SUBST([logdir], ["\$(localstatedir)/log/netdata"])
+AC_SUBST([pluginsdir], ["\$(libexecdir)/netdata/plugins.d"])
+AC_SUBST([webdir])
+
+AC_SUBST([OPTIONAL_MATH_CLFAGS])
+AC_SUBST([OPTIONAL_MATH_LIBS])
+AC_SUBST([OPTIONAL_NFACCT_CLFAGS])
+AC_SUBST([OPTIONAL_NFACCT_LIBS])
+AC_SUBST([OPTIONAL_ZLIB_CLFAGS])
+AC_SUBST([OPTIONAL_ZLIB_LIBS])
+
+AC_CONFIG_FILES([
+       Makefile
+       charts.d/Makefile
+       conf.d/Makefile
+       netdata.spec
+       plugins.d/Makefile
+       src/Makefile
+       web/Makefile
+])
+AC_OUTPUT
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
new file mode 100644 (file)
index 0000000..d383ad5
--- /dev/null
@@ -0,0 +1,332 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 21
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
+        AC_MSG_RESULT([$ax_pthread_ok])
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+# Clang doesn't consider unrecognized options an error unless we specify
+# -Werror. We throw in some extra Clang-specific options to ensure that
+# this doesn't happen for GCC, which also accepts -Werror.
+
+AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
+save_CFLAGS="$CFLAGS"
+ax_pthread_extra_flags="-Werror"
+CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
+                  [AC_MSG_RESULT([yes])],
+                  [ax_pthread_extra_flags=
+                   AC_MSG_RESULT([no])])
+CFLAGS="$save_CFLAGS"
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT([$ax_pthread_ok])
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT([$attr_name])
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                # TODO: What about Clang on Solaris?
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT([$flag])
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            [ax_cv_PTHREAD_PRIO_INHERIT], [
+                AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+                                                [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: compile with *_r variant
+        if test "x$GCC" != xyes; then
+            case $host_os in
+                aix*)
+                AS_CASE(["x/$CC"],
+                  [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+                  [#handle absolute path differently from PATH based program lookup
+                   AS_CASE(["x$CC"],
+                     [x/*],
+                     [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+                     [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                ;;
+            esac
+        fi
+fi
+
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/netdata.spec.in b/netdata.spec.in
new file mode 100644 (file)
index 0000000..af73a7b
--- /dev/null
@@ -0,0 +1,58 @@
+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
+BuildRequires: libnetfilter_acct-devel
+BuildRequires: zlib-devel
+Requires:      libmnl
+Requires:      libnetfilter_acct
+Requires:      zlib
+
+BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+%description
+Linux real time system monitoring, over the web!
+
+%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
+
+%global ovirt_create_user_engine \
+%_ovirt_create_user %{engine_user} %{engine_uid} %{engine_group} %{engine_gid} "%{ovirt_user_description}" %{engine_state}
+
+
+%prep
+%setup -q -n %{name}-@PACKAGE_VERSION@
+
+%build
+%configure \
+       --docdir="%{_docdir}/%{name}-%{version}" \
+       --enable-plugin-plugin-nfacct \
+       --with-zlib \
+       --with-math \
+       --with-user=netdata \
+       %{?conf}
+make %{?_smp_mflags}
+
+%install
+rm -rf "%{buildroot}"
+make %{?_smp_mflags} install DESTDIR="%{buildroot}"
+find "%{buildroot}" -name .keep -exec rm {} \;
+
+%files
+%attr(4755, root, root) %{_libexecdir}/%{name}/plugins.d/apps.plugin
+%attr(-, netdata, netdata) %dir %{_localstatedir}/cache/%{name}/
+%attr(-, netdata, netdata) %dir %{_localstatedir}/log/%{name}/
+%{_bindir}/%{name}
+%{_datadir}/%{name}/
+%{_libexecdir}/%{name}/
+%{_sysconfdir}/%{name}/
+
+%changelog
+* Sun Nov 15 2015 Alon Bar-Lev <alonbl@redhat.com> - 1.0.0-1
+- Initial add.
diff --git a/plugins.d/Makefile.am b/plugins.d/Makefile.am
new file mode 100644 (file)
index 0000000..903fae2
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+dist_plugins_DATA = \
+       README \
+       loopsleepms.sh.inc \
+       $(NULL)
+
+dist_plugins_SCRIPTS = \
+       charts.d.dryrun-helper.sh \
+       charts.d.plugin \
+       tc-qos-helper.sh \
+       $(NULL)
old mode 100755 (executable)
new mode 100644 (file)
index 2f9e097..fabd49a
@@ -1,5 +1,3 @@
-#!/bin/sh
-
 # this function is used to sleep a fraction of a second
 # it calculates the difference between every time is called
 # and tries to align the sleep time to give you exactly the
diff --git a/src/.keep b/src/.keep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100755 (executable)
index 65cb401..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-ifndef BIN_DIR
-BIN_DIR = "."
-endif
-
-ifndef CONFIG_DIR
-CONFIG_DIR = "conf.d"
-endif
-
-ifndef LOG_DIR
-LOG_DIR = "log"
-endif
-
-ifndef PLUGINS_DIR
-PLUGINS_DIR = "plugins.d"
-endif
-
-ifndef nomath
-STORAGE_WITH_MATH = 1
-endif
-
-ifdef nofork
-NETDATA_NO_DAEMON = 1
-endif
-
-ifdef nozlib
-NETDATA_WITHOUT_ZLIB = 1
-endif
-
-COMMON_FLAGS = BIN_DIR='$(BIN_DIR)' CONFIG_DIR='$(CONFIG_DIR)' LOG_DIR='$(LOG_DIR)' PLUGINS_DIR='$(PLUGINS_DIR)'
-
-proc_sources = proc_net_dev.c proc_net_ip_vs_stats.c proc_diskstats.c proc_meminfo.c proc_net_netstat.c proc_net_snmp.c proc_net_stat_conntrack.c proc_stat.c proc_vmstat.c proc_net_rpc_nfsd.c proc_sys_kernel_random_entropy_avail.c proc_interrupts.c
-sources = rrd.c rrd2json.c avl.c dictionary.c procfile.c common.c log.c popen.c url.c config.c web_buffer.c storage_number.c web_client.c global_statistics.c web_server.c plugins_d.c daemon.c plugin_tc.c plugin_checks.c plugin_idlejitter.c plugin_proc.c unit_test.c main.c
-libs    = -pthread
-
-ifdef debug
-COMMON_FLAGS += debug=1
-CFLAGS = -Wall -Wextra -O -ggdb -DBIN_DIR='$(BIN_DIR)' -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)' -fno-omit-frame-pointer
-# CFLAGS := -Wall -Wextra -O -g -DBIN_DIR='$(BIN_DIR)' -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)' -fsanitize=thread -fno-omit-frame-pointer -fPIE
-# libs += -pie -ltsan
-libs += -rdynamic
-else
-CFLAGS := -Wall -Wextra -O3 -DBIN_DIR='$(BIN_DIR)' -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)' -fomit-frame-pointer
-endif
-
-CC = gcc
-
-ifdef STORAGE_WITH_MATH
-CFLAGS += -DSTORAGE_WITH_MATH=1
-libs += -lm
-endif
-
-ifdef NETDATA_NO_DAEMON
-CFLAGS += -DNETDATA_NO_DAEMON=1
-endif
-
-ifdef NETDATA_WITHOUT_ZLIB
-CFLAGS += -DNETDATA_WITHOUT_ZLIB=1
-else
-libs += -lz
-endif
-
-# nfacct requires root access, so we prefer it as a plugin.d external plugin
-ifdef INTERNAL_PLUGIN_NFACCT
-CFLAGS += -DINTERNAL_PLUGIN_NFACCT=1
-sources += plugin_nfacct.c
-libs += -lmnl -lnetfilter_acct
-endif
-
-headers = $(patsubst %.c,%.h,$(sources))
-objects = $(patsubst %.c,%.o,$(sources) $(proc_sources))
-
-all: show_options netdata plugins
-
-%.o: %.c ${headers}
-       $(CC) $(CFLAGS) -c $< -o $@
-
-netdata: $(objects)
-       $(CC) -o netdata $(objects) $(libs)
-
-show_options:
-       @echo "    COMPILING WITH OPTIONS: $(CFLAGS)"
-
-plugins:
-       $(MAKE) -C plugins.d $(COMMON_FLAGS) all
-
-clean:
-       $(MAKE) -C plugins.d clean
-       rm -f *.o netdata core
-
-install: all
-       $(MAKE) -C plugins.d $(COMMON_FLAGS) install
-       @echo; \
-       echo "    INSTALLING netdata to $(BIN_DIR)"; \
-       if [ -f $(BIN_DIR)/netdata ]; \
-       then \
-               mv -f $(BIN_DIR)/netdata $(BIN_DIR)/netdata.old; \
-       fi; \
-       cp -f netdata $(BIN_DIR)/; \
-       echo
-
-.PHONY: all show_options plugins clean install
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..dde9014
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+AM_CPPFLAGS = \
+       -DCACHE_DIR="\"$(cachedir)\"" \
+       -DCONFIG_DIR="\"$(configdir)\"" \
+       -DLOG_DIR="\"$(logdir)\"" \
+       -DPLUGINS_DIR="\"$(pluginsdir)\"" \
+       -DWEB_DIR="\"$(webdir)\"" \
+       $(NULL)
+AM_CFLAGS = \
+       $(OPTIONAL_MATH_CFLAGS) \
+       $(OPTIONAL_NFACCT_CLFAGS) \
+       $(OPTIONAL_ZLIB_CFLAGS) \
+       $(NULL)
+
+bin_PROGRAMS = netdata
+dist_cache_DATA = .keep
+dist_log_DATA = .keep
+plugins_PROGRAMS = apps.plugin
+
+netdata_SOURCES = \
+       appconfig.c appconfig.h \
+       avl.c avl.h \
+       common.c common.h \
+       daemon.c daemon.h \
+       dictionary.c dictionary.h \
+       global_statistics.c global_statistics.h \
+       log.c log.h \
+       main.c main.h \
+       plugin_checks.c plugin_checks.h \
+       plugin_idlejitter.c plugin_idlejitter.h \
+       plugin_nfacct.c plugin_nfacct.h \
+       plugin_proc.c plugin_proc.h \
+       plugin_tc.c plugin_tc.h \
+       plugins_d.c plugins_d.h \
+       popen.c popen.h \
+       proc_diskstats.c \
+       proc_interrupts.c \
+       proc_meminfo.c \
+       proc_net_dev.c \
+       proc_net_ip_vs_stats.c \
+       proc_net_netstat.c \
+       proc_net_rpc_nfsd.c \
+       proc_net_snmp.c \
+       proc_net_stat_conntrack.c \
+       proc_stat.c \
+       proc_sys_kernel_random_entropy_avail.c \
+       proc_vmstat.c \
+       procfile.c procfile.h \
+       rrd.c rrd.h \
+       rrd2json.c rrd2json.h \
+       storage_number.c storage_number.h \
+       unit_test.c unit_test.h \
+       url.c url.h \
+       web_buffer.c web_buffer.h \
+       web_client.c web_client.h \
+       web_server.c web_server.h \
+       $(NULL)
+netdata_LDADD = \
+       $(OPTIONAL_MATH_LIBS) \
+       $(OPTIONAL_NFACCT_LIBS) \
+       $(OPTIONAL_ZLIB_LIBS) \
+       $(NULL)
+
+apps_plugin_SOURCES = \
+       apps_plugin.c \
+       avl.c avl.h \
+       common.c common.h \
+       log.c log.h \
+       procfile.c procfile.h \
+       $(NULL)
+
+install-data-hook:
+       @echo
+       @echo "ATTENTION"
+       @echo
+       @echo "setuid bit of $(pluginsdir)/apps.plugin must be set, please execute as root:"
+       @echo "chown root '$(pluginsdir)/apps.plugin' && chmod 4755 '$(pluginsdir)/apps.plugin'"
+       @echo
diff --git a/src/appconfig.c b/src/appconfig.c
new file mode 100755 (executable)
index 0000000..0a60bc5
--- /dev/null
@@ -0,0 +1,420 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "avl.h"
+#include "common.h"
+#include "appconfig.h"
+#include "log.h"
+
+#define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
+
+pthread_rwlock_t config_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+// ----------------------------------------------------------------------------
+// definitions
+
+#define CONFIG_VALUE_LOADED  0x01 // has been loaded from the config
+#define CONFIG_VALUE_USED    0x02 // has been accessed from the program
+#define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value
+#define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
+
+struct config_value {
+       avl avl;                                // the index - this has to be first!
+
+       uint32_t hash;                  // a simple hash to speed up searching
+                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
+
+       char *name;
+       char *value;
+
+       uint8_t flags;
+
+       struct config_value *next;
+};
+
+struct config {
+       avl avl;
+
+       uint32_t hash;                  // a simple hash to speed up searching
+                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
+
+       char *name;
+
+       struct config_value *values;
+       avl_tree values_index;
+
+       struct config *next;
+} *config_root = NULL;
+
+
+// ----------------------------------------------------------------------------
+// config value
+
+static int config_value_iterator(avl *a) { if(a) {}; return 0; }
+
+static int config_value_compare(void* a, void* b) {
+       if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
+       else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
+       else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
+}
+
+#define config_value_index_add(co, cv) avl_insert(&((co)->values_index), (avl *)(cv))
+#define config_value_index_del(co, cv) avl_remove(&((co)->values_index), (avl *)(cv))
+
+static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
+       struct config_value *result = NULL, tmp;
+       tmp.hash = (hash)?hash:simple_hash(name);
+       tmp.name = (char *)name;
+
+       avl_search(&(co->values_index), (avl *)&tmp, config_value_iterator, (avl **)&result);
+       return result;
+}
+
+// ----------------------------------------------------------------------------
+// config
+
+static int config_iterator(avl *a) { if(a) {}; return 0; }
+
+static int config_compare(void* a, void* b) {
+       if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
+       else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
+       else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
+}
+
+avl_tree config_root_index = {
+               NULL,
+               config_compare
+};
+
+#define config_index_add(cfg) avl_insert(&config_root_index, (avl *)(cfg))
+#define config_index_del(cfg) avl_remove(&config_root_index, (avl *)(cfg))
+
+static struct config *config_index_find(const char *name, uint32_t hash) {
+       struct config *result = NULL, tmp;
+       tmp.hash = (hash)?hash:simple_hash(name);
+       tmp.name = (char *)name;
+
+       avl_search(&config_root_index, (avl *)&tmp, config_iterator, (avl **)&result);
+       return result;
+}
+
+struct config_value *config_value_create(struct config *co, const char *name, const char *value)
+{
+       debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
+
+       struct config_value *cv = calloc(1, sizeof(struct config_value));
+       if(!cv) fatal("Cannot allocate config_value");
+
+       cv->name = strdup(name);
+       if(!cv->name) fatal("Cannot allocate config.name");
+       cv->hash = simple_hash(cv->name);
+
+       cv->value = strdup(value);
+       if(!cv->value) fatal("Cannot allocate config.value");
+
+       config_value_index_add(co, cv);
+
+       // no need for string termination, due to calloc()
+
+       struct config_value *cv2 = co->values;
+       if(cv2) {
+               while (cv2->next) cv2 = cv2->next;
+               cv2->next = cv;
+       }
+       else co->values = cv;
+
+       return cv;
+}
+
+struct config *config_create(const char *section)
+{
+       debug(D_CONFIG, "Creating section '%s'.", section);
+
+       struct config *co = calloc(1, sizeof(struct config));
+       if(!co) fatal("Cannot allocate config");
+
+       co->name = strdup(section);
+       if(!co->name) fatal("Cannot allocate config.name");
+       co->hash = simple_hash(co->name);
+
+       co->values_index.compar = config_value_compare;
+
+       config_index_add(co);
+
+       // no need for string termination, due to calloc()
+
+       struct config *co2 = config_root;
+       if(co2) {
+               while (co2->next) co2 = co2->next;
+               co2->next = co;
+       }
+       else config_root = co;
+
+       return co;
+}
+
+struct config *config_find_section(const char *section)
+{
+       return config_index_find(section, 0);
+}
+
+int load_config(char *filename, int overwrite_used)
+{
+       int line = 0;
+       struct config *co = NULL;
+
+       pthread_rwlock_wrlock(&config_rwlock);
+
+       char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
+
+       if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
+       FILE *fp = fopen(filename, "r");
+       if(!fp) {
+               error("Cannot open file '%s'", CONFIG_DIR "/" CONFIG_FILENAME);
+               pthread_rwlock_unlock(&config_rwlock);
+               return 0;
+       }
+
+       while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
+               buffer[CONFIG_FILE_LINE_MAX] = '\0';
+               line++;
+
+               s = trim(buffer);
+               if(!s) {
+                       debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
+                       continue;
+               }
+
+               int len = strlen(s);
+               if(*s == '[' && s[len - 1] == ']') {
+                       // new section
+                       s[len - 1] = '\0';
+                       s++;
+
+                       co = config_find_section(s);
+                       if(!co) co = config_create(s);
+
+                       continue;
+               }
+
+               if(!co) {
+                       // line outside a section
+                       error("Ignoring line %d ('%s'), it is outsize all sections.", line, s);
+                       continue;
+               }
+
+               char *name = s;
+               char *value = strchr(s, '=');
+               if(!value) {
+                       error("Ignoring line %d ('%s'), there is no = in it.", line, s);
+                       continue;
+               }
+               *value = '\0';
+               value++;
+
+               name = trim(name);
+               value = trim(value);
+
+               if(!name) {
+                       error("Ignoring line %d, name is empty.", line);
+                       continue;
+               }
+               if(!value) {
+                       debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
+                       continue;
+               }
+
+               struct config_value *cv = config_value_index_find(co, name, 0);
+
+               if(!cv) cv = config_value_create(co, name, value);
+               else {
+                       if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
+                               debug(D_CONFIG, "Overwriting '%s/%s'.", line, co->name, cv->name);
+                               free(cv->value);
+                               cv->value = strdup(value);
+                               if(!cv->value) fatal("Cannot allocate config.value");
+                       }
+                       else
+                               debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
+               }
+               cv->flags |= CONFIG_VALUE_LOADED;
+       }
+
+       fclose(fp);
+
+       pthread_rwlock_unlock(&config_rwlock);
+       return 1;
+}
+
+char *config_get(const char *section, const char *name, const char *default_value)
+{
+       struct config_value *cv;
+
+       debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
+
+       pthread_rwlock_rdlock(&config_rwlock);
+
+       struct config *co = config_find_section(section);
+       if(!co) co = config_create(section);
+
+       cv = config_value_index_find(co, name, 0);
+       if(!cv) {
+               cv = config_value_create(co, name, default_value);
+               if(!cv) return NULL;
+       }
+       cv->flags |= CONFIG_VALUE_USED;
+
+       if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
+               // this is a loaded value from the config file
+               // if it is different that the default, mark it
+               if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
+                       if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
+                       cv->flags |= CONFIG_VALUE_CHECKED;
+               }
+       }
+
+       pthread_rwlock_unlock(&config_rwlock);
+       return(cv->value);
+}
+
+long long config_get_number(const char *section, const char *name, long long value)
+{
+       char buffer[100], *s;
+       sprintf(buffer, "%lld", value);
+
+       s = config_get(section, name, buffer);
+       if(!s) return 0;
+
+       return strtoll(s, NULL, 0);
+}
+
+int config_get_boolean(const char *section, const char *name, int value)
+{
+       char *s;
+       if(value) s = "yes";
+       else s = "no";
+
+       s = config_get(section, name, s);
+       if(!s) return 0;
+
+       if(!strcmp(s, "yes")) return 1;
+       else return 0;
+}
+
+const char *config_set(const char *section, const char *name, const char *value)
+{
+       struct config_value *cv;
+
+       debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
+
+       pthread_rwlock_wrlock(&config_rwlock);
+
+       struct config *co = config_find_section(section);
+       if(!co) co = config_create(section);
+
+       cv = config_value_index_find(co, name, 0);
+       if(!cv) cv = config_value_create(co, name, value);
+       cv->flags |= CONFIG_VALUE_USED;
+
+       if(strcmp(cv->value, value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
+
+       free(cv->value);
+       cv->value = strdup(value);
+       if(!cv->value) fatal("Cannot allocate config.value");
+
+       pthread_rwlock_unlock(&config_rwlock);
+
+       return value;
+}
+
+long long config_set_number(const char *section, const char *name, long long value)
+{
+       char buffer[100];
+       sprintf(buffer, "%lld", value);
+
+       config_set(section, name, buffer);
+
+       return value;
+}
+
+int config_set_boolean(const char *section, const char *name, int value)
+{
+       char *s;
+       if(value) s = "yes";
+       else s = "no";
+
+       config_set(section, name, s);
+
+       return value;
+}
+
+void generate_config(struct web_buffer *wb, int only_changed)
+{
+       int i, pri;
+       struct config *co;
+       struct config_value *cv;
+
+       for(i = 0; i < 3 ;i++) {
+               web_buffer_increase(wb, 500);
+               switch(i) {
+                       case 0:
+                               web_buffer_printf(wb, 
+                                       "# NetData Configuration\n"
+                                       "# You can uncomment and change any of the options below.\n"
+                                       "# The value shown in the commented settings, is the default value.\n"
+                                       "\n# global netdata configuration\n");
+                               break;
+
+                       case 1:
+                               web_buffer_printf(wb, "\n\n# per plugin configuration\n");
+                               break;
+
+                       case 2:
+                               web_buffer_printf(wb, "\n\n# per chart configuration\n");
+                               break;
+               }
+
+               for(co = config_root; co ; co = co->next) {
+                       if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0) pri = 0;
+                       else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
+                       else pri = 2;
+
+                       if(i == pri) {
+                               int used = 0;
+                               int changed = 0;
+                               int count = 0;
+                               for(cv = co->values; cv ; cv = cv->next) {
+                                       used += (cv->flags && CONFIG_VALUE_USED)?1:0;
+                                       changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
+                                       count++;
+                               }
+
+                               if(!count) continue;
+                               if(only_changed && !changed) continue;
+
+                               if(!used) {
+                                       web_buffer_increase(wb, 500);
+                                       web_buffer_printf(wb, "\n# node '%s' is not used.", co->name);
+                               }
+
+                               web_buffer_increase(wb, CONFIG_FILE_LINE_MAX+1);
+                               web_buffer_printf(wb, "\n[%s]\n", co->name);
+
+                               for(cv = co->values; cv ; cv = cv->next) {
+
+                                       if(used && !(cv->flags & CONFIG_VALUE_USED)) {
+                                               web_buffer_increase(wb, CONFIG_FILE_LINE_MAX + 1);
+                                               web_buffer_printf(wb, "\n\t# option '%s' is not used.\n", cv->name);
+                                       }
+                                       web_buffer_increase(wb, CONFIG_FILE_LINE_MAX + 1);
+                                       web_buffer_printf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
+                               }
+                       }
+               }
+       }
+}
+
diff --git a/src/appconfig.h b/src/appconfig.h
new file mode 100755 (executable)
index 0000000..79c2238
--- /dev/null
@@ -0,0 +1,25 @@
+#include "web_buffer.h"
+
+#ifndef NETDATA_CONFIG_H
+#define NETDATA_CONFIG_H 1
+
+#define CONFIG_FILENAME "netdata.conf"
+
+// these are used to limit the configuration names and values lengths
+// they are not enforced by config.c functions (they will strdup() all strings, no matter of their length)
+#define CONFIG_MAX_NAME 1024
+#define CONFIG_MAX_VALUE 2048
+
+extern int load_config(char *filename, int overwrite_used);
+
+extern char *config_get(const char *section, const char *name, const char *default_value);
+extern long long config_get_number(const char *section, const char *name, long long value);
+extern int config_get_boolean(const char *section, const char *name, int value);
+
+extern const char *config_set(const char *section, const char *name, const char *value);
+extern long long config_set_number(const char *section, const char *name, long long value);
+extern int config_set_boolean(const char *section, const char *name, int value);
+
+extern void generate_config(struct web_buffer *wb, int only_changed);
+
+#endif /* NETDATA_CONFIG_H */
diff --git a/src/apps_plugin.c b/src/apps_plugin.c
new file mode 100755 (executable)
index 0000000..99a1de0
--- /dev/null
@@ -0,0 +1,1961 @@
+// TODO
+//
+// 1. support -ch option to set global /proc and /sys prefix
+// 2. disable RESET_OR_OVERFLOW check in charts
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <sys/resource.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <locale.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <malloc.h>
+#include <dirent.h>
+#include <arpa/inet.h>
+
+#include "common.h"
+#include "log.h"
+#include "avl.h"
+#include "procfile.h"
+
+#define MAX_COMPARE_NAME 15
+#define MAX_NAME 100
+
+unsigned long long Hertz = 1;
+
+long processors = 1;
+long pid_max = 32768;
+int debug = 0;
+
+int update_every = 1;
+unsigned long long file_counter = 0;
+
+#define PROC_BUFFER 4096
+
+// ----------------------------------------------------------------------------
+// memory debugger
+
+struct allocations {
+       size_t allocations;
+       size_t allocated;
+       size_t allocated_max;
+} allocations = { 0, 0, 0 };
+
+#define MALLOC_MARK (uint32_t)(0x0BADCAFE)
+#define MALLOC_PREFIX (sizeof(uint32_t) * 2)
+#define MALLOC_SUFFIX (sizeof(uint32_t))
+#define MALLOC_OVERHEAD (MALLOC_PREFIX + MALLOC_SUFFIX)
+
+void *mark_allocation(void *allocated_ptr, size_t size_without_overheads) {
+       uint32_t *real_ptr = (uint32_t *)allocated_ptr;
+       real_ptr[0] = MALLOC_MARK;
+       real_ptr[1] = size_without_overheads;
+
+       uint32_t *end_ptr = (uint32_t *)(allocated_ptr + MALLOC_PREFIX + size_without_overheads);
+       end_ptr[0] = MALLOC_MARK;
+
+       // fprintf(stderr, "MEMORY_POINTER: Allocated at %p, returning %p.\n", allocated_ptr, (void *)(allocated_ptr + MALLOC_PREFIX));
+
+       return (void *)(allocated_ptr + MALLOC_PREFIX);
+}
+
+void *check_allocation(const char *file, int line, const char *function, void *marked_ptr, size_t *size_without_overheads_ptr) {
+       uint32_t *real_ptr = (uint32_t *)(marked_ptr - MALLOC_PREFIX);
+
+       // fprintf(stderr, "MEMORY_POINTER: Checking pointer at %p, real %p for %s/%u@%s.\n", marked_ptr, (void *)(marked_ptr - MALLOC_PREFIX), function, line, file);
+
+       if(real_ptr[0] != MALLOC_MARK) fatal("MEMORY: prefix MARK is not valid for %s/%u@%s.", function, line, file);
+
+       size_t size = real_ptr[1];
+
+       uint32_t *end_ptr = (uint32_t *)(marked_ptr + size);
+       if(end_ptr[0] != MALLOC_MARK) fatal("MEMORY: suffix MARK of allocation with size %zu is not valid for %s/%u@%s.", size, function, line, file);
+
+       if(size_without_overheads_ptr) *size_without_overheads_ptr = size;
+
+       return real_ptr;
+}
+
+void *malloc_debug(const char *file, int line, const char *function, size_t size) {
+       void *ptr = malloc(size + MALLOC_OVERHEAD);
+       if(!ptr) fatal("MEMORY: Cannot allocate %zu bytes for %s/%u@%s.", size, function, line, file);
+
+       allocations.allocated += size;
+       allocations.allocations++;
+
+       debug(D_MEMORY, "MEMORY: Allocated %zu bytes for %s/%u@%s."
+               " Status: allocated %zu in %zu allocs."
+               , size
+               , function, line, file
+               , allocations.allocated
+               , allocations.allocations
+       );
+
+       if(allocations.allocated > allocations.allocated_max) {
+               debug(D_MEMORY, "MEMORY: total allocation peak increased from %zu to %zu", allocations.allocated_max, allocations.allocated);
+               allocations.allocated_max = allocations.allocated;
+       }
+
+       size_t csize;
+       check_allocation(file, line, function, mark_allocation(ptr, size), &csize);
+       if(size != csize) {
+               fatal("Invalid size.");
+       }
+
+       return mark_allocation(ptr, size);
+}
+
+void *calloc_debug(const char *file, int line, const char *function, size_t nmemb, size_t size) {
+       void *ptr = malloc_debug(file, line, function, (nmemb * size));
+       bzero(ptr, nmemb * size);
+       return ptr;
+}
+
+void free_debug(const char *file, int line, const char *function, void *ptr) {
+       size_t size;
+       void *real_ptr = check_allocation(file, line, function, ptr, &size);
+
+       bzero(real_ptr, size + MALLOC_OVERHEAD);
+
+       free(real_ptr);
+       allocations.allocated -= size;
+       allocations.allocations--;
+
+       debug(D_MEMORY, "MEMORY: freed %zu bytes for %s/%u@%s."
+               " Status: allocated %zu in %zu allocs."
+               , size
+               , function, line, file
+               , allocations.allocated
+               , allocations.allocations
+       );
+}
+
+void *realloc_debug(const char *file, int line, const char *function, void *ptr, size_t size) {
+       if(!ptr) return malloc_debug(file, line, function, size);
+       if(!size) { free_debug(file, line, function, ptr); return NULL; }
+
+       size_t old_size;
+       void *real_ptr = check_allocation(file, line, function, ptr, &old_size);
+
+       void *new_ptr = realloc(real_ptr, size + MALLOC_OVERHEAD);
+       if(!new_ptr) fatal("MEMORY: Cannot allocate %zu bytes for %s/%u@%s.", size, function, line, file);
+
+       allocations.allocated += size;
+       allocations.allocated -= old_size;
+
+       debug(D_MEMORY, "MEMORY: Re-allocated from %zu to %zu bytes for %s/%u@%s."
+               " Status: allocated %z in %zu allocs."
+               , old_size, size
+               , function, line, file
+               , allocations.allocated
+               , allocations.allocations
+       );
+
+       if(allocations.allocated > allocations.allocated_max) {
+               debug(D_MEMORY, "MEMORY: total allocation peak increased from %zu to %zu", allocations.allocated_max, allocations.allocated);
+               allocations.allocated_max = allocations.allocated;
+       }
+
+       return mark_allocation(new_ptr, size);
+}
+
+char *strdup_debug(const char *file, int line, const char *function, const char *ptr) {
+       size_t size = 0;
+       const char *s = ptr;
+
+       while(*s++) size++;
+       size++;
+
+       char *p = malloc_debug(file, line, function, size);
+       if(!p) fatal("Cannot allocate %zu bytes.", size);
+
+       memcpy(p, ptr, size);
+       return p;
+}
+
+#define malloc(size) malloc_debug(__FILE__, __LINE__, __FUNCTION__, (size))
+#define calloc(nmemb, size) calloc_debug(__FILE__, __LINE__, __FUNCTION__, (nmemb), (size))
+#define realloc(ptr, size) realloc_debug(__FILE__, __LINE__, __FUNCTION__, (ptr), (size))
+#define free(ptr) free_debug(__FILE__, __LINE__, __FUNCTION__, (ptr))
+
+#ifdef strdup
+#undef strdup
+#endif
+#define strdup(ptr) strdup_debug(__FILE__, __LINE__, __FUNCTION__, (ptr))
+
+// ----------------------------------------------------------------------------
+// helper functions
+
+procfile *ff = NULL;
+
+long get_processors(void) {
+       int processors = 0;
+
+       ff = procfile_reopen(ff, "/proc/stat", "", PROCFILE_FLAG_DEFAULT);
+       if(!ff) return 1;
+
+       ff = procfile_readall(ff);
+       if(!ff) {
+               // procfile_close(ff);
+               return 1;
+       }
+
+       unsigned int i;
+       for(i = 0; i < procfile_lines(ff); i++) {
+               if(!procfile_linewords(ff, i)) continue;
+
+               if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
+       }
+       processors--;
+       if(processors < 1) processors = 1;
+
+       // procfile_close(ff);
+       return processors;
+}
+
+long get_pid_max(void) {
+       long mpid = 32768;
+
+       ff = procfile_reopen(ff, "/proc/sys/kernel/pid_max", "", PROCFILE_FLAG_DEFAULT);
+       if(!ff) return mpid;
+
+       ff = procfile_readall(ff);
+       if(!ff) {
+               // procfile_close(ff);
+               return mpid;
+       }
+
+       mpid = atol(procfile_lineword(ff, 0, 0));
+       if(mpid) mpid = 32768;
+
+       // procfile_close(ff);
+       return mpid;
+}
+
+unsigned long long get_hertz(void)
+{
+       unsigned long long hz = 1;
+
+#ifdef _SC_CLK_TCK
+       if((hz = sysconf(_SC_CLK_TCK)) > 0) {
+               return hz;
+       }
+#endif
+
+#ifdef HZ
+       hz = (unsigned long long)HZ;    /* <asm/param.h> */
+#else
+       /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
+       hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
+#endif
+
+       error("Unknown HZ value. Assuming %llu.", hz);
+       return hz;
+}
+
+
+// ----------------------------------------------------------------------------
+// target
+// target is the point to aggregate a process tree values
+
+struct target {
+       char compare[MAX_COMPARE_NAME + 1];
+       char id[MAX_NAME + 1];
+       char name[MAX_NAME + 1];
+
+       unsigned long long minflt;
+       unsigned long long cminflt;
+       unsigned long long majflt;
+       unsigned long long cmajflt;
+       unsigned long long utime;
+       unsigned long long stime;
+       unsigned long long cutime;
+       unsigned long long cstime;
+       unsigned long long num_threads;
+       unsigned long long rss;
+
+       unsigned long long fix_minflt;
+       unsigned long long fix_cminflt;
+       unsigned long long fix_majflt;
+       unsigned long long fix_cmajflt;
+       unsigned long long fix_utime;
+       unsigned long long fix_stime;
+       unsigned long long fix_cutime;
+       unsigned long long fix_cstime;
+
+       unsigned long long statm_size;
+       unsigned long long statm_resident;
+       unsigned long long statm_share;
+       unsigned long long statm_text;
+       unsigned long long statm_lib;
+       unsigned long long statm_data;
+       unsigned long long statm_dirty;
+
+       unsigned long long io_logical_bytes_read;
+       unsigned long long io_logical_bytes_written;
+       unsigned long long io_read_calls;
+       unsigned long long io_write_calls;
+       unsigned long long io_storage_bytes_read;
+       unsigned long long io_storage_bytes_written;
+       unsigned long long io_cancelled_write_bytes;
+
+       unsigned long long fix_io_logical_bytes_read;
+       unsigned long long fix_io_logical_bytes_written;
+       unsigned long long fix_io_read_calls;
+       unsigned long long fix_io_write_calls;
+       unsigned long long fix_io_storage_bytes_read;
+       unsigned long long fix_io_storage_bytes_written;
+       unsigned long long fix_io_cancelled_write_bytes;
+
+       int *fds;
+       unsigned long long openfiles;
+       unsigned long long openpipes;
+       unsigned long long opensockets;
+       unsigned long long openinotifies;
+       unsigned long long openeventfds;
+       unsigned long long opentimerfds;
+       unsigned long long opensignalfds;
+       unsigned long long openeventpolls;
+       unsigned long long openother;
+
+       unsigned long processes;        // how many processes have been merged to this
+       int exposed;                            // if set, we have sent this to netdata
+       int hidden;                                     // if set, we set the hidden flag on the dimension
+       int debug;
+
+       struct target *target;  // the one that will be reported to netdata
+       struct target *next;
+} *target_root = NULL, *default_target = NULL;
+
+long targets = 0;
+
+// find or create a target
+// there are targets that are just agregated to other target (the second argument)
+struct target *get_target(const char *id, struct target *target)
+{
+       const char *nid = id;
+       if(nid[0] == '-') nid++;
+
+       struct target *w;
+       for(w = target_root ; w ; w = w->next)
+               if(strncmp(nid, w->id, MAX_NAME) == 0) return w;
+       
+       w = calloc(sizeof(struct target), 1);
+       if(!w) {
+               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
+               return NULL;
+       }
+
+       strncpy(w->id, nid, MAX_NAME);
+       strncpy(w->name, nid, MAX_NAME);
+       strncpy(w->compare, nid, MAX_COMPARE_NAME);
+       if(id[0] == '-') w->hidden = 1;
+
+       w->target = target;
+
+       w->next = target_root;
+       target_root = w;
+
+       if(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:"");
+
+       return w;
+}
+
+// read the process groups file
+int read_process_groups(const char *name)
+{
+       char buffer[4096+1];
+       char filename[FILENAME_MAX + 1];
+
+       snprintf(filename, FILENAME_MAX, "%s/apps_%s.conf", CONFIG_DIR, name);
+
+       if(debug) fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
+       FILE *fp = fopen(filename, "r");
+       if(!fp) {
+               error("Cannot open file '%s' (%s)", filename, strerror(errno));
+               return 1;
+       }
+
+       long line = 0;
+       while(fgets(buffer, 4096, fp) != NULL) {
+               int whidden = 0, wdebug = 0;
+               line++;
+
+               // if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", buffer);
+
+               char *s = buffer, *t, *p;
+               s = trim(s);
+               if(!s || !*s || *s == '#') continue;
+
+               if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", s);
+
+               // the target name
+               t = strsep(&s, ":");
+               if(t) t = trim(t);
+               if(!t || !*t) continue;
+
+               while(t[0]) {
+                       int stop = 1;
+
+                       switch(t[0]) {
+                               case '-':
+                                       stop = 0;
+                                       whidden = 1;
+                                       t++;
+                                       break;
+
+                               case '+':
+                                       stop = 0;
+                                       wdebug = 1;
+                                       t++;
+                                       break;
+                       }
+
+                       if(stop) break;
+               }
+
+               if(debug) fprintf(stderr, "apps.plugin: \t\ttarget %s\n", t);
+
+               struct target *w = NULL;
+               long count = 0;
+
+               // the process names
+               while((p = strsep(&s, " "))) {
+                       p = trim(p);
+                       if(!p || !*p) continue;
+
+                       struct target *n = get_target(p, w);
+                       n->hidden = whidden;
+                       n->debug = wdebug;
+                       if(!w) w = n;
+
+                       count++;
+               }
+
+               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);
+
+       default_target = get_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
+       strncpy(default_target->name, "other", MAX_NAME);
+
+       return 0;
+}
+
+
+// ----------------------------------------------------------------------------
+// data to store for each pid
+// see: man proc
+
+struct pid_stat {
+       int32_t pid;
+       char comm[MAX_COMPARE_NAME + 1];
+       // char state;
+       int32_t ppid;
+       // int32_t pgrp;
+       // int32_t session;
+       // int32_t tty_nr;
+       // int32_t tpgid;
+       // uint64_t flags;
+       unsigned long long minflt;
+       unsigned long long cminflt;
+       unsigned long long majflt;
+       unsigned long long cmajflt;
+       unsigned long long utime;
+       unsigned long long stime;
+       unsigned long long cutime;
+       unsigned long long cstime;
+       // int64_t priority;
+       // int64_t nice;
+       int32_t num_threads;
+       // int64_t itrealvalue;
+       // unsigned long long starttime;
+       // unsigned long long vsize;
+       unsigned long long rss;
+       // unsigned long long rsslim;
+       // unsigned long long starcode;
+       // unsigned long long endcode;
+       // unsigned long long startstack;
+       // unsigned long long kstkesp;
+       // unsigned long long kstkeip;
+       // uint64_t signal;
+       // uint64_t blocked;
+       // uint64_t sigignore;
+       // uint64_t sigcatch;
+       // uint64_t wchan;
+       // uint64_t nswap;
+       // uint64_t cnswap;
+       // int32_t exit_signal;
+       // int32_t processor;
+       // uint32_t rt_priority;
+       // uint32_t policy;
+       // unsigned long long delayacct_blkio_ticks;
+       // uint64_t guest_time;
+       // int64_t cguest_time;
+
+       unsigned long long statm_size;
+       unsigned long long statm_resident;
+       unsigned long long statm_share;
+       unsigned long long statm_text;
+       unsigned long long statm_lib;
+       unsigned long long statm_data;
+       unsigned long long statm_dirty;
+
+       unsigned long long io_logical_bytes_read;
+       unsigned long long io_logical_bytes_written;
+       unsigned long long io_read_calls;
+       unsigned long long io_write_calls;
+       unsigned long long io_storage_bytes_read;
+       unsigned long long io_storage_bytes_written;
+       unsigned long long io_cancelled_write_bytes;
+
+#ifdef INCLUDE_CHILDS
+       unsigned long long old_utime;
+       unsigned long long old_stime;
+       unsigned long long old_minflt;
+       unsigned long long old_majflt;
+
+       unsigned long long old_cutime;
+       unsigned long long old_cstime;
+       unsigned long long old_cminflt;
+       unsigned long long old_cmajflt;
+
+       unsigned long long fix_cutime;
+       unsigned long long fix_cstime;
+       unsigned long long fix_cminflt;
+       unsigned long long fix_cmajflt;
+
+       unsigned long long diff_cutime;
+       unsigned long long diff_cstime;
+       unsigned long long diff_cminflt;
+       unsigned long long diff_cmajflt;
+#endif
+
+       int *fds;                                       // array of fds it uses
+       int fds_size;                           // the size of the fds array
+
+       int childs;                                     // number of processes directly referencing this
+       int updated;                            // 1 when update
+       int merged;                                     // 1 when it has been merged to its parent
+       int new_entry;
+       struct target *target;
+       struct pid_stat *parent;
+       struct pid_stat *prev;
+       struct pid_stat *next;
+} *root_of_pids = NULL, **all_pids;
+
+long all_pids_count = 0;
+
+struct pid_stat *get_pid_entry(pid_t pid)
+{
+       if(all_pids[pid]) {
+               all_pids[pid]->new_entry = 0;
+               return all_pids[pid];
+       }
+
+       all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
+       if(!all_pids[pid]) {
+               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct pid_stat));
+               return NULL;
+       }
+
+       all_pids[pid]->fds = calloc(sizeof(int), 100);
+       if(!all_pids[pid]->fds)
+               error("Cannot allocate %ld bytes of memory", (unsigned long)(sizeof(int) * 100));
+       else all_pids[pid]->fds_size = 100;
+
+       if(root_of_pids) root_of_pids->prev = all_pids[pid];
+       all_pids[pid]->next = root_of_pids;
+       root_of_pids = all_pids[pid];
+
+       all_pids[pid]->pid = pid;
+       all_pids[pid]->new_entry = 1;
+
+       return all_pids[pid];
+}
+
+void del_pid_entry(pid_t pid)
+{
+       if(!all_pids[pid]) return;
+
+       if(debug) fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
+
+       if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
+       if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
+       if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
+
+       if(all_pids[pid]->fds) free(all_pids[pid]->fds);
+       free(all_pids[pid]);
+       all_pids[pid] = NULL;
+}
+
+
+// ----------------------------------------------------------------------------
+// update pids from proc
+
+int read_proc_pid_stat(struct pid_stat *p) {
+       char filename[FILENAME_MAX + 1];
+
+       snprintf(filename, FILENAME_MAX, "/proc/%d/stat", p->pid);
+
+       ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+       if(!ff) return 1;
+
+       ff = procfile_readall(ff);
+       if(!ff) {
+               // procfile_close(ff);
+               return 1;
+       }
+
+       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);
+       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);
+
+       // p->pid                       = atol(procfile_lineword(ff, 0, 0+i));
+       // comm is at 1
+       // p->state                     = *(procfile_lineword(ff, 0, 2+i));
+       p->ppid                         = atol(procfile_lineword(ff, 0, 3+i));
+       // p->pgrp                      = atol(procfile_lineword(ff, 0, 4+i));
+       // p->session           = atol(procfile_lineword(ff, 0, 5+i));
+       // p->tty_nr            = atol(procfile_lineword(ff, 0, 6+i));
+       // p->tpgid                     = atol(procfile_lineword(ff, 0, 7+i));
+       // p->flags                     = strtoull(procfile_lineword(ff, 0, 8+i), NULL, 10);
+       p->minflt                       = strtoull(procfile_lineword(ff, 0, 9+i), NULL, 10);
+       p->cminflt                      = strtoull(procfile_lineword(ff, 0, 10+i), NULL, 10);
+       p->majflt                       = strtoull(procfile_lineword(ff, 0, 11+i), NULL, 10);
+       p->cmajflt                      = strtoull(procfile_lineword(ff, 0, 12+i), NULL, 10);
+       p->utime                        = strtoull(procfile_lineword(ff, 0, 13+i), NULL, 10);
+       p->stime                        = strtoull(procfile_lineword(ff, 0, 14+i), NULL, 10);
+       p->cutime                       = strtoull(procfile_lineword(ff, 0, 15+i), NULL, 10);
+       p->cstime                       = strtoull(procfile_lineword(ff, 0, 16+i), NULL, 10);
+       // p->priority          = strtoull(procfile_lineword(ff, 0, 17+i), NULL, 10);
+       // p->nice                      = strtoull(procfile_lineword(ff, 0, 18+i), NULL, 10);
+       p->num_threads          = atol(procfile_lineword(ff, 0, 19+i));
+       // p->itrealvalue       = strtoull(procfile_lineword(ff, 0, 20+i), NULL, 10);
+       // p->starttime         = strtoull(procfile_lineword(ff, 0, 21+i), NULL, 10);
+       // p->vsize                     = strtoull(procfile_lineword(ff, 0, 22+i), NULL, 10);
+       p->rss                          = strtoull(procfile_lineword(ff, 0, 23+i), NULL, 10);
+       // p->rsslim            = strtoull(procfile_lineword(ff, 0, 24+i), NULL, 10);
+       // p->starcode          = strtoull(procfile_lineword(ff, 0, 25+i), NULL, 10);
+       // p->endcode           = strtoull(procfile_lineword(ff, 0, 26+i), NULL, 10);
+       // p->startstack        = strtoull(procfile_lineword(ff, 0, 27+i), NULL, 10);
+       // p->kstkesp           = strtoull(procfile_lineword(ff, 0, 28+i), NULL, 10);
+       // p->kstkeip           = strtoull(procfile_lineword(ff, 0, 29+i), NULL, 10);
+       // p->signal            = strtoull(procfile_lineword(ff, 0, 30+i), NULL, 10);
+       // p->blocked           = strtoull(procfile_lineword(ff, 0, 31+i), NULL, 10);
+       // p->sigignore         = strtoull(procfile_lineword(ff, 0, 32+i), NULL, 10);
+       // p->sigcatch          = strtoull(procfile_lineword(ff, 0, 33+i), NULL, 10);
+       // p->wchan                     = strtoull(procfile_lineword(ff, 0, 34+i), NULL, 10);
+       // p->nswap                     = strtoull(procfile_lineword(ff, 0, 35+i), NULL, 10);
+       // p->cnswap            = strtoull(procfile_lineword(ff, 0, 36+i), NULL, 10);
+       // p->exit_signal       = atol(procfile_lineword(ff, 0, 37+i));
+       // p->processor         = atol(procfile_lineword(ff, 0, 38+i));
+       // p->rt_priority       = strtoul(procfile_lineword(ff, 0, 39+i), NULL, 10);
+       // p->policy            = strtoul(procfile_lineword(ff, 0, 40+i), NULL, 10);
+       // p->delayacct_blkio_ticks             = strtoull(procfile_lineword(ff, 0, 41+i), NULL, 10);
+       // 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);
+
+       // procfile_close(ff);
+       return 0;
+}
+
+int read_proc_pid_statm(struct pid_stat *p) {
+       char filename[FILENAME_MAX + 1];
+
+       snprintf(filename, FILENAME_MAX, "/proc/%d/statm", p->pid);
+
+       ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+       if(!ff) return 1;
+
+       ff = procfile_readall(ff);
+       if(!ff) {
+               // procfile_close(ff);
+               return 1;
+       }
+
+       file_counter++;
+
+       p->statm_size                   = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
+       p->statm_resident               = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+       p->statm_share                  = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
+       p->statm_text                   = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
+       p->statm_lib                    = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
+       p->statm_data                   = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
+       p->statm_dirty                  = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
+
+       // procfile_close(ff);
+       return 0;
+}
+
+int read_proc_pid_io(struct pid_stat *p) {
+       char filename[FILENAME_MAX + 1];
+
+       snprintf(filename, FILENAME_MAX, "/proc/%d/io", p->pid);
+
+       ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
+       if(!ff) return 1;
+
+       ff = procfile_readall(ff);
+       if(!ff) {
+               // procfile_close(ff);
+               return 1;
+       }
+
+       file_counter++;
+
+       p->io_logical_bytes_read                = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
+       p->io_logical_bytes_written     = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
+       p->io_read_calls                                = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
+       p->io_write_calls                               = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
+       p->io_storage_bytes_read                = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
+       p->io_storage_bytes_written     = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
+       p->io_cancelled_write_bytes             = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
+
+       // procfile_close(ff);
+       return 0;
+}
+
+
+// ----------------------------------------------------------------------------
+
+#ifdef INCLUDE_CHILDS
+// print a tree view of all processes
+int walk_down(pid_t pid, int level) {
+       struct pid_stat *p = NULL;
+       char b[level+3];
+       int i, ret = 0;
+
+       for(i = 0; i < level; i++) b[i] = '\t';
+       b[level] = '|';
+       b[level+1] = '-';
+       b[level+2] = '\0';
+
+       for(p = root_of_pids; p ; p = p->next) {
+               if(p->ppid == pid) {
+                       ret += walk_down(p->pid, level+1);
+               }
+       }
+
+       p = all_pids[pid];
+       if(p) {
+               if(!p->updated) ret += 1;
+               if(ret) fprintf(stderr, "%s %s %d [%s, %s] c=%d u=%llu+%llu, s=%llu+%llu, cu=%llu+%llu, cs=%llu+%llu, n=%llu+%llu, j=%llu+%llu, cn=%llu+%llu, cj=%llu+%llu\n"
+                       , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->childs
+                       , p->utime, p->utime - p->old_utime
+                       , p->stime, p->stime - p->old_stime
+                       , p->cutime, p->cutime - p->old_cutime
+                       , p->cstime, p->cstime - p->old_cstime
+                       , p->minflt, p->minflt - p->old_minflt
+                       , p->majflt, p->majflt - p->old_majflt
+                       , p->cminflt, p->cminflt - p->old_cminflt
+                       , p->cmajflt, p->cmajflt - p->old_cmajflt
+                       );
+       }
+
+       return ret;
+}
+#endif
+
+
+// ----------------------------------------------------------------------------
+// file descriptor
+// this is used to keep a global list of all open files of the system
+// it is needed in order to figure out the unique files a process tree has open
+
+#define FILE_DESCRIPTORS_INCREASE_STEP 100
+
+struct file_descriptor {
+       avl avl;
+       uint32_t magic;
+       uint32_t hash;
+       const char *name;
+       int type;
+       long count;
+       long pos;
+} *all_files = NULL;
+
+int all_files_len = 0;
+int all_files_size = 0;
+
+int file_descriptor_compare(void* a, void* b) {
+       if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
+               error("Corrupted index data detected. Please report this.");
+
+       if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
+               return -1;
+       else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
+               return 1;
+       else return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
+}
+int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
+
+avl_tree all_files_index = {
+               NULL,
+               file_descriptor_compare
+};
+
+static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
+       struct file_descriptor *result = NULL, tmp;
+       tmp.hash = (hash)?hash:simple_hash(name);
+       tmp.name = name;
+       tmp.count = 0;
+       tmp.pos = 0;
+       tmp.magic = 0x0BADCAFE;
+
+       avl_search(&all_files_index, (avl *)&tmp, file_descriptor_iterator, (avl **)&result);
+       return result;
+}
+
+#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd))
+#define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl *)(fd))
+
+#define FILETYPE_OTHER 0
+#define FILETYPE_FILE 1
+#define FILETYPE_PIPE 2
+#define FILETYPE_SOCKET 3
+#define FILETYPE_INOTIFY 4
+#define FILETYPE_EVENTFD 5
+#define FILETYPE_EVENTPOLL 6
+#define FILETYPE_TIMERFD 7
+#define FILETYPE_SIGNALFD 8
+
+void file_descriptor_not_used(int id)
+{
+       if(id > 0 && id < all_files_size) {
+               if(all_files[id].magic != 0x0BADCAFE) {
+                       error("Ignoring request to remove empty file id %d.", id);
+                       return;
+               }
+
+               if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %ld).\n", id, all_files[id].count);
+
+               if(all_files[id].count > 0) {
+                       all_files[id].count--;
+
+                       if(!all_files[id].count) {
+                               if(debug) fprintf(stderr, "apps.plugin:   >> slot %d is empty.\n", id);
+                               file_descriptor_remove(&all_files[id]);
+                               all_files[id].magic = 0x00000000;
+                               all_files_len--;
+                       }
+               }
+               else
+                       error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
+       }
+       else    error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
+}
+
+unsigned long file_descriptor_find_or_add(const char *name)
+{
+       static int last_pos = 0;
+       uint32_t hash = simple_hash(name);
+
+       if(debug) fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
+
+       struct file_descriptor *fd = file_descriptor_find(name, hash);
+       if(fd) {
+               // found
+               if(debug) fprintf(stderr, "apps.plugin:   >> found on slot %ld\n", fd->pos);
+               fd->count++;
+               return fd->pos;
+       }
+       // not found
+
+       // check we have enough memory to add it
+       if(!all_files || all_files_len == all_files_size) {
+               void *old = all_files;
+               int i;
+
+               // there is no empty slot
+               if(debug) fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
+               all_files = realloc(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
+
+               // if the address changed, we have to rebuild the index
+               // since all pointers are now invalid
+               if(old && old != (void *)all_files) {
+                       if(debug) fprintf(stderr, "apps.plugin:   >> re-indexing.\n");
+                       all_files_index.root = NULL;
+                       for(i = 0; i < all_files_size; i++) {
+                               if(!all_files[i].count) continue;
+                               file_descriptor_add(&all_files[i]);
+                       }
+                       if(debug) fprintf(stderr, "apps.plugin:   >> re-indexing done.\n");
+               }
+
+               for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
+                       all_files[i].count = 0;
+                       all_files[i].name = NULL;
+                       all_files[i].magic = 0x00000000;
+                       all_files[i].pos = i;
+               }
+
+               if(!all_files_size) all_files_len = 1;
+               all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
+       }
+
+       if(debug) fprintf(stderr, "apps.plugin:   >> searching for empty slot.\n");
+
+       // search for an empty slot
+       int i, c;
+       for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
+               if(c >= all_files_size) c = 0;
+               if(c == 0) continue;
+
+               if(!all_files[c].count) {
+                       if(debug) fprintf(stderr, "apps.plugin:   >> Examining slot %d.\n", c);
+
+                       if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
+                               error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
+
+                       if(debug) fprintf(stderr, "apps.plugin:   >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
+                       if(all_files[c].name) free((void *)all_files[c].name);
+                       all_files[c].name = NULL;
+                       last_pos = c;
+                       break;
+               }
+       }
+       if(i == all_files_size) {
+               fatal("We should find an empty slot, but there isn't any");
+               exit(1);
+       }
+       if(debug) fprintf(stderr, "apps.plugin:   >> updating slot %d.\n", c);
+
+       all_files_len++;
+
+       // else we have an empty slot in 'c'
+
+       int type = FILETYPE_OTHER;
+       if(name[0] == '/') type = FILETYPE_FILE;
+       else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
+       else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
+       else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
+       else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
+       else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
+       else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
+       else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
+       else if(strncmp(name, "anon_inode:", 11) == 0) {
+               if(debug) fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
+               type = FILETYPE_OTHER;
+       }
+       else {
+               if(debug) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
+               type = FILETYPE_OTHER;
+       }
+
+       all_files[c].name = strdup(name);
+       all_files[c].hash = hash;
+       all_files[c].type = type;
+       all_files[c].pos  = c;
+       all_files[c].count = 1;
+       all_files[c].magic = 0x0BADCAFE;
+
+       file_descriptor_add(&all_files[c]);
+
+       if(debug) fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
+
+       return c;
+}
+
+
+// 1. read all files in /proc
+// 2. for each numeric directory:
+//    i.   read /proc/pid/stat
+//    ii.  read /proc/pid/statm
+//    iii. read /proc/pid/io (requires root access)
+//    iii. read the entries in directory /proc/pid/fd (requires root access)
+//         for each entry:
+//         a. find or create a struct file_descriptor
+//         b. cleanup any old/unused file_descriptors
+
+// after all these, some pids may be linked to targets, while others may not
+
+// in case of errors, only 1 every 1000 errors is printed
+// to avoid filling up all disk space
+// if debug is enabled, all errors are printed
+
+int update_from_proc(void)
+{
+       static long count_errors = 0;
+
+       char filename[FILENAME_MAX+1];
+       DIR *dir = opendir("/proc");
+       if(!dir) return 0;
+
+       struct dirent *file = NULL;
+       struct pid_stat *p = NULL;
+
+       // mark them all as un-updated
+       all_pids_count = 0;
+       for(p = root_of_pids; p ; p = p->next) {
+               all_pids_count++;
+               p->parent = NULL;
+               p->updated = 0;
+               p->childs = 0;
+               p->merged = 0;
+               p->new_entry = 0;
+       }
+
+       while((file = readdir(dir))) {
+               char *endptr = file->d_name;
+               pid_t pid = strtoul(file->d_name, &endptr, 10);
+               if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
+
+               p = get_pid_entry(pid);
+               if(!p) continue;
+
+               // --------------------------------------------------------------------
+               // /proc/<pid>/stat
+
+               if(read_proc_pid_stat(p)) {
+                       if(!count_errors++ || debug || (p->target && p->target->debug))
+                               error("Cannot process /proc/%d/stat", pid);
+
+                       continue;
+               }
+               if(p->ppid < 0 || p->ppid > pid_max) p->ppid = 0;
+
+
+               // --------------------------------------------------------------------
+               // /proc/<pid>/statm
+
+               if(read_proc_pid_statm(p)) {
+                       if(!count_errors++ || debug || (p->target && p->target->debug))
+                               error("Cannot process /proc/%d/statm", pid);
+
+                       continue;
+               }
+
+
+               // --------------------------------------------------------------------
+               // /proc/<pid>/io
+
+               if(read_proc_pid_io(p)) {
+                       if(!count_errors++ || debug || (p->target && p->target->debug))
+                               error("Cannot process /proc/%d/io", pid);
+
+                       continue;
+               }
+
+               // --------------------------------------------------------------------
+               // link it
+
+               // check if it is target
+               // we do this only once, the first time this pid is loaded
+               if(p->new_entry) {
+                       if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
+
+                       struct target *w;
+                       for(w = target_root; 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(strcmp(w->compare, p->comm) == 0) {
+                                       if(w->target) p->target = w->target;
+                                       else p->target = w;
+
+                                       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
+                               }
+                       }
+               }
+
+               // --------------------------------------------------------------------
+               // /proc/<pid>/fd
+
+               snprintf(filename, FILENAME_MAX, "/proc/%s/fd", file->d_name);
+               DIR *fds = opendir(filename);
+               if(fds) {
+                       int c;
+                       struct dirent *de;
+                       char fdname[FILENAME_MAX + 1];
+                       char linkname[FILENAME_MAX + 1];
+
+                       // make the array negative
+                       for(c = 0 ; c < p->fds_size ; c++) p->fds[c] = -p->fds[c];
+
+                       while((de = readdir(fds))) {
+                               if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
+
+                               // check if the fds array is small
+                               int fdid = atol(de->d_name);
+                               if(fdid < 0) continue;
+                               if(fdid >= p->fds_size) {
+                                       // it is small, extend it
+                                       if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
+                                       p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
+                                       if(!p->fds) {
+                                               error("Cannot re-allocate fds for %s", p->comm);
+                                               break;
+                                       }
+
+                                       // and initialize it
+                                       for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
+                                       p->fds_size = fdid + 100;
+                               }
+
+                               if(p->fds[fdid] == 0) {
+                                       // we don't know this fd, get it
+
+                                       sprintf(fdname, "/proc/%s/fd/%s", file->d_name, de->d_name);
+                                       int l = readlink(fdname, linkname, FILENAME_MAX);
+                                       if(l == -1) {
+                                               if(debug || (p->target && p->target->debug)) {
+                                                       if(!count_errors++ || debug || (p->target && p->target->debug))
+                                                               error("Cannot read link %s", fdname);
+                                               }
+                                               continue;
+                                       }
+                                       linkname[l] = '\0';
+                                       file_counter++;
+
+                                       // if another process already has this, we will get
+                                       // the same id
+                                       p->fds[fdid] = file_descriptor_find_or_add(linkname);
+                               }
+
+                               // else make it positive again, we need it
+                               // of course, the actual file may have changed, but we don't care so much
+                               // FIXME: we could compare the inode as returned by readdir direct structure
+                               else p->fds[fdid] = -p->fds[fdid];
+                       }
+                       closedir(fds);
+
+                       // remove all the negative file descriptors
+                       for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
+                               file_descriptor_not_used(-p->fds[c]);
+                               p->fds[c] = 0;
+                       }
+               }
+
+               // --------------------------------------------------------------------
+               // done!
+
+               // mark it as updated
+               p->updated = 1;
+       }
+       if(count_errors > 1000) {
+               error("%ld more errors encountered\n", count_errors - 1);
+               count_errors = 0;
+       }
+
+       closedir(dir);
+
+       return 1;
+}
+
+
+// ----------------------------------------------------------------------------
+// update statistics on the targets
+
+// 1. link all childs to their parents
+// 2. go from bottom to top, marking as merged all childs to their parents
+//    this step links all parents without a target to the child target, if any
+// 3. link all top level processes (the ones not merged) to the default target
+// 4. go from top to bottom, linking all childs without a target, to their parent target
+//    after this step, all processes have a target
+// [5. for each killed pid (updated = 0), remove its usage from its target]
+// 6. zero all targets
+// 7. concentrate all values on the targets
+// 8. remove all killed processes
+// 9. find the unique file count for each target
+
+void update_statistics(void)
+{
+       int c;
+       struct pid_stat *p = NULL;
+
+       // link all parents and update childs count
+       for(p = root_of_pids; p ; p = p->next) {
+               if(p->ppid > 0 && p->ppid <= pid_max && all_pids[p->ppid]) {
+                       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \tparent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);
+                       
+                       p->parent = all_pids[p->ppid];
+                       p->parent->childs++;
+               }
+               else if(p->ppid != 0) error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
+       }
+
+       // find all the procs with 0 childs and merge them to their parents
+       // repeat, until nothing more can be done.
+       int found = 1;
+       while(found) {
+               found = 0;
+               for(p = root_of_pids; p ; p = p->next) {
+                       // if this process does not have any childs, and
+                       // is not already merged, and
+                       // its parent has childs waiting to be merged, and
+                       // the target of this process and its parent is the same, or the parent does not have a target, or this process does not have a parent
+                       // and its parent is not init
+                       // then... merge them!
+                       if(!p->childs && !p->merged && p->parent && p->parent->childs && (p->target == p->parent->target || !p->parent->target || !p->target) && p->ppid != 1) {
+                               p->parent->childs--;
+                               p->merged = 1;
+
+                               // the parent inherits the child's target, if it does not have a target itself
+                               if(p->target && !p->parent->target) {
+                                       p->parent->target = p->target;
+                                       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\ttarget %s is inherited by %d %s from its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
+                               }
+
+                               found++;
+                       }
+               }
+               if(debug) fprintf(stderr, "apps.plugin: merged %d processes\n", found);
+       }
+
+       // give a default target on all top level processes
+       // init goes always to default target
+       if(all_pids[1]) all_pids[1]->target = default_target;
+
+       for(p = root_of_pids; p ; p = p->next) {
+               // if the process is not merged itself
+               // then is is a top level process
+               if(!p->merged && !p->target) p->target = default_target;
+
+#ifdef INCLUDE_CHILDS
+               // by the way, update the diffs
+               // will be used later for substracting killed process times
+               p->diff_cutime = p->utime - p->cutime;
+               p->diff_cstime = p->stime - p->cstime;
+               p->diff_cminflt = p->minflt - p->cminflt;
+               p->diff_cmajflt = p->majflt - p->cmajflt;
+#endif
+       }
+
+       // give a target to all merged child processes
+       found = 1;
+       while(found) {
+               found = 0;
+               for(p = root_of_pids; p ; p = p->next) {
+                       if(!p->target && p->merged && p->parent && p->parent->target) {
+                               p->target = p->parent->target;
+                               found++;
+                       }
+               }
+       }
+
+#ifdef INCLUDE_CHILDS
+       // for each killed process, remove its values from the parents
+       // sums (we had already added them in a previous loop)
+       for(p = root_of_pids; p ; p = p->next) {
+               if(p->updated) continue;
+
+               if(debug) fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
+
+               unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
+               unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
+               unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
+               unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
+
+               struct pid_stat *t = p;
+               while((t = t->parent)) {
+                       if(!t->updated) continue;
+
+                       unsigned long long x;
+                       if(diff_utime && t->diff_cutime) {
+                               x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
+                               diff_utime -= x;
+                               t->diff_cutime -= x;
+                               t->fix_cutime += x;
+                               if(debug) fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
+                       }
+                       if(diff_stime && t->diff_cstime) {
+                               x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
+                               diff_stime -= x;
+                               t->diff_cstime -= x;
+                               t->fix_cstime += x;
+                               if(debug) fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
+                       }
+                       if(diff_minflt && t->diff_cminflt) {
+                               x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
+                               diff_minflt -= x;
+                               t->diff_cminflt -= x;
+                               t->fix_cminflt += x;
+                               if(debug) fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
+                       }
+                       if(diff_majflt && t->diff_cmajflt) {
+                               x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
+                               diff_majflt -= x;
+                               t->diff_cmajflt -= x;
+                               t->fix_cmajflt += x;
+                               if(debug) fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
+                       }
+               }
+
+               if(diff_utime) error("Cannot fix up utime %llu", diff_utime);
+               if(diff_stime) error("Cannot fix up stime %llu", diff_stime);
+               if(diff_minflt) error("Cannot fix up minflt %llu", diff_minflt);
+               if(diff_majflt) error("Cannot fix up majflt %llu", diff_majflt);
+       }
+#endif
+
+       // zero all the targets
+       targets = 0;
+       struct target *w;
+       for (w = target_root; w ; w = w->next) {
+               targets++;
+
+               w->fds = calloc(sizeof(int), all_files_size);
+               if(!w->fds)
+                       error("Cannot allocate memory for fds in %s", w->name);
+       
+               w->minflt = 0;
+               w->majflt = 0;
+               w->utime = 0;
+               w->stime = 0;
+               w->cminflt = 0;
+               w->cmajflt = 0;
+               w->cutime = 0;
+               w->cstime = 0;
+               w->num_threads = 0;
+               w->rss = 0;
+               w->processes = 0;
+
+               w->statm_size = 0;
+               w->statm_resident = 0;
+               w->statm_share = 0;
+               w->statm_text = 0;
+               w->statm_lib = 0;
+               w->statm_data = 0;
+               w->statm_dirty = 0;
+
+               w->io_logical_bytes_read = 0;
+               w->io_logical_bytes_written = 0;
+               w->io_read_calls = 0;
+               w->io_write_calls = 0;
+               w->io_storage_bytes_read = 0;
+               w->io_storage_bytes_written = 0;
+               w->io_cancelled_write_bytes = 0;
+       }
+
+#ifdef INCLUDE_CHILDS
+       if(debug) walk_down(0, 1);
+#endif
+
+       // concentrate everything on the targets
+       for(p = root_of_pids; p ; p = p->next) {
+               if(!p->target) {
+                       error("pid %d %s was left without a target!", p->pid, p->comm);
+                       continue;
+               }
+
+               if(p->updated) {
+                       p->target->cutime += p->cutime; // - p->fix_cutime;
+                       p->target->cstime += p->cstime; // - p->fix_cstime;
+                       p->target->cminflt += p->cminflt; // - p->fix_cminflt;
+                       p->target->cmajflt += p->cmajflt; // - p->fix_cmajflt;
+
+                       p->target->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
+                       p->target->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
+                       p->target->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
+                       p->target->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
+
+                       //if(p->num_threads < 0)
+                       //      error("Negative threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
+
+                       //if(p->num_threads > 10000)
+                       //      error("Excessive threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
+
+                       p->target->num_threads += p->num_threads;
+                       p->target->rss += p->rss;
+
+                       p->target->statm_size += p->statm_size;
+                       p->target->statm_resident += p->statm_resident;
+                       p->target->statm_share += p->statm_share;
+                       p->target->statm_text += p->statm_text;
+                       p->target->statm_lib += p->statm_lib;
+                       p->target->statm_data += p->statm_data;
+                       p->target->statm_dirty += p->statm_dirty;
+
+                       p->target->io_logical_bytes_read += p->io_logical_bytes_read;
+                       p->target->io_logical_bytes_written += p->io_logical_bytes_written;
+                       p->target->io_read_calls += p->io_read_calls;
+                       p->target->io_write_calls += p->io_write_calls;
+                       p->target->io_storage_bytes_read += p->io_storage_bytes_read;
+                       p->target->io_storage_bytes_written += p->io_storage_bytes_written;
+                       p->target->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
+
+                       p->target->processes++;
+
+                       for(c = 0; c < p->fds_size ;c++) {
+                               if(p->fds[c] == 0) continue;
+                               if(p->fds[c] < all_files_size) {
+                                       if(p->target->fds) p->target->fds[p->fds[c]]++;
+                               }
+                               else
+                                       error("Invalid fd number %d", p->fds[c]);
+                       }
+
+                       if(debug || p->target->debug) fprintf(stderr, "apps.plugin: \tAgregating %s pid %d on %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
+
+/*                     if(p->utime - p->old_utime > 100) fprintf(stderr, "BIG CHANGE: %d %s utime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->utime - p->old_utime, p->old_utime, p->utime);
+                       if(p->cutime - p->old_cutime > 100) fprintf(stderr, "BIG CHANGE: %d %s cutime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cutime - p->old_cutime, p->old_cutime, p->cutime);
+                       if(p->stime - p->old_stime > 100) fprintf(stderr, "BIG CHANGE: %d %s stime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->stime - p->old_stime, p->old_stime, p->stime);
+                       if(p->cstime - p->old_cstime > 100) fprintf(stderr, "BIG CHANGE: %d %s cstime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cstime - p->old_cstime, p->old_cstime, p->cstime);
+                       if(p->minflt - p->old_minflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s minflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->minflt - p->old_minflt, p->old_minflt, p->minflt);
+                       if(p->majflt - p->old_majflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s majflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->majflt - p->old_majflt, p->old_majflt, p->majflt);
+                       if(p->cminflt - p->old_cminflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cminflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cminflt - p->old_cminflt, p->old_cminflt, p->cminflt);
+                       if(p->cmajflt - p->old_cmajflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cmajflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cmajflt - p->old_cmajflt, p->old_cmajflt, p->cmajflt);
+*/
+#ifdef INCLUDE_CHILDS
+                       p->old_utime = p->utime;
+                       p->old_cutime = p->cutime;
+                       p->old_stime = p->stime;
+                       p->old_cstime = p->cstime;
+                       p->old_minflt = p->minflt;
+                       p->old_majflt = p->majflt;
+                       p->old_cminflt = p->cminflt;
+                       p->old_cmajflt = p->cmajflt;
+#endif
+               }
+               else {
+                       // since the process has exited, the user
+                       // will see a drop in our charts, because the incremental
+                       // values of this process will not be there
+
+                       // add them to the fix_* values and they will be added to
+                       // the reported values, so that the report goes steady
+                       p->target->fix_minflt += p->minflt;
+                       p->target->fix_majflt += p->majflt;
+                       p->target->fix_utime += p->utime;
+                       p->target->fix_stime += p->stime;
+                       p->target->fix_cminflt += p->cminflt;
+                       p->target->fix_cmajflt += p->cmajflt;
+                       p->target->fix_cutime += p->cutime;
+                       p->target->fix_cstime += p->cstime;
+
+                       p->target->fix_io_logical_bytes_read += p->io_logical_bytes_read;
+                       p->target->fix_io_logical_bytes_written += p->io_logical_bytes_written;
+                       p->target->fix_io_read_calls += p->io_read_calls;
+                       p->target->fix_io_write_calls += p->io_write_calls;
+                       p->target->fix_io_storage_bytes_read += p->io_storage_bytes_read;
+                       p->target->fix_io_storage_bytes_written += p->io_storage_bytes_written;
+                       p->target->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
+               }
+       }
+
+//     fprintf(stderr, "\n");
+       // cleanup all un-updated processed (exited, killed, etc)
+       for(p = root_of_pids; p ;) {
+               if(!p->updated) {
+//                     fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name,  p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
+                       
+                       for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
+                               file_descriptor_not_used(p->fds[c]);
+                               p->fds[c] = 0;
+                       }
+
+                       pid_t r = p->pid;
+                       p = p->next;
+                       del_pid_entry(r);
+               }
+               else p = p->next;
+       }
+
+       for (w = target_root; w ; w = w->next) {
+               w->openfiles = 0;
+               w->openpipes = 0;
+               w->opensockets = 0;
+               w->openinotifies = 0;
+               w->openeventfds = 0;
+               w->opentimerfds = 0;
+               w->opensignalfds = 0;
+               w->openeventpolls = 0;
+               w->openother = 0;
+
+               for(c = 1; c < all_files_size ;c++) {
+                       if(w->fds && w->fds[c] > 0) switch(all_files[c].type) {
+                               case FILETYPE_FILE:
+                                       w->openfiles++;
+                                       break;
+
+                               case FILETYPE_PIPE:
+                                       w->openpipes++;
+                                       break;
+
+                               case FILETYPE_SOCKET:
+                                       w->opensockets++;
+                                       break;
+
+                               case FILETYPE_INOTIFY:
+                                       w->openinotifies++;
+                                       break;
+
+                               case FILETYPE_EVENTFD:
+                                       w->openeventfds++;
+                                       break;
+
+                               case FILETYPE_TIMERFD:
+                                       w->opentimerfds++;
+                                       break;
+
+                               case FILETYPE_SIGNALFD:
+                                       w->opensignalfds++;
+                                       break;
+
+                               case FILETYPE_EVENTPOLL:
+                                       w->openeventpolls++;
+                                       break;
+
+                               default:
+                                       w->openother++;
+                       }
+               }
+
+               free(w->fds);
+               w->fds = NULL;
+       }
+}
+
+// ----------------------------------------------------------------------------
+// update chart dimensions
+
+void show_dimensions(void)
+{
+       static struct timeval last = { 0, 0 };
+       static struct rusage me_last;
+
+       struct target *w;
+       struct timeval now;
+       struct rusage me;
+
+       unsigned long long usec;
+       unsigned long long cpuuser;
+       unsigned long long cpusyst;
+
+       if(!last.tv_sec) {
+               gettimeofday(&last, NULL);
+               getrusage(RUSAGE_SELF, &me_last);
+
+               // the first time, give a zero to allow
+               // netdata calibrate to the current time
+               // usec = update_every * 1000000ULL;
+               usec = 0ULL;
+               cpuuser = 0;
+               cpusyst = 0;
+       }
+       else {
+               gettimeofday(&now, NULL);
+               getrusage(RUSAGE_SELF, &me);
+
+               usec = usecdiff(&now, &last);
+               cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
+               cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
+
+               bcopy(&now, &last, sizeof(struct timeval));
+               bcopy(&me, &me_last, sizeof(struct rusage));
+       }
+
+       fprintf(stdout, "BEGIN apps.cpu %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + w->fix_utime + w->fix_stime);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.cpu_user %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->fix_utime);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.cpu_system %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + w->fix_stime);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.threads %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.processes %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %lu\n", w->name, w->processes);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.mem %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %lld\n", w->name, (long long)w->statm_resident - (long long)w->statm_share);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.minor_faults %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + w->fix_minflt);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.major_faults %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + w->fix_majflt);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.lreads %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.lwrites %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.preads %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.pwrites %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.files %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->openfiles);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.sockets %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->opensockets);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN apps.pipes %llu\n", usec);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->openpipes);
+       }
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
+       fprintf(stdout, "SET user = %llu\n", cpuuser);
+       fprintf(stdout, "SET system = %llu\n", cpusyst);
+       fprintf(stdout, "END\n");
+
+       fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
+       fprintf(stdout, "SET files = %llu\n", file_counter);
+       fprintf(stdout, "SET pids = %ld\n", all_pids_count);
+       fprintf(stdout, "SET fds = %d\n", all_files_len);
+       fprintf(stdout, "SET targets = %ld\n", targets);
+       fprintf(stdout, "END\n");
+
+       fflush(stdout);
+}
+
+
+// ----------------------------------------------------------------------------
+// generate the charts
+
+void show_charts(void)
+{
+       struct target *w;
+       int newly_added = 0;
+
+       for(w = target_root ; w ; w = w->next)
+               if(!w->exposed && w->processes) {
+                       newly_added++;
+                       w->exposed = 1;
+                       if(debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
+               }
+
+       // nothing more to show
+       if(!newly_added) return;
+
+       // we have something new to show
+       // update the charts
+       fprintf(stdout, "CHART apps.cpu '' 'Apps CPU Time (%ld%% = %ld core%s)' 'cpu time %%' apps apps stacked 20001 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, (unsigned long long)(Hertz * update_every), w->hidden?"hidden":"");
+       }
+
+       fprintf(stdout, "CHART apps.mem '' 'Apps Dedicated Memory (w/o shared)' 'MB' apps apps stacked 20003 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
+       }
+
+       fprintf(stdout, "CHART apps.threads '' 'Apps Threads' 'threads' apps apps stacked 20005 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
+       }
+
+       fprintf(stdout, "CHART apps.processes '' 'Apps Processes' 'processes' apps apps stacked 20004 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
+       }
+
+       fprintf(stdout, "CHART apps.cpu_user '' 'Apps CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20020 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
+       }
+
+       fprintf(stdout, "CHART apps.cpu_system '' 'Apps CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20021 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
+       }
+
+       fprintf(stdout, "CHART apps.major_faults '' 'Apps Major Page Faults (swaps in)' 'page faults/s' apps apps stacked 20010 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
+       }
+
+       fprintf(stdout, "CHART apps.minor_faults '' 'Apps Minor Page Faults' 'page faults/s' apps none stacked 20011 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
+       }
+
+       fprintf(stdout, "CHART apps.lreads '' 'Apps Disk Logical Reads' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
+       }
+
+       fprintf(stdout, "CHART apps.lwrites '' 'Apps I/O Logical Writes' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
+       }
+
+       fprintf(stdout, "CHART apps.preads '' 'Apps Disk Reads' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
+       }
+
+       fprintf(stdout, "CHART apps.pwrites '' 'Apps Disk Writes' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
+       }
+
+       fprintf(stdout, "CHART apps.files '' 'Apps Open Files' 'open files' apps apps stacked 20050 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
+       }
+
+       fprintf(stdout, "CHART apps.sockets '' 'Apps Open Sockets' 'open sockets' apps apps stacked 20051 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
+       }
+
+       fprintf(stdout, "CHART apps.pipes '' 'Apps Pipes' 'open pipes' apps none stacked 20053 %d\n", update_every);
+       for (w = target_root; w ; w = w->next) {
+               if(w->target || (!w->processes && !w->exposed)) continue;
+
+               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
+       }
+
+       fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' netdata netdata stacked 10000 %d\n", update_every);
+       fprintf(stdout, "DIMENSION user '' incremental 1 %d\n", 1000 * update_every);
+       fprintf(stdout, "DIMENSION system '' incremental 1 %d\n", 1000 * update_every);
+
+       fprintf(stdout, "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' netdata netdata line 10001 %d\n", update_every);
+       fprintf(stdout, "DIMENSION files '' incremental 1 %d\n", update_every);
+       fprintf(stdout, "DIMENSION pids '' absolute 1 1\n");
+       fprintf(stdout, "DIMENSION fds '' absolute 1 1\n");
+       fprintf(stdout, "DIMENSION targets '' absolute 1 1\n");
+
+       fflush(stdout);
+}
+
+
+// ----------------------------------------------------------------------------
+// parse command line arguments
+
+void parse_args(int argc, char **argv)
+{
+       int i, freq = 0;
+       char *name = NULL;
+
+       for(i = 1; i < argc; i++) {
+               if(!freq) {
+                       int n = atoi(argv[i]);
+                       if(n > 0) {
+                               freq = n;
+                               continue;
+                       }
+               }
+
+               if(strcmp("debug", argv[i]) == 0) {
+                       debug = 1;
+                       debug_flags = 0xffffffff;
+                       continue;
+               }
+
+               if(!name) {
+                       name = argv[i];
+                       continue;
+               }
+
+               error("Cannot understand option %s", argv[i]);
+               exit(1);
+       }
+
+       if(freq > 0) update_every = freq;
+       if(!name) name = "groups";
+
+       if(read_process_groups(name)) {
+               error("Cannot read process groups %s", name);
+               exit(1);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       // debug_flags = D_PROCFILE;
+
+       // set the name for logging
+       program_name = "apps.plugin";
+
+       info("starting...");
+
+       procfile_adaptive_initial_allocation = 1;
+
+       unsigned long started_t = time(NULL), current_t;
+       Hertz = get_hertz();
+       pid_max = get_pid_max();
+       processors = get_processors();
+
+       parse_args(argc, argv);
+
+       all_pids = calloc(sizeof(struct pid_stat *), pid_max);
+       if(!all_pids) {
+               error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
+               printf("DISABLE\n");
+               exit(1);
+       }
+
+       unsigned long long counter = 1;
+       unsigned long long usec = 0, susec = 0;
+       struct timeval last, now;
+       gettimeofday(&last, NULL);
+
+       for(;1; counter++) {
+               if(!update_from_proc()) {
+                       error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
+                       printf("DISABLE\n");
+                       exit(1);
+               }
+
+               update_statistics();
+               show_charts();          // this is smart enough to show only newly added apps, when needed
+               show_dimensions();
+
+               if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
+               fflush(NULL);
+
+               gettimeofday(&now, NULL);
+               usec = usecdiff(&now, &last) - susec;
+               if(debug) fprintf(stderr, "apps.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);
+
+               // if the last loop took less than half the time
+               // wait the rest of the time
+               if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec;
+               else susec = update_every * 1000000ULL / 2;
+
+               usleep(susec);
+               bcopy(&now, &last, sizeof(struct timeval));
+               
+               current_t = time(NULL);
+               if(current_t - started_t > 3600) exit(0);
+       }
+}
index 7b3d94273d2951871e749a5cd5261db53744292f..352cf4d7777b0fb3f9ebb5a7f5e413d9930e8247 100644 (file)
--- a/src/avl.c
+++ b/src/avl.c
@@ -13,6 +13,9 @@
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include "avl.h"
 
 /* Private methods */
index 4af46b30b0e91f462067fdd49bc3e8d1da737d13..2df676f90216253f4e7584e242ba697d6e354d1f 100755 (executable)
@@ -1,5 +1,6 @@
-// enable O_NOATIME
-#define _GNU_SOURCE
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <sys/syscall.h>
 #include <string.h>
 #include <ctype.h>
index 71ef6f691dc43361e0eb92739b706ed8399dd63a..46af8bc867d163e135ff166a060bdc1f33af3833 100755 (executable)
@@ -1,10 +1,14 @@
+#ifndef NETDATA_COMMON_H
+#define NETDATA_COMMON_H 1
+
+#if defined(HAVE_INTTYPES_H)
 #include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
 #include <sys/types.h>
 #include <unistd.h>
 
-#ifndef NETDATA_COMMON_H
-#define NETDATA_COMMON_H 1
-
 #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)))
 
@@ -20,17 +24,6 @@ extern int fd_is_valid(int fd);
 
 extern char *global_host_prefix;
 
-#ifdef __GNUC__
-// gcc branch optimization
-// #warning "Using GCC branch optimizations"
-#define likely(x)       __builtin_expect(!!(x), 1)
-#define unlikely(x)     __builtin_expect(!!(x), 0)
-#else
-#define likely(x)       (x)
-#define unlikely(x)     (x)
-#endif
-
-
 /* Number of ticks per second */
 #define HZ             hz
 extern unsigned int hz;
diff --git a/src/config.c b/src/config.c
deleted file mode 100755 (executable)
index d76f20c..0000000
+++ /dev/null
@@ -1,418 +0,0 @@
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include "avl.h"
-#include "common.h"
-#include "config.h"
-#include "log.h"
-
-#define CONFIG_FILE_LINE_MAX ((CONFIG_MAX_NAME + CONFIG_MAX_VALUE + 1024) * 2)
-
-pthread_rwlock_t config_rwlock = PTHREAD_RWLOCK_INITIALIZER;
-
-// ----------------------------------------------------------------------------
-// definitions
-
-#define CONFIG_VALUE_LOADED  0x01 // has been loaded from the config
-#define CONFIG_VALUE_USED    0x02 // has been accessed from the program
-#define CONFIG_VALUE_CHANGED 0x04 // has been changed from the loaded value
-#define CONFIG_VALUE_CHECKED 0x08 // has been checked if the value is different from the default
-
-struct config_value {
-       avl avl;                                // the index - this has to be first!
-
-       uint32_t hash;                  // a simple hash to speed up searching
-                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
-
-       char *name;
-       char *value;
-
-       uint8_t flags;
-
-       struct config_value *next;
-};
-
-struct config {
-       avl avl;
-
-       uint32_t hash;                  // a simple hash to speed up searching
-                                                       // we first compare hashes, and only if the hashes are equal we do string comparisons
-
-       char *name;
-
-       struct config_value *values;
-       avl_tree values_index;
-
-       struct config *next;
-} *config_root = NULL;
-
-
-// ----------------------------------------------------------------------------
-// config value
-
-static int config_value_iterator(avl *a) { if(a) {}; return 0; }
-
-static int config_value_compare(void* a, void* b) {
-       if(((struct config_value *)a)->hash < ((struct config_value *)b)->hash) return -1;
-       else if(((struct config_value *)a)->hash > ((struct config_value *)b)->hash) return 1;
-       else return strcmp(((struct config_value *)a)->name, ((struct config_value *)b)->name);
-}
-
-#define config_value_index_add(co, cv) avl_insert(&((co)->values_index), (avl *)(cv))
-#define config_value_index_del(co, cv) avl_remove(&((co)->values_index), (avl *)(cv))
-
-static struct config_value *config_value_index_find(struct config *co, const char *name, uint32_t hash) {
-       struct config_value *result = NULL, tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = (char *)name;
-
-       avl_search(&(co->values_index), (avl *)&tmp, config_value_iterator, (avl **)&result);
-       return result;
-}
-
-// ----------------------------------------------------------------------------
-// config
-
-static int config_iterator(avl *a) { if(a) {}; return 0; }
-
-static int config_compare(void* a, void* b) {
-       if(((struct config *)a)->hash < ((struct config *)b)->hash) return -1;
-       else if(((struct config *)a)->hash > ((struct config *)b)->hash) return 1;
-       else return strcmp(((struct config *)a)->name, ((struct config *)b)->name);
-}
-
-avl_tree config_root_index = {
-               NULL,
-               config_compare
-};
-
-#define config_index_add(cfg) avl_insert(&config_root_index, (avl *)(cfg))
-#define config_index_del(cfg) avl_remove(&config_root_index, (avl *)(cfg))
-
-static struct config *config_index_find(const char *name, uint32_t hash) {
-       struct config *result = NULL, tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = (char *)name;
-
-       avl_search(&config_root_index, (avl *)&tmp, config_iterator, (avl **)&result);
-       return result;
-}
-
-struct config_value *config_value_create(struct config *co, const char *name, const char *value)
-{
-       debug(D_CONFIG, "Creating config entry for name '%s', value '%s', in section '%s'.", name, value, co->name);
-
-       struct config_value *cv = calloc(1, sizeof(struct config_value));
-       if(!cv) fatal("Cannot allocate config_value");
-
-       cv->name = strdup(name);
-       if(!cv->name) fatal("Cannot allocate config.name");
-       cv->hash = simple_hash(cv->name);
-
-       cv->value = strdup(value);
-       if(!cv->value) fatal("Cannot allocate config.value");
-
-       config_value_index_add(co, cv);
-
-       // no need for string termination, due to calloc()
-
-       struct config_value *cv2 = co->values;
-       if(cv2) {
-               while (cv2->next) cv2 = cv2->next;
-               cv2->next = cv;
-       }
-       else co->values = cv;
-
-       return cv;
-}
-
-struct config *config_create(const char *section)
-{
-       debug(D_CONFIG, "Creating section '%s'.", section);
-
-       struct config *co = calloc(1, sizeof(struct config));
-       if(!co) fatal("Cannot allocate config");
-
-       co->name = strdup(section);
-       if(!co->name) fatal("Cannot allocate config.name");
-       co->hash = simple_hash(co->name);
-
-       co->values_index.compar = config_value_compare;
-
-       config_index_add(co);
-
-       // no need for string termination, due to calloc()
-
-       struct config *co2 = config_root;
-       if(co2) {
-               while (co2->next) co2 = co2->next;
-               co2->next = co;
-       }
-       else config_root = co;
-
-       return co;
-}
-
-struct config *config_find_section(const char *section)
-{
-       return config_index_find(section, 0);
-}
-
-int load_config(char *filename, int overwrite_used)
-{
-       int line = 0;
-       struct config *co = NULL;
-
-       pthread_rwlock_wrlock(&config_rwlock);
-
-       char buffer[CONFIG_FILE_LINE_MAX + 1], *s;
-
-       if(!filename) filename = CONFIG_DIR "/" CONFIG_FILENAME;
-       FILE *fp = fopen(filename, "r");
-       if(!fp) {
-               error("Cannot open file '%s'", CONFIG_DIR "/" CONFIG_FILENAME);
-               pthread_rwlock_unlock(&config_rwlock);
-               return 0;
-       }
-
-       while(fgets(buffer, CONFIG_FILE_LINE_MAX, fp) != NULL) {
-               buffer[CONFIG_FILE_LINE_MAX] = '\0';
-               line++;
-
-               s = trim(buffer);
-               if(!s) {
-                       debug(D_CONFIG, "Ignoring line %d, it is empty.", line);
-                       continue;
-               }
-
-               int len = strlen(s);
-               if(*s == '[' && s[len - 1] == ']') {
-                       // new section
-                       s[len - 1] = '\0';
-                       s++;
-
-                       co = config_find_section(s);
-                       if(!co) co = config_create(s);
-
-                       continue;
-               }
-
-               if(!co) {
-                       // line outside a section
-                       error("Ignoring line %d ('%s'), it is outsize all sections.", line, s);
-                       continue;
-               }
-
-               char *name = s;
-               char *value = strchr(s, '=');
-               if(!value) {
-                       error("Ignoring line %d ('%s'), there is no = in it.", line, s);
-                       continue;
-               }
-               *value = '\0';
-               value++;
-
-               name = trim(name);
-               value = trim(value);
-
-               if(!name) {
-                       error("Ignoring line %d, name is empty.", line);
-                       continue;
-               }
-               if(!value) {
-                       debug(D_CONFIG, "Ignoring line %d, value is empty.", line);
-                       continue;
-               }
-
-               struct config_value *cv = config_value_index_find(co, name, 0);
-
-               if(!cv) cv = config_value_create(co, name, value);
-               else {
-                       if(((cv->flags & CONFIG_VALUE_USED) && overwrite_used) || !(cv->flags & CONFIG_VALUE_USED)) {
-                               debug(D_CONFIG, "Overwriting '%s/%s'.", line, co->name, cv->name);
-                               free(cv->value);
-                               cv->value = strdup(value);
-                               if(!cv->value) fatal("Cannot allocate config.value");
-                       }
-                       else
-                               debug(D_CONFIG, "Ignoring line %d, '%s/%s' is already present and used.", line, co->name, cv->name);
-               }
-               cv->flags |= CONFIG_VALUE_LOADED;
-       }
-
-       fclose(fp);
-
-       pthread_rwlock_unlock(&config_rwlock);
-       return 1;
-}
-
-char *config_get(const char *section, const char *name, const char *default_value)
-{
-       struct config_value *cv;
-
-       debug(D_CONFIG, "request to get config in section '%s', name '%s', default_value '%s'", section, name, default_value);
-
-       pthread_rwlock_rdlock(&config_rwlock);
-
-       struct config *co = config_find_section(section);
-       if(!co) co = config_create(section);
-
-       cv = config_value_index_find(co, name, 0);
-       if(!cv) {
-               cv = config_value_create(co, name, default_value);
-               if(!cv) return NULL;
-       }
-       cv->flags |= CONFIG_VALUE_USED;
-
-       if((cv->flags & CONFIG_VALUE_LOADED) || (cv->flags & CONFIG_VALUE_CHANGED)) {
-               // this is a loaded value from the config file
-               // if it is different that the default, mark it
-               if(!(cv->flags & CONFIG_VALUE_CHECKED)) {
-                       if(strcmp(cv->value, default_value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
-                       cv->flags |= CONFIG_VALUE_CHECKED;
-               }
-       }
-
-       pthread_rwlock_unlock(&config_rwlock);
-       return(cv->value);
-}
-
-long long config_get_number(const char *section, const char *name, long long value)
-{
-       char buffer[100], *s;
-       sprintf(buffer, "%lld", value);
-
-       s = config_get(section, name, buffer);
-       if(!s) return 0;
-
-       return strtoll(s, NULL, 0);
-}
-
-int config_get_boolean(const char *section, const char *name, int value)
-{
-       char *s;
-       if(value) s = "yes";
-       else s = "no";
-
-       s = config_get(section, name, s);
-       if(!s) return 0;
-
-       if(!strcmp(s, "yes")) return 1;
-       else return 0;
-}
-
-const char *config_set(const char *section, const char *name, const char *value)
-{
-       struct config_value *cv;
-
-       debug(D_CONFIG, "request to set config in section '%s', name '%s', value '%s'", section, name, value);
-
-       pthread_rwlock_wrlock(&config_rwlock);
-
-       struct config *co = config_find_section(section);
-       if(!co) co = config_create(section);
-
-       cv = config_value_index_find(co, name, 0);
-       if(!cv) cv = config_value_create(co, name, value);
-       cv->flags |= CONFIG_VALUE_USED;
-
-       if(strcmp(cv->value, value) != 0) cv->flags |= CONFIG_VALUE_CHANGED;
-
-       free(cv->value);
-       cv->value = strdup(value);
-       if(!cv->value) fatal("Cannot allocate config.value");
-
-       pthread_rwlock_unlock(&config_rwlock);
-
-       return value;
-}
-
-long long config_set_number(const char *section, const char *name, long long value)
-{
-       char buffer[100];
-       sprintf(buffer, "%lld", value);
-
-       config_set(section, name, buffer);
-
-       return value;
-}
-
-int config_set_boolean(const char *section, const char *name, int value)
-{
-       char *s;
-       if(value) s = "yes";
-       else s = "no";
-
-       config_set(section, name, s);
-
-       return value;
-}
-
-void generate_config(struct web_buffer *wb, int only_changed)
-{
-       int i, pri;
-       struct config *co;
-       struct config_value *cv;
-
-       for(i = 0; i < 3 ;i++) {
-               web_buffer_increase(wb, 500);
-               switch(i) {
-                       case 0:
-                               web_buffer_printf(wb, 
-                                       "# NetData Configuration\n"
-                                       "# You can uncomment and change any of the options below.\n"
-                                       "# The value shown in the commented settings, is the default value.\n"
-                                       "\n# global netdata configuration\n");
-                               break;
-
-                       case 1:
-                               web_buffer_printf(wb, "\n\n# per plugin configuration\n");
-                               break;
-
-                       case 2:
-                               web_buffer_printf(wb, "\n\n# per chart configuration\n");
-                               break;
-               }
-
-               for(co = config_root; co ; co = co->next) {
-                       if(strcmp(co->name, "global") == 0 || strcmp(co->name, "plugins") == 0) pri = 0;
-                       else if(strncmp(co->name, "plugin:", 7) == 0) pri = 1;
-                       else pri = 2;
-
-                       if(i == pri) {
-                               int used = 0;
-                               int changed = 0;
-                               int count = 0;
-                               for(cv = co->values; cv ; cv = cv->next) {
-                                       used += (cv->flags && CONFIG_VALUE_USED)?1:0;
-                                       changed += (cv->flags & CONFIG_VALUE_CHANGED)?1:0;
-                                       count++;
-                               }
-
-                               if(!count) continue;
-                               if(only_changed && !changed) continue;
-
-                               if(!used) {
-                                       web_buffer_increase(wb, 500);
-                                       web_buffer_printf(wb, "\n# node '%s' is not used.", co->name);
-                               }
-
-                               web_buffer_increase(wb, CONFIG_FILE_LINE_MAX+1);
-                               web_buffer_printf(wb, "\n[%s]\n", co->name);
-
-                               for(cv = co->values; cv ; cv = cv->next) {
-
-                                       if(used && !(cv->flags & CONFIG_VALUE_USED)) {
-                                               web_buffer_increase(wb, CONFIG_FILE_LINE_MAX + 1);
-                                               web_buffer_printf(wb, "\n\t# option '%s' is not used.\n", cv->name);
-                                       }
-                                       web_buffer_increase(wb, CONFIG_FILE_LINE_MAX + 1);
-                                       web_buffer_printf(wb, "\t%s%s = %s\n", ((!(cv->flags & CONFIG_VALUE_CHANGED)) && (cv->flags & CONFIG_VALUE_USED))?"# ":"", cv->name, cv->value);
-                               }
-                       }
-               }
-       }
-}
-
diff --git a/src/config.h b/src/config.h
deleted file mode 100755 (executable)
index 79c2238..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "web_buffer.h"
-
-#ifndef NETDATA_CONFIG_H
-#define NETDATA_CONFIG_H 1
-
-#define CONFIG_FILENAME "netdata.conf"
-
-// these are used to limit the configuration names and values lengths
-// they are not enforced by config.c functions (they will strdup() all strings, no matter of their length)
-#define CONFIG_MAX_NAME 1024
-#define CONFIG_MAX_VALUE 2048
-
-extern int load_config(char *filename, int overwrite_used);
-
-extern char *config_get(const char *section, const char *name, const char *default_value);
-extern long long config_get_number(const char *section, const char *name, long long value);
-extern int config_get_boolean(const char *section, const char *name, int value);
-
-extern const char *config_set(const char *section, const char *name, const char *value);
-extern long long config_set_number(const char *section, const char *name, long long value);
-extern int config_set_boolean(const char *section, const char *name, int value);
-
-extern void generate_config(struct web_buffer *wb, int only_changed);
-
-#endif /* NETDATA_CONFIG_H */
index 83e00bb30fe5c21a1037987912c83aa6bf9b22b1..19c7657e912b3c263c82c79c3bfb544b3ff9a1a5 100755 (executable)
@@ -1,3 +1,7 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#ifdef NETDATA_DAEMON
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -12,9 +16,9 @@
 #include <sys/stat.h>
 #include <execinfo.h>
 
-#include "config.h"
-#include "log.h"
 #include "common.h"
+#include "appconfig.h"
+#include "log.h"
 #include "web_client.h"
 #include "plugins_d.h"
 #include "rrd.h"
@@ -314,3 +318,4 @@ int become_daemon(int dont_fork, int close_all_files, const char *input, const c
 
        return(0);
 }
+#endif
index 74d58920befae76ea34220e5c7f24730a0de53af..e0c38a680167b0fb9ba991172865fc198cdacc46 100755 (executable)
@@ -1,7 +1,9 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
-#include <inttypes.h>
 
 #include "avl.h"
 #include "common.h"
index 493e92ea3bc348c1c88dcd024df3e75773e0b6b1..1b8c5d6f53c2583f55f90446d594c2ac2ff84d28 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <pthread.h>
 
 #include "common.h"
index c7b8283bd26186719e40715043fea9d74f5f8114..21c44afc866870e8fff989b6d879a6a011727ded 100755 (executable)
--- a/src/log.c
+++ b/src/log.c
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <time.h>
 #include <syslog.h>
 #include <errno.h>
index 6a341917d936c17c5d359663b7c26fbabd133144..138277df4645654033ef89fca7a0870f5de728be 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -16,7 +19,7 @@
 #include "daemon.h"
 #include "web_server.h"
 #include "popen.h"
-#include "config.h"
+#include "appconfig.h"
 #include "web_client.h"
 #include "rrd.h"
 #include "rrd2json.h"
@@ -340,7 +343,7 @@ int main(int argc, char **argv)
                // --------------------------------------------------------------------
 
                prepare_rundir();
-               char *user = config_get("global", "run as user", (getuid() == 0)?"nobody":"");
+               char *user = config_get("global", "run as user", (getuid() == 0)?NETDATA_USER:"");
                if(*user) {
                        if(become_user(user) != 0) {
                                fprintf(stderr, "Cannot become user %s.\n", user);
@@ -379,7 +382,7 @@ int main(int argc, char **argv)
        // never become a problem
        if(nice(20) == -1) fprintf(stderr, "Cannot lower my CPU priority. Error: %s.\n", strerror(errno));
 
-#ifndef NETDATA_NO_DAEMON
+#ifdef NETDATA_DAEMON
        if(become_daemon(dont_fork, 0, input_log_file, output_log_file, error_log_file, access_log_file, &access_fd, &stdaccess) == -1) {
                fprintf(stderr, "Cannot demonize myself (%s).", strerror(errno));
                exit(1);
index cf02241760276fe9985793397be7d7d9e2fdf007..c8e1f75fabcf65e0d331fa157485c0c43f2b957f 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <pthread.h>
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -5,7 +8,7 @@
 #include <unistd.h>
 
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "log.h"
 #include "rrd.h"
 #include "plugin_checks.h"
index bc336fcdb30e4fa7468344114ef6cc0facac5e49..92b9b393d3242e5dca6d6cfd54c4b6f5d72fa97e 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <pthread.h>
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -6,7 +9,7 @@
 
 #include "global_statistics.h"
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "log.h"
 #include "rrd.h"
 #include "plugin_idlejitter.h"
index f4c191bd5fa3c9113570cb30f9c75439da616d3e..507db93f14c8a2f43c4c89b2bc32e52f002485f6 100644 (file)
@@ -1,3 +1,7 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#ifdef INTERNAL_PLUGIN_NFACCT
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -14,7 +18,7 @@
 
 #include "global_statistics.h"
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "log.h"
 #include "rrd.h"
 #include "plugin_proc.h"
@@ -206,3 +210,4 @@ void *nfacct_main(void *ptr) {
        mnl_socket_close(nl);
        return NULL;
 }
+#endif
index 8691d2d039f5a3d1e24e8ad5d5b4aad37bf4de01..8e7a257bc047c0726a89638c8733e6b8cdb9d5d3 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <pthread.h>
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -6,7 +9,7 @@
 
 #include "global_statistics.h"
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "log.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 3edcc200951d4a45cbc5196165a8948ec831a173..8f932011dad62f398290e1a6d654a9c2b7d98206 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -6,7 +9,7 @@
 #include "avl.h"
 #include "log.h"
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "rrd.h"
 #include "popen.h"
 #include "plugin_tc.h"
diff --git a/src/plugins.d/Makefile b/src/plugins.d/Makefile
deleted file mode 100755 (executable)
index f83a256..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-ifndef BIN_DIR\r
-BIN_DIR = "."\r
-endif\r
-\r
-ifndef CONFIG_DIR\r
-CONFIG_DIR = "conf.d"\r
-endif\r
-\r
-ifndef LOG_DIR\r
-LOG_DIR = "log"\r
-endif\r
-\r
-ifndef PLUGINS_DIR\r
-PLUGINS_DIR = "plugins.d"\r
-endif\r
-\r
-COMMON_FLAGS = BIN_DIR='$(BIN_DIR)' CONFIG_DIR='$(CONFIG_DIR)' LOG_DIR='$(LOG_DIR)' PLUGINS_DIR='$(PLUGINS_DIR)'\r
-\r
-CC := gcc\r
-libs :=\r
-\r
-ifdef debug\r
-COMMON_FLAGS += debug=1\r
-# CFLAGS = -Wall -Wextra -ggdb -DBIN_DIR='$(BIN_DIR)' -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)' -fsanitize=address -fno-omit-frame-pointer\r
-CFLAGS := -Wall -Wextra -O3 -ggdb -DBIN_DIR='$(BIN_DIR)' -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)' -fsanitize=address -fno-omit-frame-pointer\r
-#libs += -ltsan -lpie\r
-else\r
-CFLAGS := -Wall -Wextra -O3 -DBIN_DIR='$(BIN_DIR)' -DCONFIG_DIR='$(CONFIG_DIR)' -DLOG_DIR='$(LOG_DIR)' -DPLUGINS_DIR='$(PLUGINS_DIR)' -fomit-frame-pointer\r
-endif\r
-\r
-all: options plugins\r
-\r
-options:\r
-       @echo "    COMPILING WITH OPTIONS: $(CFLAGS)"\r
-\r
-plugins: apps.plugin\r
-\r
-apps.plugin: apps_plugin.c\r
-       $(CC) $(CFLAGS) -o $@ apps_plugin.c ../avl.o ../common.o ../log.o ../procfile.o $(libs)\r
-\r
-clean:\r
-       -rm -f *.o apps.plugin core\r
-\r
-install: all\r
-       @echo; \\r
-       echo "    INSTALLING apps.plugin to $(PLUGINS_DIR)"; \\r
-       if [ -f $(PLUGINS_DIR)/apps.plugin ]; \\r
-       then \\r
-               mv -f $(PLUGINS_DIR)/apps.plugin $(PLUGINS_DIR)/apps.plugin.old; \\r
-       fi; \\r
-       cp apps.plugin $(PLUGINS_DIR)/; \\r
-       if [ ! "$$USER" = "root" ]; \\r
-       then \\r
-               echo; \\r
-               echo " >>> apps.plugin requires root access to access files in /proc"; \\r
-               echo " >>> Please authorize it!"; \\r
-               echo; \\r
-               sudo chown root $(PLUGINS_DIR)/apps.plugin; \\r
-               sudo chmod 4775 $(PLUGINS_DIR)/apps.plugin; \\r
-       else \\r
-               chown root $(PLUGINS_DIR)/apps.plugin; \\r
-               chmod 4775 $(PLUGINS_DIR)/apps.plugin; \\r
-       fi; \\r
-       echo;\r
-\r
-.PHONY: options all plugins clean install\r
diff --git a/src/plugins.d/apps_plugin.c b/src/plugins.d/apps_plugin.c
deleted file mode 100755 (executable)
index cf16ab4..0000000
+++ /dev/null
@@ -1,1963 +0,0 @@
-// TODO
-//
-// 1. support -ch option to set global /proc and /sys prefix
-// 2. disable RESET_OR_OVERFLOW check in charts
-
-
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-
-#include <sys/resource.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stdarg.h>
-#include <locale.h>
-#include <ctype.h>
-#include <fcntl.h>
-
-#include <malloc.h>
-#include <inttypes.h>
-#include <dirent.h>
-#include <arpa/inet.h>
-
-#include "../common.h"
-#include "../log.h"
-#include "../avl.h"
-#include "../procfile.h"
-
-#define MAX_COMPARE_NAME 15
-#define MAX_NAME 100
-
-unsigned long long Hertz = 1;
-
-long processors = 1;
-long pid_max = 32768;
-int debug = 0;
-
-int update_every = 1;
-unsigned long long file_counter = 0;
-
-#define PROC_BUFFER 4096
-
-// ----------------------------------------------------------------------------
-// memory debugger
-
-struct allocations {
-       size_t allocations;
-       size_t allocated;
-       size_t allocated_max;
-} allocations = { 0, 0, 0 };
-
-#define MALLOC_MARK (uint32_t)(0x0BADCAFE)
-#define MALLOC_PREFIX (sizeof(uint32_t) * 2)
-#define MALLOC_SUFFIX (sizeof(uint32_t))
-#define MALLOC_OVERHEAD (MALLOC_PREFIX + MALLOC_SUFFIX)
-
-void *mark_allocation(void *allocated_ptr, size_t size_without_overheads) {
-       uint32_t *real_ptr = (uint32_t *)allocated_ptr;
-       real_ptr[0] = MALLOC_MARK;
-       real_ptr[1] = size_without_overheads;
-
-       uint32_t *end_ptr = (uint32_t *)(allocated_ptr + MALLOC_PREFIX + size_without_overheads);
-       end_ptr[0] = MALLOC_MARK;
-
-       // fprintf(stderr, "MEMORY_POINTER: Allocated at %p, returning %p.\n", allocated_ptr, (void *)(allocated_ptr + MALLOC_PREFIX));
-
-       return (void *)(allocated_ptr + MALLOC_PREFIX);
-}
-
-void *check_allocation(const char *file, int line, const char *function, void *marked_ptr, size_t *size_without_overheads_ptr) {
-       uint32_t *real_ptr = (uint32_t *)(marked_ptr - MALLOC_PREFIX);
-
-       // fprintf(stderr, "MEMORY_POINTER: Checking pointer at %p, real %p for %s/%u@%s.\n", marked_ptr, (void *)(marked_ptr - MALLOC_PREFIX), function, line, file);
-
-       if(real_ptr[0] != MALLOC_MARK) fatal("MEMORY: prefix MARK is not valid for %s/%u@%s.", function, line, file);
-
-       size_t size = real_ptr[1];
-
-       uint32_t *end_ptr = (uint32_t *)(marked_ptr + size);
-       if(end_ptr[0] != MALLOC_MARK) fatal("MEMORY: suffix MARK of allocation with size %zu is not valid for %s/%u@%s.", size, function, line, file);
-
-       if(size_without_overheads_ptr) *size_without_overheads_ptr = size;
-
-       return real_ptr;
-}
-
-void *malloc_debug(const char *file, int line, const char *function, size_t size) {
-       void *ptr = malloc(size + MALLOC_OVERHEAD);
-       if(!ptr) fatal("MEMORY: Cannot allocate %zu bytes for %s/%u@%s.", size, function, line, file);
-
-       allocations.allocated += size;
-       allocations.allocations++;
-
-       debug(D_MEMORY, "MEMORY: Allocated %zu bytes for %s/%u@%s."
-               " Status: allocated %zu in %zu allocs."
-               , size
-               , function, line, file
-               , allocations.allocated
-               , allocations.allocations
-       );
-
-       if(allocations.allocated > allocations.allocated_max) {
-               debug(D_MEMORY, "MEMORY: total allocation peak increased from %zu to %zu", allocations.allocated_max, allocations.allocated);
-               allocations.allocated_max = allocations.allocated;
-       }
-
-       size_t csize;
-       check_allocation(file, line, function, mark_allocation(ptr, size), &csize);
-       if(size != csize) {
-               fatal("Invalid size.");
-       }
-
-       return mark_allocation(ptr, size);
-}
-
-void *calloc_debug(const char *file, int line, const char *function, size_t nmemb, size_t size) {
-       void *ptr = malloc_debug(file, line, function, (nmemb * size));
-       bzero(ptr, nmemb * size);
-       return ptr;
-}
-
-void free_debug(const char *file, int line, const char *function, void *ptr) {
-       size_t size;
-       void *real_ptr = check_allocation(file, line, function, ptr, &size);
-
-       bzero(real_ptr, size + MALLOC_OVERHEAD);
-
-       free(real_ptr);
-       allocations.allocated -= size;
-       allocations.allocations--;
-
-       debug(D_MEMORY, "MEMORY: freed %zu bytes for %s/%u@%s."
-               " Status: allocated %zu in %zu allocs."
-               , size
-               , function, line, file
-               , allocations.allocated
-               , allocations.allocations
-       );
-}
-
-void *realloc_debug(const char *file, int line, const char *function, void *ptr, size_t size) {
-       if(!ptr) return malloc_debug(file, line, function, size);
-       if(!size) { free_debug(file, line, function, ptr); return NULL; }
-
-       size_t old_size;
-       void *real_ptr = check_allocation(file, line, function, ptr, &old_size);
-
-       void *new_ptr = realloc(real_ptr, size + MALLOC_OVERHEAD);
-       if(!new_ptr) fatal("MEMORY: Cannot allocate %zu bytes for %s/%u@%s.", size, function, line, file);
-
-       allocations.allocated += size;
-       allocations.allocated -= old_size;
-
-       debug(D_MEMORY, "MEMORY: Re-allocated from %zu to %zu bytes for %s/%u@%s."
-               " Status: allocated %z in %zu allocs."
-               , old_size, size
-               , function, line, file
-               , allocations.allocated
-               , allocations.allocations
-       );
-
-       if(allocations.allocated > allocations.allocated_max) {
-               debug(D_MEMORY, "MEMORY: total allocation peak increased from %zu to %zu", allocations.allocated_max, allocations.allocated);
-               allocations.allocated_max = allocations.allocated;
-       }
-
-       return mark_allocation(new_ptr, size);
-}
-
-char *strdup_debug(const char *file, int line, const char *function, const char *ptr) {
-       size_t size = 0;
-       const char *s = ptr;
-
-       while(*s++) size++;
-       size++;
-
-       char *p = malloc_debug(file, line, function, size);
-       if(!p) fatal("Cannot allocate %zu bytes.", size);
-
-       memcpy(p, ptr, size);
-       return p;
-}
-
-#define malloc(size) malloc_debug(__FILE__, __LINE__, __FUNCTION__, (size))
-#define calloc(nmemb, size) calloc_debug(__FILE__, __LINE__, __FUNCTION__, (nmemb), (size))
-#define realloc(ptr, size) realloc_debug(__FILE__, __LINE__, __FUNCTION__, (ptr), (size))
-#define free(ptr) free_debug(__FILE__, __LINE__, __FUNCTION__, (ptr))
-
-#ifdef strdup
-#undef strdup
-#endif
-#define strdup(ptr) strdup_debug(__FILE__, __LINE__, __FUNCTION__, (ptr))
-
-// ----------------------------------------------------------------------------
-// helper functions
-
-procfile *ff = NULL;
-
-long get_processors(void) {
-       int processors = 0;
-
-       ff = procfile_reopen(ff, "/proc/stat", "", PROCFILE_FLAG_DEFAULT);
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) {
-               // procfile_close(ff);
-               return 1;
-       }
-
-       unsigned int i;
-       for(i = 0; i < procfile_lines(ff); i++) {
-               if(!procfile_linewords(ff, i)) continue;
-
-               if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
-       }
-       processors--;
-       if(processors < 1) processors = 1;
-
-       // procfile_close(ff);
-       return processors;
-}
-
-long get_pid_max(void) {
-       long mpid = 32768;
-
-       ff = procfile_reopen(ff, "/proc/sys/kernel/pid_max", "", PROCFILE_FLAG_DEFAULT);
-       if(!ff) return mpid;
-
-       ff = procfile_readall(ff);
-       if(!ff) {
-               // procfile_close(ff);
-               return mpid;
-       }
-
-       mpid = atol(procfile_lineword(ff, 0, 0));
-       if(mpid) mpid = 32768;
-
-       // procfile_close(ff);
-       return mpid;
-}
-
-unsigned long long get_hertz(void)
-{
-       unsigned long long hz = 1;
-
-#ifdef _SC_CLK_TCK
-       if((hz = sysconf(_SC_CLK_TCK)) > 0) {
-               return hz;
-       }
-#endif
-
-#ifdef HZ
-       hz = (unsigned long long)HZ;    /* <asm/param.h> */
-#else
-       /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
-       hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
-#endif
-
-       error("Unknown HZ value. Assuming %llu.", hz);
-       return hz;
-}
-
-
-// ----------------------------------------------------------------------------
-// target
-// target is the point to aggregate a process tree values
-
-struct target {
-       char compare[MAX_COMPARE_NAME + 1];
-       char id[MAX_NAME + 1];
-       char name[MAX_NAME + 1];
-
-       unsigned long long minflt;
-       unsigned long long cminflt;
-       unsigned long long majflt;
-       unsigned long long cmajflt;
-       unsigned long long utime;
-       unsigned long long stime;
-       unsigned long long cutime;
-       unsigned long long cstime;
-       unsigned long long num_threads;
-       unsigned long long rss;
-
-       unsigned long long fix_minflt;
-       unsigned long long fix_cminflt;
-       unsigned long long fix_majflt;
-       unsigned long long fix_cmajflt;
-       unsigned long long fix_utime;
-       unsigned long long fix_stime;
-       unsigned long long fix_cutime;
-       unsigned long long fix_cstime;
-
-       unsigned long long statm_size;
-       unsigned long long statm_resident;
-       unsigned long long statm_share;
-       unsigned long long statm_text;
-       unsigned long long statm_lib;
-       unsigned long long statm_data;
-       unsigned long long statm_dirty;
-
-       unsigned long long io_logical_bytes_read;
-       unsigned long long io_logical_bytes_written;
-       unsigned long long io_read_calls;
-       unsigned long long io_write_calls;
-       unsigned long long io_storage_bytes_read;
-       unsigned long long io_storage_bytes_written;
-       unsigned long long io_cancelled_write_bytes;
-
-       unsigned long long fix_io_logical_bytes_read;
-       unsigned long long fix_io_logical_bytes_written;
-       unsigned long long fix_io_read_calls;
-       unsigned long long fix_io_write_calls;
-       unsigned long long fix_io_storage_bytes_read;
-       unsigned long long fix_io_storage_bytes_written;
-       unsigned long long fix_io_cancelled_write_bytes;
-
-       int *fds;
-       unsigned long long openfiles;
-       unsigned long long openpipes;
-       unsigned long long opensockets;
-       unsigned long long openinotifies;
-       unsigned long long openeventfds;
-       unsigned long long opentimerfds;
-       unsigned long long opensignalfds;
-       unsigned long long openeventpolls;
-       unsigned long long openother;
-
-       unsigned long processes;        // how many processes have been merged to this
-       int exposed;                            // if set, we have sent this to netdata
-       int hidden;                                     // if set, we set the hidden flag on the dimension
-       int debug;
-
-       struct target *target;  // the one that will be reported to netdata
-       struct target *next;
-} *target_root = NULL, *default_target = NULL;
-
-long targets = 0;
-
-// find or create a target
-// there are targets that are just agregated to other target (the second argument)
-struct target *get_target(const char *id, struct target *target)
-{
-       const char *nid = id;
-       if(nid[0] == '-') nid++;
-
-       struct target *w;
-       for(w = target_root ; w ; w = w->next)
-               if(strncmp(nid, w->id, MAX_NAME) == 0) return w;
-       
-       w = calloc(sizeof(struct target), 1);
-       if(!w) {
-               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
-               return NULL;
-       }
-
-       strncpy(w->id, nid, MAX_NAME);
-       strncpy(w->name, nid, MAX_NAME);
-       strncpy(w->compare, nid, MAX_COMPARE_NAME);
-       if(id[0] == '-') w->hidden = 1;
-
-       w->target = target;
-
-       w->next = target_root;
-       target_root = w;
-
-       if(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:"");
-
-       return w;
-}
-
-// read the process groups file
-int read_process_groups(const char *name)
-{
-       char buffer[4096+1];
-       char filename[FILENAME_MAX + 1];
-
-       snprintf(filename, FILENAME_MAX, "%s/apps_%s.conf", CONFIG_DIR, name);
-
-       if(debug) fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
-       FILE *fp = fopen(filename, "r");
-       if(!fp) {
-               error("Cannot open file '%s' (%s)", filename, strerror(errno));
-               return 1;
-       }
-
-       long line = 0;
-       while(fgets(buffer, 4096, fp) != NULL) {
-               int whidden = 0, wdebug = 0;
-               line++;
-
-               // if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", buffer);
-
-               char *s = buffer, *t, *p;
-               s = trim(s);
-               if(!s || !*s || *s == '#') continue;
-
-               if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", s);
-
-               // the target name
-               t = strsep(&s, ":");
-               if(t) t = trim(t);
-               if(!t || !*t) continue;
-
-               while(t[0]) {
-                       int stop = 1;
-
-                       switch(t[0]) {
-                               case '-':
-                                       stop = 0;
-                                       whidden = 1;
-                                       t++;
-                                       break;
-
-                               case '+':
-                                       stop = 0;
-                                       wdebug = 1;
-                                       t++;
-                                       break;
-                       }
-
-                       if(stop) break;
-               }
-
-               if(debug) fprintf(stderr, "apps.plugin: \t\ttarget %s\n", t);
-
-               struct target *w = NULL;
-               long count = 0;
-
-               // the process names
-               while((p = strsep(&s, " "))) {
-                       p = trim(p);
-                       if(!p || !*p) continue;
-
-                       struct target *n = get_target(p, w);
-                       n->hidden = whidden;
-                       n->debug = wdebug;
-                       if(!w) w = n;
-
-                       count++;
-               }
-
-               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);
-
-       default_target = get_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
-       strncpy(default_target->name, "other", MAX_NAME);
-
-       return 0;
-}
-
-
-// ----------------------------------------------------------------------------
-// data to store for each pid
-// see: man proc
-
-struct pid_stat {
-       int32_t pid;
-       char comm[MAX_COMPARE_NAME + 1];
-       // char state;
-       int32_t ppid;
-       // int32_t pgrp;
-       // int32_t session;
-       // int32_t tty_nr;
-       // int32_t tpgid;
-       // uint64_t flags;
-       unsigned long long minflt;
-       unsigned long long cminflt;
-       unsigned long long majflt;
-       unsigned long long cmajflt;
-       unsigned long long utime;
-       unsigned long long stime;
-       unsigned long long cutime;
-       unsigned long long cstime;
-       // int64_t priority;
-       // int64_t nice;
-       int32_t num_threads;
-       // int64_t itrealvalue;
-       // unsigned long long starttime;
-       // unsigned long long vsize;
-       unsigned long long rss;
-       // unsigned long long rsslim;
-       // unsigned long long starcode;
-       // unsigned long long endcode;
-       // unsigned long long startstack;
-       // unsigned long long kstkesp;
-       // unsigned long long kstkeip;
-       // uint64_t signal;
-       // uint64_t blocked;
-       // uint64_t sigignore;
-       // uint64_t sigcatch;
-       // uint64_t wchan;
-       // uint64_t nswap;
-       // uint64_t cnswap;
-       // int32_t exit_signal;
-       // int32_t processor;
-       // uint32_t rt_priority;
-       // uint32_t policy;
-       // unsigned long long delayacct_blkio_ticks;
-       // uint64_t guest_time;
-       // int64_t cguest_time;
-
-       unsigned long long statm_size;
-       unsigned long long statm_resident;
-       unsigned long long statm_share;
-       unsigned long long statm_text;
-       unsigned long long statm_lib;
-       unsigned long long statm_data;
-       unsigned long long statm_dirty;
-
-       unsigned long long io_logical_bytes_read;
-       unsigned long long io_logical_bytes_written;
-       unsigned long long io_read_calls;
-       unsigned long long io_write_calls;
-       unsigned long long io_storage_bytes_read;
-       unsigned long long io_storage_bytes_written;
-       unsigned long long io_cancelled_write_bytes;
-
-#ifdef INCLUDE_CHILDS
-       unsigned long long old_utime;
-       unsigned long long old_stime;
-       unsigned long long old_minflt;
-       unsigned long long old_majflt;
-
-       unsigned long long old_cutime;
-       unsigned long long old_cstime;
-       unsigned long long old_cminflt;
-       unsigned long long old_cmajflt;
-
-       unsigned long long fix_cutime;
-       unsigned long long fix_cstime;
-       unsigned long long fix_cminflt;
-       unsigned long long fix_cmajflt;
-
-       unsigned long long diff_cutime;
-       unsigned long long diff_cstime;
-       unsigned long long diff_cminflt;
-       unsigned long long diff_cmajflt;
-#endif
-
-       int *fds;                                       // array of fds it uses
-       int fds_size;                           // the size of the fds array
-
-       int childs;                                     // number of processes directly referencing this
-       int updated;                            // 1 when update
-       int merged;                                     // 1 when it has been merged to its parent
-       int new_entry;
-       struct target *target;
-       struct pid_stat *parent;
-       struct pid_stat *prev;
-       struct pid_stat *next;
-} *root_of_pids = NULL, **all_pids;
-
-long all_pids_count = 0;
-
-struct pid_stat *get_pid_entry(pid_t pid)
-{
-       if(all_pids[pid]) {
-               all_pids[pid]->new_entry = 0;
-               return all_pids[pid];
-       }
-
-       all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
-       if(!all_pids[pid]) {
-               error("Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct pid_stat));
-               return NULL;
-       }
-
-       all_pids[pid]->fds = calloc(sizeof(int), 100);
-       if(!all_pids[pid]->fds)
-               error("Cannot allocate %ld bytes of memory", (unsigned long)(sizeof(int) * 100));
-       else all_pids[pid]->fds_size = 100;
-
-       if(root_of_pids) root_of_pids->prev = all_pids[pid];
-       all_pids[pid]->next = root_of_pids;
-       root_of_pids = all_pids[pid];
-
-       all_pids[pid]->pid = pid;
-       all_pids[pid]->new_entry = 1;
-
-       return all_pids[pid];
-}
-
-void del_pid_entry(pid_t pid)
-{
-       if(!all_pids[pid]) return;
-
-       if(debug) fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
-
-       if(root_of_pids == all_pids[pid]) root_of_pids = all_pids[pid]->next;
-       if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
-       if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
-
-       if(all_pids[pid]->fds) free(all_pids[pid]->fds);
-       free(all_pids[pid]);
-       all_pids[pid] = NULL;
-}
-
-
-// ----------------------------------------------------------------------------
-// update pids from proc
-
-int read_proc_pid_stat(struct pid_stat *p) {
-       char filename[FILENAME_MAX + 1];
-
-       snprintf(filename, FILENAME_MAX, "/proc/%d/stat", p->pid);
-
-       ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) {
-               // procfile_close(ff);
-               return 1;
-       }
-
-       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);
-       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);
-
-       // p->pid                       = atol(procfile_lineword(ff, 0, 0+i));
-       // comm is at 1
-       // p->state                     = *(procfile_lineword(ff, 0, 2+i));
-       p->ppid                         = atol(procfile_lineword(ff, 0, 3+i));
-       // p->pgrp                      = atol(procfile_lineword(ff, 0, 4+i));
-       // p->session           = atol(procfile_lineword(ff, 0, 5+i));
-       // p->tty_nr            = atol(procfile_lineword(ff, 0, 6+i));
-       // p->tpgid                     = atol(procfile_lineword(ff, 0, 7+i));
-       // p->flags                     = strtoull(procfile_lineword(ff, 0, 8+i), NULL, 10);
-       p->minflt                       = strtoull(procfile_lineword(ff, 0, 9+i), NULL, 10);
-       p->cminflt                      = strtoull(procfile_lineword(ff, 0, 10+i), NULL, 10);
-       p->majflt                       = strtoull(procfile_lineword(ff, 0, 11+i), NULL, 10);
-       p->cmajflt                      = strtoull(procfile_lineword(ff, 0, 12+i), NULL, 10);
-       p->utime                        = strtoull(procfile_lineword(ff, 0, 13+i), NULL, 10);
-       p->stime                        = strtoull(procfile_lineword(ff, 0, 14+i), NULL, 10);
-       p->cutime                       = strtoull(procfile_lineword(ff, 0, 15+i), NULL, 10);
-       p->cstime                       = strtoull(procfile_lineword(ff, 0, 16+i), NULL, 10);
-       // p->priority          = strtoull(procfile_lineword(ff, 0, 17+i), NULL, 10);
-       // p->nice                      = strtoull(procfile_lineword(ff, 0, 18+i), NULL, 10);
-       p->num_threads          = atol(procfile_lineword(ff, 0, 19+i));
-       // p->itrealvalue       = strtoull(procfile_lineword(ff, 0, 20+i), NULL, 10);
-       // p->starttime         = strtoull(procfile_lineword(ff, 0, 21+i), NULL, 10);
-       // p->vsize                     = strtoull(procfile_lineword(ff, 0, 22+i), NULL, 10);
-       p->rss                          = strtoull(procfile_lineword(ff, 0, 23+i), NULL, 10);
-       // p->rsslim            = strtoull(procfile_lineword(ff, 0, 24+i), NULL, 10);
-       // p->starcode          = strtoull(procfile_lineword(ff, 0, 25+i), NULL, 10);
-       // p->endcode           = strtoull(procfile_lineword(ff, 0, 26+i), NULL, 10);
-       // p->startstack        = strtoull(procfile_lineword(ff, 0, 27+i), NULL, 10);
-       // p->kstkesp           = strtoull(procfile_lineword(ff, 0, 28+i), NULL, 10);
-       // p->kstkeip           = strtoull(procfile_lineword(ff, 0, 29+i), NULL, 10);
-       // p->signal            = strtoull(procfile_lineword(ff, 0, 30+i), NULL, 10);
-       // p->blocked           = strtoull(procfile_lineword(ff, 0, 31+i), NULL, 10);
-       // p->sigignore         = strtoull(procfile_lineword(ff, 0, 32+i), NULL, 10);
-       // p->sigcatch          = strtoull(procfile_lineword(ff, 0, 33+i), NULL, 10);
-       // p->wchan                     = strtoull(procfile_lineword(ff, 0, 34+i), NULL, 10);
-       // p->nswap                     = strtoull(procfile_lineword(ff, 0, 35+i), NULL, 10);
-       // p->cnswap            = strtoull(procfile_lineword(ff, 0, 36+i), NULL, 10);
-       // p->exit_signal       = atol(procfile_lineword(ff, 0, 37+i));
-       // p->processor         = atol(procfile_lineword(ff, 0, 38+i));
-       // p->rt_priority       = strtoul(procfile_lineword(ff, 0, 39+i), NULL, 10);
-       // p->policy            = strtoul(procfile_lineword(ff, 0, 40+i), NULL, 10);
-       // p->delayacct_blkio_ticks             = strtoull(procfile_lineword(ff, 0, 41+i), NULL, 10);
-       // 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);
-
-       // procfile_close(ff);
-       return 0;
-}
-
-int read_proc_pid_statm(struct pid_stat *p) {
-       char filename[FILENAME_MAX + 1];
-
-       snprintf(filename, FILENAME_MAX, "/proc/%d/statm", p->pid);
-
-       ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) {
-               // procfile_close(ff);
-               return 1;
-       }
-
-       file_counter++;
-
-       p->statm_size                   = strtoull(procfile_lineword(ff, 0, 0), NULL, 10);
-       p->statm_resident               = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
-       p->statm_share                  = strtoull(procfile_lineword(ff, 0, 2), NULL, 10);
-       p->statm_text                   = strtoull(procfile_lineword(ff, 0, 3), NULL, 10);
-       p->statm_lib                    = strtoull(procfile_lineword(ff, 0, 4), NULL, 10);
-       p->statm_data                   = strtoull(procfile_lineword(ff, 0, 5), NULL, 10);
-       p->statm_dirty                  = strtoull(procfile_lineword(ff, 0, 6), NULL, 10);
-
-       // procfile_close(ff);
-       return 0;
-}
-
-int read_proc_pid_io(struct pid_stat *p) {
-       char filename[FILENAME_MAX + 1];
-
-       snprintf(filename, FILENAME_MAX, "/proc/%d/io", p->pid);
-
-       ff = procfile_reopen(ff, filename, NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO);
-       if(!ff) return 1;
-
-       ff = procfile_readall(ff);
-       if(!ff) {
-               // procfile_close(ff);
-               return 1;
-       }
-
-       file_counter++;
-
-       p->io_logical_bytes_read                = strtoull(procfile_lineword(ff, 0, 1), NULL, 10);
-       p->io_logical_bytes_written     = strtoull(procfile_lineword(ff, 1, 1), NULL, 10);
-       p->io_read_calls                                = strtoull(procfile_lineword(ff, 2, 1), NULL, 10);
-       p->io_write_calls                               = strtoull(procfile_lineword(ff, 3, 1), NULL, 10);
-       p->io_storage_bytes_read                = strtoull(procfile_lineword(ff, 4, 1), NULL, 10);
-       p->io_storage_bytes_written     = strtoull(procfile_lineword(ff, 5, 1), NULL, 10);
-       p->io_cancelled_write_bytes             = strtoull(procfile_lineword(ff, 6, 1), NULL, 10);
-
-       // procfile_close(ff);
-       return 0;
-}
-
-
-// ----------------------------------------------------------------------------
-
-#ifdef INCLUDE_CHILDS
-// print a tree view of all processes
-int walk_down(pid_t pid, int level) {
-       struct pid_stat *p = NULL;
-       char b[level+3];
-       int i, ret = 0;
-
-       for(i = 0; i < level; i++) b[i] = '\t';
-       b[level] = '|';
-       b[level+1] = '-';
-       b[level+2] = '\0';
-
-       for(p = root_of_pids; p ; p = p->next) {
-               if(p->ppid == pid) {
-                       ret += walk_down(p->pid, level+1);
-               }
-       }
-
-       p = all_pids[pid];
-       if(p) {
-               if(!p->updated) ret += 1;
-               if(ret) fprintf(stderr, "%s %s %d [%s, %s] c=%d u=%llu+%llu, s=%llu+%llu, cu=%llu+%llu, cs=%llu+%llu, n=%llu+%llu, j=%llu+%llu, cn=%llu+%llu, cj=%llu+%llu\n"
-                       , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->childs
-                       , p->utime, p->utime - p->old_utime
-                       , p->stime, p->stime - p->old_stime
-                       , p->cutime, p->cutime - p->old_cutime
-                       , p->cstime, p->cstime - p->old_cstime
-                       , p->minflt, p->minflt - p->old_minflt
-                       , p->majflt, p->majflt - p->old_majflt
-                       , p->cminflt, p->cminflt - p->old_cminflt
-                       , p->cmajflt, p->cmajflt - p->old_cmajflt
-                       );
-       }
-
-       return ret;
-}
-#endif
-
-
-// ----------------------------------------------------------------------------
-// file descriptor
-// this is used to keep a global list of all open files of the system
-// it is needed in order to figure out the unique files a process tree has open
-
-#define FILE_DESCRIPTORS_INCREASE_STEP 100
-
-struct file_descriptor {
-       avl avl;
-       uint32_t magic;
-       uint32_t hash;
-       const char *name;
-       int type;
-       long count;
-       long pos;
-} *all_files = NULL;
-
-int all_files_len = 0;
-int all_files_size = 0;
-
-int file_descriptor_compare(void* a, void* b) {
-       if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
-               error("Corrupted index data detected. Please report this.");
-
-       if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
-               return -1;
-       else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
-               return 1;
-       else return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
-}
-int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
-
-avl_tree all_files_index = {
-               NULL,
-               file_descriptor_compare
-};
-
-static struct file_descriptor *file_descriptor_find(const char *name, uint32_t hash) {
-       struct file_descriptor *result = NULL, tmp;
-       tmp.hash = (hash)?hash:simple_hash(name);
-       tmp.name = name;
-       tmp.count = 0;
-       tmp.pos = 0;
-       tmp.magic = 0x0BADCAFE;
-
-       avl_search(&all_files_index, (avl *)&tmp, file_descriptor_iterator, (avl **)&result);
-       return result;
-}
-
-#define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd))
-#define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl *)(fd))
-
-#define FILETYPE_OTHER 0
-#define FILETYPE_FILE 1
-#define FILETYPE_PIPE 2
-#define FILETYPE_SOCKET 3
-#define FILETYPE_INOTIFY 4
-#define FILETYPE_EVENTFD 5
-#define FILETYPE_EVENTPOLL 6
-#define FILETYPE_TIMERFD 7
-#define FILETYPE_SIGNALFD 8
-
-void file_descriptor_not_used(int id)
-{
-       if(id > 0 && id < all_files_size) {
-               if(all_files[id].magic != 0x0BADCAFE) {
-                       error("Ignoring request to remove empty file id %d.", id);
-                       return;
-               }
-
-               if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %ld).\n", id, all_files[id].count);
-
-               if(all_files[id].count > 0) {
-                       all_files[id].count--;
-
-                       if(!all_files[id].count) {
-                               if(debug) fprintf(stderr, "apps.plugin:   >> slot %d is empty.\n", id);
-                               file_descriptor_remove(&all_files[id]);
-                               all_files[id].magic = 0x00000000;
-                               all_files_len--;
-                       }
-               }
-               else
-                       error("Request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
-       }
-       else    error("Request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
-}
-
-unsigned long file_descriptor_find_or_add(const char *name)
-{
-       static int last_pos = 0;
-       uint32_t hash = simple_hash(name);
-
-       if(debug) fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %u\n", name, hash);
-
-       struct file_descriptor *fd = file_descriptor_find(name, hash);
-       if(fd) {
-               // found
-               if(debug) fprintf(stderr, "apps.plugin:   >> found on slot %ld\n", fd->pos);
-               fd->count++;
-               return fd->pos;
-       }
-       // not found
-
-       // check we have enough memory to add it
-       if(!all_files || all_files_len == all_files_size) {
-               void *old = all_files;
-               int i;
-
-               // there is no empty slot
-               if(debug) fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
-               all_files = realloc(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
-
-               // if the address changed, we have to rebuild the index
-               // since all pointers are now invalid
-               if(old && old != (void *)all_files) {
-                       if(debug) fprintf(stderr, "apps.plugin:   >> re-indexing.\n");
-                       all_files_index.root = NULL;
-                       for(i = 0; i < all_files_size; i++) {
-                               if(!all_files[i].count) continue;
-                               file_descriptor_add(&all_files[i]);
-                       }
-                       if(debug) fprintf(stderr, "apps.plugin:   >> re-indexing done.\n");
-               }
-
-               for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
-                       all_files[i].count = 0;
-                       all_files[i].name = NULL;
-                       all_files[i].magic = 0x00000000;
-                       all_files[i].pos = i;
-               }
-
-               if(!all_files_size) all_files_len = 1;
-               all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
-       }
-
-       if(debug) fprintf(stderr, "apps.plugin:   >> searching for empty slot.\n");
-
-       // search for an empty slot
-       int i, c;
-       for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
-               if(c >= all_files_size) c = 0;
-               if(c == 0) continue;
-
-               if(!all_files[c].count) {
-                       if(debug) fprintf(stderr, "apps.plugin:   >> Examining slot %d.\n", c);
-
-                       if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
-                               error("fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
-
-                       if(debug) fprintf(stderr, "apps.plugin:   >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
-                       if(all_files[c].name) free((void *)all_files[c].name);
-                       all_files[c].name = NULL;
-                       last_pos = c;
-                       break;
-               }
-       }
-       if(i == all_files_size) {
-               fatal("We should find an empty slot, but there isn't any");
-               exit(1);
-       }
-       if(debug) fprintf(stderr, "apps.plugin:   >> updating slot %d.\n", c);
-
-       all_files_len++;
-
-       // else we have an empty slot in 'c'
-
-       int type = FILETYPE_OTHER;
-       if(name[0] == '/') type = FILETYPE_FILE;
-       else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
-       else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
-       else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
-       else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
-       else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
-       else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
-       else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
-       else if(strncmp(name, "anon_inode:", 11) == 0) {
-               if(debug) fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
-               type = FILETYPE_OTHER;
-       }
-       else {
-               if(debug) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
-               type = FILETYPE_OTHER;
-       }
-
-       all_files[c].name = strdup(name);
-       all_files[c].hash = hash;
-       all_files[c].type = type;
-       all_files[c].pos  = c;
-       all_files[c].count = 1;
-       all_files[c].magic = 0x0BADCAFE;
-
-       file_descriptor_add(&all_files[c]);
-
-       if(debug) fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
-
-       return c;
-}
-
-
-// 1. read all files in /proc
-// 2. for each numeric directory:
-//    i.   read /proc/pid/stat
-//    ii.  read /proc/pid/statm
-//    iii. read /proc/pid/io (requires root access)
-//    iii. read the entries in directory /proc/pid/fd (requires root access)
-//         for each entry:
-//         a. find or create a struct file_descriptor
-//         b. cleanup any old/unused file_descriptors
-
-// after all these, some pids may be linked to targets, while others may not
-
-// in case of errors, only 1 every 1000 errors is printed
-// to avoid filling up all disk space
-// if debug is enabled, all errors are printed
-
-int update_from_proc(void)
-{
-       static long count_errors = 0;
-
-       char filename[FILENAME_MAX+1];
-       DIR *dir = opendir("/proc");
-       if(!dir) return 0;
-
-       struct dirent *file = NULL;
-       struct pid_stat *p = NULL;
-
-       // mark them all as un-updated
-       all_pids_count = 0;
-       for(p = root_of_pids; p ; p = p->next) {
-               all_pids_count++;
-               p->parent = NULL;
-               p->updated = 0;
-               p->childs = 0;
-               p->merged = 0;
-               p->new_entry = 0;
-       }
-
-       while((file = readdir(dir))) {
-               char *endptr = file->d_name;
-               pid_t pid = strtoul(file->d_name, &endptr, 10);
-               if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
-
-               p = get_pid_entry(pid);
-               if(!p) continue;
-
-               // --------------------------------------------------------------------
-               // /proc/<pid>/stat
-
-               if(read_proc_pid_stat(p)) {
-                       if(!count_errors++ || debug || (p->target && p->target->debug))
-                               error("Cannot process /proc/%d/stat", pid);
-
-                       continue;
-               }
-               if(p->ppid < 0 || p->ppid > pid_max) p->ppid = 0;
-
-
-               // --------------------------------------------------------------------
-               // /proc/<pid>/statm
-
-               if(read_proc_pid_statm(p)) {
-                       if(!count_errors++ || debug || (p->target && p->target->debug))
-                               error("Cannot process /proc/%d/statm", pid);
-
-                       continue;
-               }
-
-
-               // --------------------------------------------------------------------
-               // /proc/<pid>/io
-
-               if(read_proc_pid_io(p)) {
-                       if(!count_errors++ || debug || (p->target && p->target->debug))
-                               error("Cannot process /proc/%d/io", pid);
-
-                       continue;
-               }
-
-               // --------------------------------------------------------------------
-               // link it
-
-               // check if it is target
-               // we do this only once, the first time this pid is loaded
-               if(p->new_entry) {
-                       if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
-
-                       struct target *w;
-                       for(w = target_root; 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(strcmp(w->compare, p->comm) == 0) {
-                                       if(w->target) p->target = w->target;
-                                       else p->target = w;
-
-                                       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
-                               }
-                       }
-               }
-
-               // --------------------------------------------------------------------
-               // /proc/<pid>/fd
-
-               snprintf(filename, FILENAME_MAX, "/proc/%s/fd", file->d_name);
-               DIR *fds = opendir(filename);
-               if(fds) {
-                       int c;
-                       struct dirent *de;
-                       char fdname[FILENAME_MAX + 1];
-                       char linkname[FILENAME_MAX + 1];
-
-                       // make the array negative
-                       for(c = 0 ; c < p->fds_size ; c++) p->fds[c] = -p->fds[c];
-
-                       while((de = readdir(fds))) {
-                               if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
-
-                               // check if the fds array is small
-                               int fdid = atol(de->d_name);
-                               if(fdid < 0) continue;
-                               if(fdid >= p->fds_size) {
-                                       // it is small, extend it
-                                       if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
-                                       p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
-                                       if(!p->fds) {
-                                               error("Cannot re-allocate fds for %s", p->comm);
-                                               break;
-                                       }
-
-                                       // and initialize it
-                                       for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
-                                       p->fds_size = fdid + 100;
-                               }
-
-                               if(p->fds[fdid] == 0) {
-                                       // we don't know this fd, get it
-
-                                       sprintf(fdname, "/proc/%s/fd/%s", file->d_name, de->d_name);
-                                       int l = readlink(fdname, linkname, FILENAME_MAX);
-                                       if(l == -1) {
-                                               if(debug || (p->target && p->target->debug)) {
-                                                       if(!count_errors++ || debug || (p->target && p->target->debug))
-                                                               error("Cannot read link %s", fdname);
-                                               }
-                                               continue;
-                                       }
-                                       linkname[l] = '\0';
-                                       file_counter++;
-
-                                       // if another process already has this, we will get
-                                       // the same id
-                                       p->fds[fdid] = file_descriptor_find_or_add(linkname);
-                               }
-
-                               // else make it positive again, we need it
-                               // of course, the actual file may have changed, but we don't care so much
-                               // FIXME: we could compare the inode as returned by readdir direct structure
-                               else p->fds[fdid] = -p->fds[fdid];
-                       }
-                       closedir(fds);
-
-                       // remove all the negative file descriptors
-                       for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
-                               file_descriptor_not_used(-p->fds[c]);
-                               p->fds[c] = 0;
-                       }
-               }
-
-               // --------------------------------------------------------------------
-               // done!
-
-               // mark it as updated
-               p->updated = 1;
-       }
-       if(count_errors > 1000) {
-               error("%ld more errors encountered\n", count_errors - 1);
-               count_errors = 0;
-       }
-
-       closedir(dir);
-
-       return 1;
-}
-
-
-// ----------------------------------------------------------------------------
-// update statistics on the targets
-
-// 1. link all childs to their parents
-// 2. go from bottom to top, marking as merged all childs to their parents
-//    this step links all parents without a target to the child target, if any
-// 3. link all top level processes (the ones not merged) to the default target
-// 4. go from top to bottom, linking all childs without a target, to their parent target
-//    after this step, all processes have a target
-// [5. for each killed pid (updated = 0), remove its usage from its target]
-// 6. zero all targets
-// 7. concentrate all values on the targets
-// 8. remove all killed processes
-// 9. find the unique file count for each target
-
-void update_statistics(void)
-{
-       int c;
-       struct pid_stat *p = NULL;
-
-       // link all parents and update childs count
-       for(p = root_of_pids; p ; p = p->next) {
-               if(p->ppid > 0 && p->ppid <= pid_max && all_pids[p->ppid]) {
-                       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \tparent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);
-                       
-                       p->parent = all_pids[p->ppid];
-                       p->parent->childs++;
-               }
-               else if(p->ppid != 0) error("pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
-       }
-
-       // find all the procs with 0 childs and merge them to their parents
-       // repeat, until nothing more can be done.
-       int found = 1;
-       while(found) {
-               found = 0;
-               for(p = root_of_pids; p ; p = p->next) {
-                       // if this process does not have any childs, and
-                       // is not already merged, and
-                       // its parent has childs waiting to be merged, and
-                       // the target of this process and its parent is the same, or the parent does not have a target, or this process does not have a parent
-                       // and its parent is not init
-                       // then... merge them!
-                       if(!p->childs && !p->merged && p->parent && p->parent->childs && (p->target == p->parent->target || !p->parent->target || !p->target) && p->ppid != 1) {
-                               p->parent->childs--;
-                               p->merged = 1;
-
-                               // the parent inherits the child's target, if it does not have a target itself
-                               if(p->target && !p->parent->target) {
-                                       p->parent->target = p->target;
-                                       if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\ttarget %s is inherited by %d %s from its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
-                               }
-
-                               found++;
-                       }
-               }
-               if(debug) fprintf(stderr, "apps.plugin: merged %d processes\n", found);
-       }
-
-       // give a default target on all top level processes
-       // init goes always to default target
-       if(all_pids[1]) all_pids[1]->target = default_target;
-
-       for(p = root_of_pids; p ; p = p->next) {
-               // if the process is not merged itself
-               // then is is a top level process
-               if(!p->merged && !p->target) p->target = default_target;
-
-#ifdef INCLUDE_CHILDS
-               // by the way, update the diffs
-               // will be used later for substracting killed process times
-               p->diff_cutime = p->utime - p->cutime;
-               p->diff_cstime = p->stime - p->cstime;
-               p->diff_cminflt = p->minflt - p->cminflt;
-               p->diff_cmajflt = p->majflt - p->cmajflt;
-#endif
-       }
-
-       // give a target to all merged child processes
-       found = 1;
-       while(found) {
-               found = 0;
-               for(p = root_of_pids; p ; p = p->next) {
-                       if(!p->target && p->merged && p->parent && p->parent->target) {
-                               p->target = p->parent->target;
-                               found++;
-                       }
-               }
-       }
-
-#ifdef INCLUDE_CHILDS
-       // for each killed process, remove its values from the parents
-       // sums (we had already added them in a previous loop)
-       for(p = root_of_pids; p ; p = p->next) {
-               if(p->updated) continue;
-
-               if(debug) fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
-
-               unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
-               unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
-               unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
-               unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
-
-               struct pid_stat *t = p;
-               while((t = t->parent)) {
-                       if(!t->updated) continue;
-
-                       unsigned long long x;
-                       if(diff_utime && t->diff_cutime) {
-                               x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
-                               diff_utime -= x;
-                               t->diff_cutime -= x;
-                               t->fix_cutime += x;
-                               if(debug) fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
-                       }
-                       if(diff_stime && t->diff_cstime) {
-                               x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
-                               diff_stime -= x;
-                               t->diff_cstime -= x;
-                               t->fix_cstime += x;
-                               if(debug) fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
-                       }
-                       if(diff_minflt && t->diff_cminflt) {
-                               x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
-                               diff_minflt -= x;
-                               t->diff_cminflt -= x;
-                               t->fix_cminflt += x;
-                               if(debug) fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
-                       }
-                       if(diff_majflt && t->diff_cmajflt) {
-                               x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
-                               diff_majflt -= x;
-                               t->diff_cmajflt -= x;
-                               t->fix_cmajflt += x;
-                               if(debug) fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
-                       }
-               }
-
-               if(diff_utime) error("Cannot fix up utime %llu", diff_utime);
-               if(diff_stime) error("Cannot fix up stime %llu", diff_stime);
-               if(diff_minflt) error("Cannot fix up minflt %llu", diff_minflt);
-               if(diff_majflt) error("Cannot fix up majflt %llu", diff_majflt);
-       }
-#endif
-
-       // zero all the targets
-       targets = 0;
-       struct target *w;
-       for (w = target_root; w ; w = w->next) {
-               targets++;
-
-               w->fds = calloc(sizeof(int), all_files_size);
-               if(!w->fds)
-                       error("Cannot allocate memory for fds in %s", w->name);
-       
-               w->minflt = 0;
-               w->majflt = 0;
-               w->utime = 0;
-               w->stime = 0;
-               w->cminflt = 0;
-               w->cmajflt = 0;
-               w->cutime = 0;
-               w->cstime = 0;
-               w->num_threads = 0;
-               w->rss = 0;
-               w->processes = 0;
-
-               w->statm_size = 0;
-               w->statm_resident = 0;
-               w->statm_share = 0;
-               w->statm_text = 0;
-               w->statm_lib = 0;
-               w->statm_data = 0;
-               w->statm_dirty = 0;
-
-               w->io_logical_bytes_read = 0;
-               w->io_logical_bytes_written = 0;
-               w->io_read_calls = 0;
-               w->io_write_calls = 0;
-               w->io_storage_bytes_read = 0;
-               w->io_storage_bytes_written = 0;
-               w->io_cancelled_write_bytes = 0;
-       }
-
-#ifdef INCLUDE_CHILDS
-       if(debug) walk_down(0, 1);
-#endif
-
-       // concentrate everything on the targets
-       for(p = root_of_pids; p ; p = p->next) {
-               if(!p->target) {
-                       error("pid %d %s was left without a target!", p->pid, p->comm);
-                       continue;
-               }
-
-               if(p->updated) {
-                       p->target->cutime += p->cutime; // - p->fix_cutime;
-                       p->target->cstime += p->cstime; // - p->fix_cstime;
-                       p->target->cminflt += p->cminflt; // - p->fix_cminflt;
-                       p->target->cmajflt += p->cmajflt; // - p->fix_cmajflt;
-
-                       p->target->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
-                       p->target->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
-                       p->target->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
-                       p->target->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
-
-                       //if(p->num_threads < 0)
-                       //      error("Negative threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
-
-                       //if(p->num_threads > 10000)
-                       //      error("Excessive threads number for pid '%s' (%d): %d", p->comm, p->pid, p->num_threads);
-
-                       p->target->num_threads += p->num_threads;
-                       p->target->rss += p->rss;
-
-                       p->target->statm_size += p->statm_size;
-                       p->target->statm_resident += p->statm_resident;
-                       p->target->statm_share += p->statm_share;
-                       p->target->statm_text += p->statm_text;
-                       p->target->statm_lib += p->statm_lib;
-                       p->target->statm_data += p->statm_data;
-                       p->target->statm_dirty += p->statm_dirty;
-
-                       p->target->io_logical_bytes_read += p->io_logical_bytes_read;
-                       p->target->io_logical_bytes_written += p->io_logical_bytes_written;
-                       p->target->io_read_calls += p->io_read_calls;
-                       p->target->io_write_calls += p->io_write_calls;
-                       p->target->io_storage_bytes_read += p->io_storage_bytes_read;
-                       p->target->io_storage_bytes_written += p->io_storage_bytes_written;
-                       p->target->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
-
-                       p->target->processes++;
-
-                       for(c = 0; c < p->fds_size ;c++) {
-                               if(p->fds[c] == 0) continue;
-                               if(p->fds[c] < all_files_size) {
-                                       if(p->target->fds) p->target->fds[p->fds[c]]++;
-                               }
-                               else
-                                       error("Invalid fd number %d", p->fds[c]);
-                       }
-
-                       if(debug || p->target->debug) fprintf(stderr, "apps.plugin: \tAgregating %s pid %d on %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
-
-/*                     if(p->utime - p->old_utime > 100) fprintf(stderr, "BIG CHANGE: %d %s utime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->utime - p->old_utime, p->old_utime, p->utime);
-                       if(p->cutime - p->old_cutime > 100) fprintf(stderr, "BIG CHANGE: %d %s cutime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cutime - p->old_cutime, p->old_cutime, p->cutime);
-                       if(p->stime - p->old_stime > 100) fprintf(stderr, "BIG CHANGE: %d %s stime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->stime - p->old_stime, p->old_stime, p->stime);
-                       if(p->cstime - p->old_cstime > 100) fprintf(stderr, "BIG CHANGE: %d %s cstime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cstime - p->old_cstime, p->old_cstime, p->cstime);
-                       if(p->minflt - p->old_minflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s minflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->minflt - p->old_minflt, p->old_minflt, p->minflt);
-                       if(p->majflt - p->old_majflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s majflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->majflt - p->old_majflt, p->old_majflt, p->majflt);
-                       if(p->cminflt - p->old_cminflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cminflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cminflt - p->old_cminflt, p->old_cminflt, p->cminflt);
-                       if(p->cmajflt - p->old_cmajflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cmajflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cmajflt - p->old_cmajflt, p->old_cmajflt, p->cmajflt);
-*/
-#ifdef INCLUDE_CHILDS
-                       p->old_utime = p->utime;
-                       p->old_cutime = p->cutime;
-                       p->old_stime = p->stime;
-                       p->old_cstime = p->cstime;
-                       p->old_minflt = p->minflt;
-                       p->old_majflt = p->majflt;
-                       p->old_cminflt = p->cminflt;
-                       p->old_cmajflt = p->cmajflt;
-#endif
-               }
-               else {
-                       // since the process has exited, the user
-                       // will see a drop in our charts, because the incremental
-                       // values of this process will not be there
-
-                       // add them to the fix_* values and they will be added to
-                       // the reported values, so that the report goes steady
-                       p->target->fix_minflt += p->minflt;
-                       p->target->fix_majflt += p->majflt;
-                       p->target->fix_utime += p->utime;
-                       p->target->fix_stime += p->stime;
-                       p->target->fix_cminflt += p->cminflt;
-                       p->target->fix_cmajflt += p->cmajflt;
-                       p->target->fix_cutime += p->cutime;
-                       p->target->fix_cstime += p->cstime;
-
-                       p->target->fix_io_logical_bytes_read += p->io_logical_bytes_read;
-                       p->target->fix_io_logical_bytes_written += p->io_logical_bytes_written;
-                       p->target->fix_io_read_calls += p->io_read_calls;
-                       p->target->fix_io_write_calls += p->io_write_calls;
-                       p->target->fix_io_storage_bytes_read += p->io_storage_bytes_read;
-                       p->target->fix_io_storage_bytes_written += p->io_storage_bytes_written;
-                       p->target->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
-               }
-       }
-
-//     fprintf(stderr, "\n");
-       // cleanup all un-updated processed (exited, killed, etc)
-       for(p = root_of_pids; p ;) {
-               if(!p->updated) {
-//                     fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name,  p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
-                       
-                       for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
-                               file_descriptor_not_used(p->fds[c]);
-                               p->fds[c] = 0;
-                       }
-
-                       pid_t r = p->pid;
-                       p = p->next;
-                       del_pid_entry(r);
-               }
-               else p = p->next;
-       }
-
-       for (w = target_root; w ; w = w->next) {
-               w->openfiles = 0;
-               w->openpipes = 0;
-               w->opensockets = 0;
-               w->openinotifies = 0;
-               w->openeventfds = 0;
-               w->opentimerfds = 0;
-               w->opensignalfds = 0;
-               w->openeventpolls = 0;
-               w->openother = 0;
-
-               for(c = 1; c < all_files_size ;c++) {
-                       if(w->fds && w->fds[c] > 0) switch(all_files[c].type) {
-                               case FILETYPE_FILE:
-                                       w->openfiles++;
-                                       break;
-
-                               case FILETYPE_PIPE:
-                                       w->openpipes++;
-                                       break;
-
-                               case FILETYPE_SOCKET:
-                                       w->opensockets++;
-                                       break;
-
-                               case FILETYPE_INOTIFY:
-                                       w->openinotifies++;
-                                       break;
-
-                               case FILETYPE_EVENTFD:
-                                       w->openeventfds++;
-                                       break;
-
-                               case FILETYPE_TIMERFD:
-                                       w->opentimerfds++;
-                                       break;
-
-                               case FILETYPE_SIGNALFD:
-                                       w->opensignalfds++;
-                                       break;
-
-                               case FILETYPE_EVENTPOLL:
-                                       w->openeventpolls++;
-                                       break;
-
-                               default:
-                                       w->openother++;
-                       }
-               }
-
-               free(w->fds);
-               w->fds = NULL;
-       }
-}
-
-// ----------------------------------------------------------------------------
-// update chart dimensions
-
-void show_dimensions(void)
-{
-       static struct timeval last = { 0, 0 };
-       static struct rusage me_last;
-
-       struct target *w;
-       struct timeval now;
-       struct rusage me;
-
-       unsigned long long usec;
-       unsigned long long cpuuser;
-       unsigned long long cpusyst;
-
-       if(!last.tv_sec) {
-               gettimeofday(&last, NULL);
-               getrusage(RUSAGE_SELF, &me_last);
-
-               // the first time, give a zero to allow
-               // netdata calibrate to the current time
-               // usec = update_every * 1000000ULL;
-               usec = 0ULL;
-               cpuuser = 0;
-               cpusyst = 0;
-       }
-       else {
-               gettimeofday(&now, NULL);
-               getrusage(RUSAGE_SELF, &me);
-
-               usec = usecdiff(&now, &last);
-               cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
-               cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
-
-               bcopy(&now, &last, sizeof(struct timeval));
-               bcopy(&me, &me_last, sizeof(struct rusage));
-       }
-
-       fprintf(stdout, "BEGIN apps.cpu %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + w->fix_utime + w->fix_stime);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.cpu_user %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->fix_utime);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.cpu_system %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + w->fix_stime);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.threads %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.processes %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %lu\n", w->name, w->processes);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.mem %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %lld\n", w->name, (long long)w->statm_resident - (long long)w->statm_share);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.minor_faults %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + w->fix_minflt);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.major_faults %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + w->fix_majflt);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.lreads %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.lwrites %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.preads %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.pwrites %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.files %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->openfiles);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.sockets %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->opensockets);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN apps.pipes %llu\n", usec);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "SET %s = %llu\n", w->name, w->openpipes);
-       }
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
-       fprintf(stdout, "SET user = %llu\n", cpuuser);
-       fprintf(stdout, "SET system = %llu\n", cpusyst);
-       fprintf(stdout, "END\n");
-
-       fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
-       fprintf(stdout, "SET files = %llu\n", file_counter);
-       fprintf(stdout, "SET pids = %ld\n", all_pids_count);
-       fprintf(stdout, "SET fds = %d\n", all_files_len);
-       fprintf(stdout, "SET targets = %ld\n", targets);
-       fprintf(stdout, "END\n");
-
-       fflush(stdout);
-}
-
-
-// ----------------------------------------------------------------------------
-// generate the charts
-
-void show_charts(void)
-{
-       struct target *w;
-       int newly_added = 0;
-
-       for(w = target_root ; w ; w = w->next)
-               if(!w->exposed && w->processes) {
-                       newly_added++;
-                       w->exposed = 1;
-                       if(debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
-               }
-
-       // nothing more to show
-       if(!newly_added) return;
-
-       // we have something new to show
-       // update the charts
-       fprintf(stdout, "CHART apps.cpu '' 'Apps CPU Time (%ld%% = %ld core%s)' 'cpu time %%' apps apps stacked 20001 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, (unsigned long long)(Hertz * update_every), w->hidden?"hidden":"");
-       }
-
-       fprintf(stdout, "CHART apps.mem '' 'Apps Dedicated Memory (w/o shared)' 'MB' apps apps stacked 20003 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
-       }
-
-       fprintf(stdout, "CHART apps.threads '' 'Apps Threads' 'threads' apps apps stacked 20005 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       fprintf(stdout, "CHART apps.processes '' 'Apps Processes' 'processes' apps apps stacked 20004 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       fprintf(stdout, "CHART apps.cpu_user '' 'Apps CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20020 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
-       }
-
-       fprintf(stdout, "CHART apps.cpu_system '' 'Apps CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20021 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
-       }
-
-       fprintf(stdout, "CHART apps.major_faults '' 'Apps Major Page Faults (swaps in)' 'page faults/s' apps apps stacked 20010 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
-       }
-
-       fprintf(stdout, "CHART apps.minor_faults '' 'Apps Minor Page Faults' 'page faults/s' apps none stacked 20011 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
-       }
-
-       fprintf(stdout, "CHART apps.lreads '' 'Apps Disk Logical Reads' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
-       }
-
-       fprintf(stdout, "CHART apps.lwrites '' 'Apps I/O Logical Writes' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
-       }
-
-       fprintf(stdout, "CHART apps.preads '' 'Apps Disk Reads' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
-       }
-
-       fprintf(stdout, "CHART apps.pwrites '' 'Apps Disk Writes' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
-       }
-
-       fprintf(stdout, "CHART apps.files '' 'Apps Open Files' 'open files' apps apps stacked 20050 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       fprintf(stdout, "CHART apps.sockets '' 'Apps Open Sockets' 'open sockets' apps apps stacked 20051 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       fprintf(stdout, "CHART apps.pipes '' 'Apps Pipes' 'open pipes' apps none stacked 20053 %d\n", update_every);
-       for (w = target_root; w ; w = w->next) {
-               if(w->target || (!w->processes && !w->exposed)) continue;
-
-               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
-       }
-
-       fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' netdata netdata stacked 10000 %d\n", update_every);
-       fprintf(stdout, "DIMENSION user '' incremental 1 %d\n", 1000 * update_every);
-       fprintf(stdout, "DIMENSION system '' incremental 1 %d\n", 1000 * update_every);
-
-       fprintf(stdout, "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' netdata netdata line 10001 %d\n", update_every);
-       fprintf(stdout, "DIMENSION files '' incremental 1 %d\n", update_every);
-       fprintf(stdout, "DIMENSION pids '' absolute 1 1\n");
-       fprintf(stdout, "DIMENSION fds '' absolute 1 1\n");
-       fprintf(stdout, "DIMENSION targets '' absolute 1 1\n");
-
-       fflush(stdout);
-}
-
-
-// ----------------------------------------------------------------------------
-// parse command line arguments
-
-void parse_args(int argc, char **argv)
-{
-       int i, freq = 0;
-       char *name = NULL;
-
-       for(i = 1; i < argc; i++) {
-               if(!freq) {
-                       int n = atoi(argv[i]);
-                       if(n > 0) {
-                               freq = n;
-                               continue;
-                       }
-               }
-
-               if(strcmp("debug", argv[i]) == 0) {
-                       debug = 1;
-                       debug_flags = 0xffffffff;
-                       continue;
-               }
-
-               if(!name) {
-                       name = argv[i];
-                       continue;
-               }
-
-               error("Cannot understand option %s", argv[i]);
-               exit(1);
-       }
-
-       if(freq > 0) update_every = freq;
-       if(!name) name = "groups";
-
-       if(read_process_groups(name)) {
-               error("Cannot read process groups %s", name);
-               exit(1);
-       }
-}
-
-int main(int argc, char **argv)
-{
-       // debug_flags = D_PROCFILE;
-
-       // set the name for logging
-       program_name = "apps.plugin";
-
-       info("starting...");
-
-       procfile_adaptive_initial_allocation = 1;
-
-       unsigned long started_t = time(NULL), current_t;
-       Hertz = get_hertz();
-       pid_max = get_pid_max();
-       processors = get_processors();
-
-       parse_args(argc, argv);
-
-       all_pids = calloc(sizeof(struct pid_stat *), pid_max);
-       if(!all_pids) {
-               error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
-               printf("DISABLE\n");
-               exit(1);
-       }
-
-       unsigned long long counter = 1;
-       unsigned long long usec = 0, susec = 0;
-       struct timeval last, now;
-       gettimeofday(&last, NULL);
-
-       for(;1; counter++) {
-               if(!update_from_proc()) {
-                       error("Cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
-                       printf("DISABLE\n");
-                       exit(1);
-               }
-
-               update_statistics();
-               show_charts();          // this is smart enough to show only newly added apps, when needed
-               show_dimensions();
-
-               if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
-               fflush(NULL);
-
-               gettimeofday(&now, NULL);
-               usec = usecdiff(&now, &last) - susec;
-               if(debug) fprintf(stderr, "apps.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);
-
-               // if the last loop took less than half the time
-               // wait the rest of the time
-               if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec;
-               else susec = update_every * 1000000ULL / 2;
-
-               usleep(susec);
-               bcopy(&now, &last, sizeof(struct timeval));
-               
-               current_t = time(NULL);
-               if(current_t - started_t > 3600) exit(0);
-       }
-}
index 05d59eb4147bd13b9b2d432e0b472c7a63c8b75c..71d1fb2defefa5ea6d3ccbdba744365ff8e56839 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <sys/types.h>
 #include <dirent.h>
 #include <pthread.h>
@@ -8,7 +11,7 @@
 
 #include "main.h"
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "log.h"
 #include "rrd.h"
 #include "popen.h"
index dbc55c1bcc126e1a705e61ef40f596072d9a217c..16f21a7d8484733c8d936fb6f55c1bdc6b3bf3e6 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <signal.h>
 #include <unistd.h>
 #include <stdlib.h>
index e7a84162c7426e2f528fc2713ff79c4bdcb08bb5..d48b8bf980ae45fbd9c73ef911e46ccaa41f28e4 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 6e25b8408a7f4f81c01549f3ae8d57034a54663d..1e811eafe86612f8c4a78bba02cfab25af8b57d8 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index ad702687636e5381176f92642ed17a18ffa325b8..ceec23784dfaef61a60b79198c3c0a5f1ed64d50 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 88ab8fe9ada28dd326a17b5dd083e8848dd9bb75..0bb97fa7ab6249f8f459eb14bbeaeab22a5a1f42 100755 (executable)
@@ -1,9 +1,11 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 3e46c186da74079b89509f444b2f69e42537dc1c..18114af2270da647389e64bdaafd12f056220ca8 100755 (executable)
@@ -1,9 +1,11 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index ffdc80cf3180ea0ff2414794c1ccd68627e7ce38..35535265e37198a88ee8722adc9ed2cf54b8b6d9 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index a27fc2e45e24c559f0da72139fb4fa9632f1b9bd..b87a0c8e4722cc1464a802bccb27611a8c27e125 100644 (file)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index a3b5d296050b0d469cfdf970cd06f794ecf5dd12..b69298277cad6f6fe9d38b2cee97c2f5612047d2 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index e3d823306208208476db61fc1a2bd27e22788e38..5f16c8df2089fbe4161dfa81eb62942b0e44ca0d 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 77599befbb8f350ee7658173dfed6715606d272b..27d11fb32521ae147592f8127c9de315eada1bbe 100755 (executable)
@@ -1,11 +1,13 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 790ee10230f71ead9604cc1368a77b1f8cc57d12..b576968b0240e1b561669dfcedb947b5d8df9a03 100755 (executable)
@@ -1,9 +1,11 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 
 #include "common.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index 39b44a1aff4ca285bbf8c081c31b30a5d1f2cd8a..b6c8ce7bed72f5a11df84141f633e9a96246727b 100755 (executable)
@@ -1,4 +1,6 @@
-#include <inttypes.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -6,7 +8,7 @@
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "procfile.h"
 #include "rrd.h"
 #include "plugin_proc.h"
index f2269457d0d657c0a4096220b272c0672ebfd9b3..31b97ae20e837814887eca87ff84d136f806afd4 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -5,7 +8,6 @@
 #include <fcntl.h>
 #include <string.h>
 #include <malloc.h>
-#include <inttypes.h>
 #include <ctype.h>
 #include <time.h>
 #include <sys/time.h>
index aabcd5c0959d19c078039ea04fc8ebada3769040..7aa2327e161284eec798cbaa769d7f73b45503f3 100755 (executable)
--- a/src/rrd.c
+++ b/src/rrd.c
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stddef.h>
 #include <string.h>
 #include <unistd.h>
@@ -11,9 +14,9 @@
 #include <sys/types.h>
 #include <stdlib.h>
 
-#include "log.h"
-#include "config.h"
 #include "common.h"
+#include "log.h"
+#include "appconfig.h"
 
 #include "rrd.h"
 
@@ -278,7 +281,7 @@ char *rrdset_cache_dir(const char *id)
        char *ret = NULL;
 
        static char *cache_dir = NULL;
-       if(!cache_dir) cache_dir = config_get("global", "database directory", "cache");
+       if(!cache_dir) cache_dir = config_get("global", "database directory", CACHE_DIR);
 
        char b[FILENAME_MAX + 1];
        char n[FILENAME_MAX + 1];
index e2a88d9ef451e4cd9734482b14f60121e8ae69fb..84ba5866756a3acfffc5c59d80dd64a234db13d4 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <pthread.h>
 #include <sys/time.h>
 #include <stdlib.h>
index e37512991b41a0dbb63465edc1908ca4b04bec6a..fe8e4f1f70e6be8b6f6922cadf0445e2a712cb6a 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #ifdef STORAGE_WITH_MATH
 #include <math.h>
 #endif
index e26fde78767f14fe7278275415dd9c52d6907309..50d3e52f141d035e0ed39318795b8b5b3ccdf5b9 100755 (executable)
@@ -1,5 +1,3 @@
-#include <inttypes.h>
-
 #ifndef NETDATA_STORAGE_NUMBER_H
 #define NETDATA_STORAGE_NUMBER_H
 
index ca5983a7de4fc33185a16e08fbaa01b5ec7d69a3..dd69d68a90f4f512e7e176d8fab44296be45c1cd 100755 (executable)
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
index c8c0921020a7ca564c5a69818afd0877c2576140..c9dbd41590940c54d586daada51b3b3cad8767dd 100755 (executable)
--- a/src/url.c
+++ b/src/url.c
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
index d9eb11d54b93453f83da1306e98190097c3f8b15..f679f1b7e6d2f36e4d7d4c051ca76dd1d259f205 100755 (executable)
@@ -1,12 +1,14 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdlib.h>
 
 #ifdef STORAGE_WITH_MATH
 #include <math.h>
 #endif
 
-#include "web_buffer.h"
-
 #include "common.h"
+#include "web_buffer.h"
 #include "log.h"
 
 void web_buffer_strcpy(struct web_buffer *wb, const char *txt)
index 1fd41a1ea1cc739c1d611af550ebed6e53acbf6f..48fd47be4bd51cec3ab9edbc6637ec9eb64b64b7 100755 (executable)
@@ -1,6 +1,6 @@
-// enable strcasestr()
-#define _GNU_SOURCE
-
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -16,7 +16,7 @@
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "url.h"
 #include "web_buffer.h"
 #include "web_server.h"
@@ -136,7 +136,7 @@ struct web_client *web_client_free(struct web_client *w)
 int mysendfile(struct web_client *w, char *filename)
 {
        static char *web_dir = NULL;
-       if(!web_dir) web_dir = config_get("global", "web files directory", "web");
+       if(!web_dir) web_dir = config_get("global", "web files directory", WEB_DIR);
 
        debug(D_WEB_CLIENT, "%llu: Looking for file '%s'...", w->id, filename);
 
@@ -227,7 +227,7 @@ void web_client_reset(struct web_client *w)
 
        long sent = (w->mode == WEB_CLIENT_MODE_FILECOPY)?w->data->rbytes:w->data->bytes;
 
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
        if(likely(w->zoutput)) sent = (long)w->zstream.total_out;
 #endif
 
@@ -270,7 +270,7 @@ void web_client_reset(struct web_client *w)
        w->zoutput = 0;
 
        // if we had enabled compression, release it
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
        if(w->zinitialized) {
                debug(D_DEFLATE, "%llu: Reseting compression.", w->id);
                deflateEnd(&w->zstream);
@@ -282,10 +282,10 @@ void web_client_reset(struct web_client *w)
                w->zstream.total_out = 0;
                w->zinitialized = 0;
        }
-#endif // NETDATA_WITHOUT_ZLIB
+#endif // NETDATA_WITH_ZLIB
 }
 
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
 void web_client_enable_deflate(struct web_client *w) {
        if(w->zinitialized == 1) {
                error("%llu: Compression has already be initialized for this client.", w->id);
@@ -330,7 +330,7 @@ void web_client_enable_deflate(struct web_client *w) {
 
        debug(D_DEFLATE, "%llu: Initialized compression.", w->id);
 }
-#endif // NETDATA_WITHOUT_ZLIB
+#endif // NETDATA_WITH_ZLIB
 
 int web_client_data_request(struct web_client *w, char *url, int datasource_type)
 {
@@ -555,11 +555,11 @@ void web_client_process(struct web_client *w) {
                if(strcasestr(w->data->buffer, "Connection: keep-alive")) w->keepalive = 1;
                else w->keepalive = 0;
 
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
                // check if the client accepts deflate
                if(web_enable_gzip && strstr(w->data->buffer, "gzip"))
                        web_client_enable_deflate(w);
-#endif // NETDATA_WITHOUT_ZLIB
+#endif // NETDATA_WITH_ZLIB
 
                int datasource_type = DATASOURCE_GOOGLE_JSONP;
                //if(strstr(w->data->buffer, "X-DataSource-Auth"))
@@ -988,7 +988,7 @@ long web_client_send_chunk_finalize(struct web_client *w)
        return bytes;
 }
 
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
 long web_client_send_deflate(struct web_client *w)
 {
        long bytes = 0, t = 0;
@@ -1089,13 +1089,13 @@ long web_client_send_deflate(struct web_client *w)
 
        return(bytes);
 }
-#endif // NETDATA_WITHOUT_ZLIB
+#endif // NETDATA_WITH_ZLIB
 
 long web_client_send(struct web_client *w)
 {
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
        if(likely(w->zoutput)) return web_client_send_deflate(w);
-#endif // NETDATA_WITHOUT_ZLIB
+#endif // NETDATA_WITH_ZLIB
 
        long bytes;
 
index f633ca754c9dd33066f80c0cb1091b53613bb5b8..fbfc034ab3eaa1f1e8a8ad1ac016c90309fc97b9 100755 (executable)
@@ -1,6 +1,6 @@
-// enable strcasestr()
-#define _GNU_SOURCE
-
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -16,7 +16,7 @@
 
 #include "common.h"
 #include "log.h"
-#include "config.h"
+#include "appconfig.h"
 #include "url.h"
 #include "web_buffer.h"
 #include "web_client.h"
diff --git a/web/Makefile.am b/web/Makefile.am
new file mode 100644 (file)
index 0000000..2344edf
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+dist_web_DATA = \
+       datasource.html \
+       index.html \
+       index.js \
+       netdata.js \
+       robots.txt \
+       theme.css \
+       $(NULL)