-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
+++ /dev/null
-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
--- /dev/null
+#
+# 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
--- /dev/null
+#!/bin/sh
+autoreconf -ivf
--- /dev/null
+#
+# 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)
--- /dev/null
+#
+# Copyright (C) 2015 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
+
+dist_config_DATA = \
+ apps_groups.conf \
+ $(NULL)
--- /dev/null
+#
+# 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
--- /dev/null
+# ===========================================================================
+# 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
--- /dev/null
+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.
--- /dev/null
+#
+# 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)
-#!/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
+++ /dev/null
-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
--- /dev/null
+#
+# 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
--- /dev/null
+#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);
+ }
+ }
+ }
+ }
+}
+
--- /dev/null
+#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 */
--- /dev/null
+// 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);
+ }
+}
*
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include "avl.h"
/* Private methods */
-// enable O_NOATIME
-#define _GNU_SOURCE
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <sys/syscall.h>
#include <string.h>
#include <ctype.h>
+#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)))
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;
+++ /dev/null
-#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);
- }
- }
- }
- }
-}
-
+++ /dev/null
-#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 */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#ifdef NETDATA_DAEMON
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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"
return(0);
}
+#endif
+#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"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pthread.h>
#include "common.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <time.h>
#include <syslog.h>
#include <errno.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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"
// --------------------------------------------------------------------
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);
// 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);
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pthread.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include "common.h"
-#include "config.h"
+#include "appconfig.h"
#include "log.h"
#include "rrd.h"
#include "plugin_checks.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pthread.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "global_statistics.h"
#include "common.h"
-#include "config.h"
+#include "appconfig.h"
#include "log.h"
#include "rrd.h"
#include "plugin_idlejitter.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#ifdef INTERNAL_PLUGIN_NFACCT
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "global_statistics.h"
#include "common.h"
-#include "config.h"
+#include "appconfig.h"
#include "log.h"
#include "rrd.h"
#include "plugin_proc.h"
mnl_socket_close(nl);
return NULL;
}
+#endif
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pthread.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "global_statistics.h"
#include "common.h"
-#include "config.h"
+#include "appconfig.h"
#include "log.h"
#include "rrd.h"
#include "plugin_proc.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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"
+++ /dev/null
-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
+++ /dev/null
-// 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);
- }
-}
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include "main.h"
#include "common.h"
-#include "config.h"
+#include "appconfig.h"
#include "log.h"
#include "rrd.h"
#include "popen.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
-#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"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <malloc.h>
-#include <inttypes.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#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"
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];
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <pthread.h>
#include <sys/time.h>
#include <stdlib.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#ifdef STORAGE_WITH_MATH
#include <math.h>
#endif
-#include <inttypes.h>
-
#ifndef NETDATA_STORAGE_NUMBER_H
#define NETDATA_STORAGE_NUMBER_H
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#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)
-// enable strcasestr()
-#define _GNU_SOURCE
-
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include "common.h"
#include "log.h"
-#include "config.h"
+#include "appconfig.h"
#include "url.h"
#include "web_buffer.h"
#include "web_server.h"
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);
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
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);
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);
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)
{
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"))
return bytes;
}
-#ifndef NETDATA_WITHOUT_ZLIB
+#ifdef NETDATA_WITH_ZLIB
long web_client_send_deflate(struct web_client *w)
{
long bytes = 0, t = 0;
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;
-// enable strcasestr()
-#define _GNU_SOURCE
-
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include "common.h"
#include "log.h"
-#include "config.h"
+#include "appconfig.h"
#include "url.h"
#include "web_buffer.h"
#include "web_client.h"
--- /dev/null
+#
+# 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)