0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-29 07:00:01 +00:00

stock configs in /usr/lib/netdata ()

* makefiles install configs in /usr/lib/netdata/conf.d; 

* stock health config in /usr/lib/netdata/conf.d/health.d

* unit test path concatenation

* simplified health file management

* use stream.conf from stock config if it does not exist in /etc/netdata

* indicate loading of user config in function call

* load netdata.conf from stock dir if not found in /etc/netdata

* added NETDATA_USER_CONFIG_DIR

* provide defaults before loading config

* charts.d uses stock files

* fping now uses the stock config files

* tc-qos-helper.sh now uses stock configs

* cgroup-name.sh now uses stock configs too

* simplified cgroup-name.sh for user and stock config

* alarm-notify.sh uses stock configs too

* simplified fping plugin configs loading

* simplified tc-qos-helper.sh configs loading

* added error handling to charts.d.plugin

* apps.plugin used stock configs

* generalized recursive double-directory configs loading

* statsd uses stock configs

* node.d.plugin uses stock configs

* compile-time decision of netdata default paths for all files

* makeself cleans up old stock config files from user configuration directories

* fixed makeself typo

* netdata-installer.sh removes stock files from user configuration directories

* python.d.plugin user/stock configs update

* cleanup stock config files from /etc/netdata, only once

* python.d.plugin log loaded files

* fix permissions of stock config files and provide an "orig" link for quick access

* create help link on stock configs migration for static installations

* create user config directories

* example statsd synthetic charts now state they are examples

* updated configs.signatures

* spec file

* fixes in spec file

* fix typo

* install netdata after cleaning up stock configs from /etc/netdata

* python.d: add cpuidle stock conf
This commit is contained in:
Costa Tsaousis 2018-09-27 20:37:52 +03:00 committed by GitHub
parent a440a24688
commit 73608f86b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 966 additions and 591 deletions

8
.gitignore vendored
View file

@ -75,6 +75,14 @@ system/netdata.service
system/netdata.plist
system/netdata-freebsd
plugins.d/alarm-notify.sh
plugins.d/cgroup-name.sh
plugins.d/charts.d.plugin
plugins.d/fping.plugin
plugins.d/node.d.plugin
plugins.d/python.d.plugin
plugins.d/tc-qos-helper.sh
# installer generated files
netdata-uninstaller.sh
netdata-updater.sh

View file

@ -250,7 +250,7 @@ set(CGROUP_NETWORK_SOURCE_FILES
include_directories(AFTER .)
add_definitions(-DHAVE_CONFIG_H -DCACHE_DIR="/var/cache/netdata" -DCONFIG_DIR="/etc/netdata" -DLOG_DIR="/var/log/netdata" -DPLUGINS_DIR="/usr/libexec/netdata" -DWEB_DIR="/usr/share/netdata" -DVARLIB_DIR="/var/lib/netdata")
add_definitions(-DHAVE_CONFIG_H -DCACHE_DIR="/var/cache/netdata" -DCONFIG_DIR="/etc/netdata" -DLIBCONFIG_DIR="/usr/lib/netdata/conf.d" -DLOG_DIR="/var/log/netdata" -DPLUGINS_DIR="/usr/libexec/netdata" -DWEB_DIR="/usr/share/netdata" -DVARLIB_DIR="/var/lib/netdata")
add_executable(netdata ${NETDATA_COMMON_FILES} ${NETDATA_LINUX_FILES})
target_link_libraries (netdata m z uuid mnl netfilter_acct ${CMAKE_THREAD_LIBS_INIT})

View file

@ -4,6 +4,9 @@
-e 's#[@]sbindir_POST@#$(sbindir)#g' \
-e 's#[@]sysconfdir_POST@#$(sysconfdir)#g' \
-e 's#[@]pythondir_POST@#$(pythondir)#g' \
-e 's#[@]configdir_POST@#$(configdir)#g' \
-e 's#[@]libconfigdir_POST@#$(libconfigdir)#g' \
-e 's#[@]cachedir_POST@#$(cachedir)#g' \
$< > $@.tmp; then \
mv "$@.tmp" "$@"; \
else \

View file

@ -4,7 +4,7 @@
#
MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
dist_config_DATA = \
dist_libconfig_DATA = \
apps_groups.conf \
charts.d.conf \
fping.conf \
@ -15,7 +15,7 @@ dist_config_DATA = \
stream.conf \
$(NULL)
nodeconfigdir=$(configdir)/node.d
nodeconfigdir=$(libconfigdir)/node.d
dist_nodeconfig_DATA = \
node.d/README.md \
node.d/fronius.conf.md \
@ -25,7 +25,7 @@ dist_nodeconfig_DATA = \
node.d/stiebeleltron.conf.md \
$(NULL)
pythonconfigdir=$(configdir)/python.d
pythonconfigdir=$(libconfigdir)/python.d
dist_pythonconfig_DATA = \
python.d/apache.conf \
python.d/beanstalk.conf \
@ -34,6 +34,7 @@ dist_pythonconfig_DATA = \
python.d/ceph.conf \
python.d/chrony.conf \
python.d/couchdb.conf \
python.d/cpuidle.conf \
python.d/cpufreq.conf \
python.d/dns_query_time.conf \
python.d/dnsdist.conf \
@ -88,8 +89,7 @@ dist_pythonconfig_DATA = \
python.d/web_log.conf \
$(NULL)
healthconfigdir=$(configdir)/health.d
healthconfigdir=$(libconfigdir)/health.d
dist_healthconfig_DATA = \
health.d/apache.conf \
health.d/apcupsd.conf \
@ -148,7 +148,7 @@ dist_healthconfig_DATA = \
health.d/zfs.conf \
$(NULL)
chartsconfigdir=$(configdir)/charts.d
chartsconfigdir=$(libconfigdir)/charts.d
dist_chartsconfig_DATA = \
charts.d/apache.conf \
charts.d/apcupsd.conf \
@ -172,7 +172,7 @@ dist_chartsconfig_DATA = \
charts.d/squid.conf \
$(NULL)
statsdconfigdir=$(configdir)/statsd.d
statsdconfigdir=$(libconfigdir)/statsd.d
dist_statsdconfig_DATA = \
statsd.d/example.conf \
$(NULL)

View file

@ -0,0 +1,40 @@
# netdata python.d.plugin configuration for cpuidle
#
# This file is in YaML format. Generally the format is:
#
# name: value
#
# There are 2 sections:
# - global variables
# - one or more JOBS
#
# JOBS allow you to collect values from multiple sources.
# Each source will have its own set of charts.
#
# JOB parameters have to be indented (using spaces only, example below).
# ----------------------------------------------------------------------
# Global Variables
# These variables set the defaults for all JOBs, however each JOB
# may define its own, overriding the defaults.
# update_every sets the default data collection frequency.
# If unset, the python.d.plugin default is used.
# update_every: 1
# priority controls the order of charts at the netdata dashboard.
# Lower numbers move the charts towards the top of the page.
# If unset, the default for python.d.plugin is used.
# priority: 60000
# retries sets the number of retries to be made in case of failures.
# If unset, the default for python.d.plugin is used.
# Attempts to restore the service are made once every update_every
# and only if the module has collected values in the past.
# retries: 60
# autodetection_retry sets the job re-check interval in seconds.
# The job is not deleted if check fails.
# Attempts to start the job are made once every autodetection_retry.
# This feature is disabled by default.
# autodetection_retry: 0

View file

@ -7,10 +7,10 @@
# give a name for this app
# this controls the main menu on the dashboard
# and will be the prefix for all charts of the app
name = myapp
name = myexampleapp
# match all the metrics of the app
metrics = myapp.*
metrics = myexampleapp.*
# shall private charts of these metrics be created?
private charts = no
@ -29,10 +29,10 @@
# create a chart
# this is its id - the chart will be named myapp.mychart
[mychart]
# this is its id - the chart will be named myexampleapp.myexamplechart
[myexamplechart]
# a name for the chart, similar to the id (2 names for each chart)
name = mychart
name = myexamplechart
# the chart title
title = my chart title
@ -57,9 +57,9 @@
# events = the number of events for this metric
# last = the last value collected
# all the others are only valid for histograms and timers
dimension = myapp.metric1 avg average 1 1
dimension = myapp.metric1 lower min 1 1
dimension = myapp.metric1 upper max 1 1
dimension = myapp.metric2 other last 1 1
dimension = myexampleapp.metric1 avg average 1 1
dimension = myexampleapp.metric1 lower min 1 1
dimension = myexampleapp.metric1 upper max 1 1
dimension = myexampleapp.metric2 other last 1 1
# You can add as many charts as needed

View file

@ -65,6 +65,7 @@ declare -A configs_signatures=(
['111ead4b350593dd69b6f7ac0307b49b']='python.d/httpcheck.conf'
['12a4c7803ae79506a14ea784fea60dce']='health.d/net.conf'
['12d27b9f4d1696c2d49a77ed71d68e6f']='python.d/w1sensor.conf'
['12e57bea1127933a4fe49ce2e9674f4d']='statsd.d/example.conf'
['13141998a5d71308d9c119834c27bfd3']='python.d.conf'
['13ccf65fd879795f0fcea89ade27c2d0']='health.d/swap.conf'
['13e861a3d2f3075de883994ab54df658']='health.d/megacli.conf'

View file

@ -493,6 +493,7 @@ AC_SUBST([chartsdir], ["\$(libexecdir)/netdata/charts.d"])
AC_SUBST([nodedir], ["\$(libexecdir)/netdata/node.d"])
AC_SUBST([pythondir], ["\$(libexecdir)/netdata/python.d"])
AC_SUBST([configdir], ["\$(sysconfdir)/netdata"])
AC_SUBST([libconfigdir], ["\$(libdir)/netdata/conf.d"])
AC_SUBST([logdir], ["\$(localstatedir)/log/netdata"])
AC_SUBST([pluginsdir], ["\$(libexecdir)/netdata/plugins.d"])
AC_SUBST([webdir])

View file

@ -24,62 +24,49 @@ do
shift
done
deleted_stock_configs=0
if [ ! -f "etc/netdata/.installer-cleanup-of-stock-configs-done" ]
then
# -----------------------------------------------------------------------------
progress "Checking new configuration files"
# -----------------------------------------------------------------------------
progress "Deleting stock configuration files from user configuration directory"
declare -A configs_signatures=()
. system/configs.signatures
declare -A configs_signatures=()
source "system/configs.signatures"
if [ ! -d etc/netdata ]
then
run mkdir -p etc/netdata
fi
md5sum="$(which md5sum 2>/dev/null || command -v md5sum 2>/dev/null || command -v md5 2>/dev/null)"
for x in $(find etc.new -type f)
do
# find it relative filename
f="${x/etc.new\/netdata\//}"
t="${x/etc.new\//etc\/}"
d=$(dirname "${t}")
#echo >&2 "x: ${x}"
#echo >&2 "t: ${t}"
#echo >&2 "d: ${d}"
if [ ! -d "${d}" ]
if [ ! -d etc/netdata ]
then
run mkdir -p "${d}"
run mkdir -p etc/netdata
fi
if [ ! -f "${t}" ]
then
run cp "${x}" "${t}"
continue
fi
md5sum="$(which md5sum 2>/dev/null || command -v md5sum 2>/dev/null || command -v md5 2>/dev/null)"
for x in $(find etc -type f)
do
# find it relative filename
f="${x/etc\/netdata\//}"
if [ ! -z "${md5sum}" ]
then
# find the checksum of the existing file
md5="$(cat "${t}" | ${md5sum} | cut -d ' ' -f 1)"
#echo >&2 "md5: ${md5}"
# find the stock filename
t="${f/.conf.old/.conf}"
t="${t/.conf.orig/.conf}"
# check if it matches
if [ "${configs_signatures[${md5}]}" = "${f}" ]
if [ ! -z "${md5sum}" ]
then
run cp "${x}" "${t}"
# find the checksum of the existing file
md5="$( ${md5sum} <"${x}" | cut -d ' ' -f 1)"
#echo >&2 "md5: ${md5}"
# check if it matches
if [ "${configs_signatures[${md5}]}" = "${t}" ]
then
# it matches the default
run rm -f "${x}"
deleted_stock_configs=$(( deleted_stock_configs + 1 ))
fi
fi
fi
if ! [[ "${x}" =~ .*\.orig ]]
then
run mv "${x}" "${t}.orig"
fi
done
run rm -rf etc.new
done
touch "etc/netdata/.installer-cleanup-of-stock-configs-done"
fi
# -----------------------------------------------------------------------------
progress "Add user netdata to required user groups"
@ -166,6 +153,26 @@ dir_should_be_link . var/lib/netdata netdata-dbs
dir_should_be_link . var/cache/netdata netdata-metrics
dir_should_be_link . var/log/netdata netdata-logs
dir_should_be_link etc/netdata ../../usr/lib/netdata/conf.d orig
if [ ${deleted_stock_configs} -gt 0 ]
then
dir_should_be_link etc/netdata ../../usr/lib/netdata/conf.d "000.-.USE.THE.orig.LINK.TO.COPY.AND.EDIT.STOCK.CONFIG.FILES"
fi
# -----------------------------------------------------------------------------
progress "create user config directories"
for x in "python.d" "charts.d" "node.d" "health.d" "statsd.d"
do
if [ ! -d "etc/netdata/${x}" ]
then
run mkdir -p "etc/netdata/${x}" || exit 1
fi
done
# -----------------------------------------------------------------------------
progress "fix permissions"

View file

@ -13,13 +13,6 @@ else
# export CFLAGS="-static -O1 -ggdb -Wall -Wextra -Wformat-signedness"
fi
if [ ! -z "${NETDATA_INSTALL_PATH}" -a -d "${NETDATA_INSTALL_PATH}/etc" ]
then
# make sure we don't have an old etc path, so that the installer
# will install all files without examining changes
run mv "${NETDATA_INSTALL_PATH}/etc" "${NETDATA_INSTALL_PATH}/etc.new"
fi
run ./netdata-installer.sh --install "${NETDATA_INSTALL_PARENT}" \
--dont-wait \
--dont-start-it \

View file

@ -72,27 +72,6 @@ EOF
run chmod 755 "${NETDATA_INSTALL_PATH}/bin/netdata"
# -----------------------------------------------------------------------------
# move etc to protect the destination when unpacked
if [ ! -z "${NETDATA_INSTALL_PATH}" -a -d "${NETDATA_INSTALL_PATH}/etc" ]
then
if [ -d "${NETDATA_INSTALL_PATH}/etc.new" ]
then
run rm -rf "${NETDATA_INSTALL_PATH}/etc.new" || exit 1
fi
run mv "${NETDATA_INSTALL_PATH}/etc" \
"${NETDATA_INSTALL_PATH}/etc.new" || exit 1
if [ -f "${NETDATA_INSTALL_PATH}/etc.new/netdata/netdata.conf" ]
then
# delete the generated netdata.conf, so that the static installer will generate a new one
run rm "${NETDATA_INSTALL_PATH}/etc.new/netdata/netdata.conf"
fi
fi
# -----------------------------------------------------------------------------
# remove the links to allow untaring the archive

View file

@ -492,6 +492,7 @@ progress "Cleanup compilation directory"
[ -f src/netdata ] && run make clean
# -----------------------------------------------------------------------------
progress "Compile netdata"
@ -543,79 +544,76 @@ if [ -d "${NETDATA_PREFIX}/etc/netdata" ]
fi
# -----------------------------------------------------------------------------
progress "Backup existing netdata configuration before installing it"
if [ "${BASH_VERSINFO[0]}" -ge "4" ]
deleted_stock_configs=0
if [ ! -f "${NETDATA_PREFIX}/etc/netdata/.installer-cleanup-of-stock-configs-done" ]
then
declare -A configs_signatures=()
if [ -f "configs.signatures" ]
then
source "configs.signatures" || echo >&2 "ERROR: Failed to load configs.signatures !"
fi
fi
config_signature_matches() {
local md5="${1}" file="${2}"
progress "Backup existing netdata configuration before installing it"
if [ "${BASH_VERSINFO[0]}" -ge "4" ]
then
[ "${configs_signatures[${md5}]}" = "${file}" ] && return 0
return 1
fi
if [ -f "configs.signatures" ]
then
grep "\['${md5}'\]='${file}'" "configs.signatures" >/dev/null
return $?
fi
return 1
}
# backup user configurations
installer_backup_suffix="${PID}.${RANDOM}"
for x in $(find -L "${NETDATA_PREFIX}/etc/netdata" -name '*.conf' -type f)
do
if [ -f "${x}" ]
then
# make a backup of the configuration file
cp -p "${x}" "${x}.old"
if [ -z "${md5sum}" -o ! -x "${md5sum}" ]
then
declare -A configs_signatures=()
if [ -f "configs.signatures" ]
then
# we don't have md5sum - keep it
echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RET}is not known to distribution${TPUT_RESET}. Keeping it."
run cp -a "${x}" "${x}.installer_backup.${installer_backup_suffix}"
else
# find it relative filename
f="${x/*\/etc\/netdata\//}"
source "configs.signatures" || echo >&2 "ERROR: Failed to load configs.signatures !"
fi
fi
# find its checksum
md5="$(cat "${x}" | ${md5sum} | cut -d ' ' -f 1)"
config_signature_matches() {
local md5="${1}" file="${2}"
# copy the original
if [ -f "conf.d/${f}" ]
then
cp "conf.d/${f}" "${x}.orig"
fi
if config_signature_matches "${md5}" "${f}"
then
# it is a stock version - don't keep it
echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' is stock version."
else
# edited by user - keep it
echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RED} has been edited by user${TPUT_RESET}. Keeping it."
run cp -a "${x}" "${x}.installer_backup.${installer_backup_suffix}"
fi
if [ "${BASH_VERSINFO[0]}" -ge "4" ]
then
[ "${configs_signatures[${md5}]}" = "${file}" ] && return 0
return 1
fi
elif [ -f "${x}.installer_backup.${installer_backup_suffix}" ]
then
rm -f "${x}.installer_backup.${installer_backup_suffix}"
fi
done
if [ -f "configs.signatures" ]
then
grep "\['${md5}'\]='${file}'" "configs.signatures" >/dev/null
return $?
fi
return 1
}
# clean up stock config files from the user configuration directory
for x in $(find -L "${NETDATA_PREFIX}/etc/netdata" -type f)
do
if [ -f "${x}" ]
then
# find it relative filename
f="${x/${NETDATA_PREFIX}\/etc\/netdata\//}"
# find the stock filename
t="${f/.conf.installer_backup.*/.conf}"
t="${t/.conf.old/.conf}"
t="${t/.conf.orig/.conf}"
if [ -z "${md5sum}" -o ! -x "${md5sum}" ]
then
# we don't have md5sum - keep it
echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RET}is not known to distribution${TPUT_RESET}. Keeping it."
else
# find its checksum
md5="$(${md5sum} <"${x}" | cut -d ' ' -f 1)"
if config_signature_matches "${md5}" "${t}"
then
# it is a stock version - remove it
echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' is stock version of '${t}'."
run rm -f "${x}"
deleted_stock_configs=$(( deleted_stock_configs + 1 ))
else
# edited by user - keep it
echo >&2 "File '${TPUT_CYAN}${x}${TPUT_RESET}' ${TPUT_RED} does not match stock of '${t}'${TPUT_RESET}. Keeping it."
fi
fi
fi
done
touch "${NETDATA_PREFIX}/etc/netdata/.installer-cleanup-of-stock-configs-done"
fi
# -----------------------------------------------------------------------------
progress "Install netdata"
@ -623,19 +621,6 @@ progress "Install netdata"
run make install || exit 1
# -----------------------------------------------------------------------------
progress "Restore user edited netdata configuration files"
for x in $(find -L "${NETDATA_PREFIX}/etc/netdata/" -name '*.conf' -type f)
do
if [ -f "${x}.installer_backup.${installer_backup_suffix}" ]
then
run cp -a "${x}.installer_backup.${installer_backup_suffix}" "${x}" && \
run rm -f "${x}.installer_backup.${installer_backup_suffix}"
fi
done
# -----------------------------------------------------------------------------
progress "Fix generated files permissions"
@ -709,28 +694,30 @@ NETDATA_LIB_DIR="$( config_option "global" "lib directory" "${NETDATA_PREFIX}/va
NETDATA_CACHE_DIR="$( config_option "global" "cache directory" "${NETDATA_PREFIX}/var/cache/netdata" )"
NETDATA_WEB_DIR="$( config_option "global" "web files directory" "${NETDATA_PREFIX}/usr/share/netdata/web" )"
NETDATA_LOG_DIR="$( config_option "global" "log directory" "${NETDATA_PREFIX}/var/log/netdata" )"
NETDATA_CONF_DIR="$( config_option "global" "config directory" "${NETDATA_PREFIX}/etc/netdata" )"
NETDATA_USER_CONFIG_DIR="$( config_option "global" "config directory" "${NETDATA_PREFIX}/etc/netdata" )"
NETDATA_STOCK_CONFIG_DIR="$( config_option "global" "stock config directory" "${NETDATA_PREFIX}/usr/lib/netdata/conf.d" )"
NETDATA_RUN_DIR="${NETDATA_PREFIX}/var/run"
cat <<OPTIONSEOF
Permissions
- netdata user : ${NETDATA_USER}
- netdata group : ${NETDATA_GROUP}
- web files user : ${NETDATA_WEB_USER}
- web files group : ${NETDATA_WEB_GROUP}
- root user : ${ROOT_USER}
- netdata user : ${NETDATA_USER}
- netdata group : ${NETDATA_GROUP}
- web files user : ${NETDATA_WEB_USER}
- web files group : ${NETDATA_WEB_GROUP}
- root user : ${ROOT_USER}
Directories
- netdata conf dir : ${NETDATA_CONF_DIR}
- netdata log dir : ${NETDATA_LOG_DIR}
- netdata run dir : ${NETDATA_RUN_DIR}
- netdata lib dir : ${NETDATA_LIB_DIR}
- netdata web dir : ${NETDATA_WEB_DIR}
- netdata cache dir: ${NETDATA_CACHE_DIR}
- netdata user config dir : ${NETDATA_USER_CONFIG_DIR}
- netdata stock config dir : ${NETDATA_STOCK_CONFIG_DIR}
- netdata log dir : ${NETDATA_LOG_DIR}
- netdata run dir : ${NETDATA_RUN_DIR}
- netdata lib dir : ${NETDATA_LIB_DIR}
- netdata web dir : ${NETDATA_WEB_DIR}
- netdata cache dir : ${NETDATA_CACHE_DIR}
Other
- netdata port : ${NETDATA_PORT}
- netdata port : ${NETDATA_PORT}
OPTIONSEOF
@ -745,17 +732,35 @@ fi
# --- conf dir ----
for x in "python.d" "charts.d" "node.d"
for x in "python.d" "charts.d" "node.d" "health.d" "statsd.d"
do
if [ ! -d "${NETDATA_CONF_DIR}/${x}" ]
if [ ! -d "${NETDATA_USER_CONFIG_DIR}/${x}" ]
then
echo >&2 "Creating directory '${NETDATA_CONF_DIR}/${x}'"
run mkdir -p "${NETDATA_CONF_DIR}/${x}" || exit 1
echo >&2 "Creating directory '${NETDATA_USER_CONFIG_DIR}/${x}'"
run mkdir -p "${NETDATA_USER_CONFIG_DIR}/${x}" || exit 1
fi
done
run chown -R "${ROOT_USER}:${NETDATA_GROUP}" "${NETDATA_CONF_DIR}"
run find "${NETDATA_CONF_DIR}" -type f -exec chmod 0640 {} \;
run find "${NETDATA_CONF_DIR}" -type d -exec chmod 0755 {} \;
run chown -R "${ROOT_USER}:${NETDATA_GROUP}" "${NETDATA_USER_CONFIG_DIR}"
run find "${NETDATA_USER_CONFIG_DIR}" -type f -exec chmod 0640 {} \;
run find "${NETDATA_USER_CONFIG_DIR}" -type d -exec chmod 0755 {} \;
# --- stock conf dir ----
[ ! -d "${NETDATA_STOCK_CONFIG_DIR}" ] && mkdir -p "${NETDATA_STOCK_CONFIG_DIR}"
helplink="000.-.USE.THE.orig.LINK.TO.COPY.AND.EDIT.STOCK.CONFIG.FILES"
[ ${deleted_stock_configs} -eq 0 ] && helplink=""
for link in "orig" "${helplink}"
do
if [ ! -z "${link}" ]
then
[ -L "${NETDATA_USER_CONFIG_DIR}/${link}" ] && run rm -f "${NETDATA_USER_CONFIG_DIR}/${link}"
run ln -s "${NETDATA_STOCK_CONFIG_DIR}" "${NETDATA_USER_CONFIG_DIR}/${link}"
fi
done
run chown -R "${ROOT_USER}:${NETDATA_GROUP}" "${NETDATA_STOCK_CONFIG_DIR}"
run find "${NETDATA_STOCK_CONFIG_DIR}" -type f -exec chmod 0640 {} \;
run find "${NETDATA_STOCK_CONFIG_DIR}" -type d -exec chmod 0755 {} \;
# --- web dir ----

View file

@ -181,17 +181,18 @@ rm -rf "${RPM_BUILD_ROOT}"
%defattr(-,root,root)
%dir %{_sysconfdir}/%{name}
%dir %{_libdir}/%{name}
%config(noreplace) %{_sysconfdir}/%{name}/*.conf
%config(noreplace) %{_sysconfdir}/%{name}/charts.d/*.conf
%config(noreplace) %{_sysconfdir}/%{name}/health.d/*.conf
#%%config(noreplace) %{_sysconfdir}/%{name}/node.d/*.conf
%config(noreplace) %{_sysconfdir}/%{name}/python.d/*.conf
%config(noreplace) %{_sysconfdir}/%{name}/statsd.d/*.conf
%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
%config %{_libdir}/%{name}/*.conf
%config %{_libdir}/%{name}/charts.d/*.conf
%config %{_libdir}/%{name}/health.d/*.conf
%config %{_libdir}/%{name}/node.d/*.conf
%config %{_libdir}/%{name}/python.d/*.conf
%config %{_libdir}/%{name}/statsd.d/*.conf
%config %{_sysconfdir}/logrotate.d/%{name}
# To be eventually moved to %%_defaultdocdir
%{_sysconfdir}/%{name}/node.d/*.md
%{_libdir}/%{name}/node.d/*.md
%{_libexecdir}/%{name}
%{_sbindir}/%{name}
@ -215,9 +216,16 @@ rm -rf "${RPM_BUILD_ROOT}"
%dir %{_datadir}/%{name}
%dir %{_sysconfdir}/%{name}/health.d
%dir %{_libdir}/%{name}/health.d
%dir %{_sysconfdir}/%{name}/python.d
%dir %{_libdir}/%{name}/python.d
%dir %{_sysconfdir}/%{name}/charts.d
%dir %{_libdir}/%{name}/charts.d
%dir %{_sysconfdir}/%{name}/node.d
%dir %{_libdir}/%{name}/node.d
%dir %{_sysconfdir}/%{name}/statsd.d
%dir %{_libdir}/%{name}/statsd.d
%if %{with systemd}
%{_unitdir}/netdata.service

View file

@ -3,6 +3,19 @@
# SPDX-License-Identifier: GPL-3.0+
#
MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
CLEANFILES = \
alarm-notify.sh \
cgroup-name.sh \
charts.d.plugin \
fping.plugin \
node.d.plugin \
python.d.plugin \
tc-qos-helper.sh \
$(NULL)
include $(top_srcdir)/build/subst.inc
SUFFIXES = .in
dist_plugins_DATA = \
README.md \
@ -22,3 +35,13 @@ dist_plugins_SCRIPTS = \
tc-qos-helper.sh \
loopsleepms.sh.inc \
$(NULL)
dist_noinst_DATA = \
alarm-notify.sh.in \
cgroup-name.sh.in \
charts.d.plugin.in \
fping.plugin.in \
node.d.plugin.in \
python.d.plugin.in \
tc-qos-helper.sh.in \
$(NULL)

View file

@ -159,9 +159,10 @@ custom_sender() {
# -----------------------------------------------------------------------------
# defaults to allow running this script by hand
[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata"
[ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="$(dirname "${0}")/../../../../var/cache/netdata"
[ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io"
[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
[ -z "${NETDATA_CACHE_DIR}" ] && NETDATA_CACHE_DIR="@cachedir_POST@"
[ -z "${NETDATA_REGISTRY_URL}" ] && NETDATA_REGISTRY_URL="https://registry.my-netdata.io"
# -----------------------------------------------------------------------------
# parse command line parameters
@ -362,14 +363,20 @@ DEFAULT_RECIPIENT_IRC=
IRC_NETWORK=
declare -A role_recipients_irc=()
# load the user configuration
# this will overwrite the variables above
if [ -f "${NETDATA_CONFIG_DIR}/health_alarm_notify.conf" ]
then
source "${NETDATA_CONFIG_DIR}/health_alarm_notify.conf"
else
error "Cannot find file ${NETDATA_CONFIG_DIR}/health_alarm_notify.conf. Using internal defaults."
fi
# load the stock and user configuration files
# these will overwrite the variables above
for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/health_alarm_notify.conf" "${NETDATA_USER_CONFIG_DIR}/health_alarm_notify.conf"
do
if [ -f "${CONFIG}" ]
then
debug "Loading config file '${CONFIG}'..."
source "${CONFIG}"
[ $? -ne 0 ] && error "Failed to load config file '${CONFIG}'."
else
warning "Cannot find file '${CONFIG}'."
fi
done
# If we didn't autodetect the character set for e-mail and it wasn't
# set by the user, we need to set it to a reasonable default. UTF-8

View file

@ -51,9 +51,10 @@ debug() {
# -----------------------------------------------------------------------------
[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata"
[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
DOCKER_HOST="${DOCKER_HOST:=/var/run/docker.sock}"
CONFIG="${NETDATA_CONFIG_DIR}/cgroups-names.conf"
CGROUP="${1}"
NAME=
@ -64,16 +65,21 @@ if [ -z "${CGROUP}" ]
fatal "called without a cgroup name. Nothing to do."
fi
if [ -f "${CONFIG}" ]
for CONFIG in "${NETDATA_USER_CONFIG_DIR}/cgroups-names.conf" "${NETDATA_STOCK_CONFIG_DIR}/cgroups-names.conf"
do
if [ -f "${CONFIG}" ]
then
NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed "s/[[:space:]]\+/ /g" | cut -d ' ' -f 2)"
if [ -z "${NAME}" ]
then
info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
NAME="$(grep "^${CGROUP} " "${CONFIG}" | sed "s/[[:space:]]\+/ /g" | cut -d ' ' -f 2)"
if [ -z "${NAME}" ]
then
info "cannot find cgroup '${CGROUP}' in '${CONFIG}'."
else
break
fi
#else
# info "configuration file '${CONFIG}' is not available."
fi
#else
# info "configuration file '${CONFIG}' is not available."
fi
done
function docker_get_name_classic {
local id="${1}"

View file

@ -117,14 +117,15 @@ info "started from '$PROGRAM_FILE' with options: $*"
# netdata exposes a few environment variables for us
[ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")"
[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata"
[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
pluginsd="${NETDATA_PLUGINS_DIR}"
confd="${NETDATA_CONFIG_DIR}"
stockconfd="${NETDATA_STOCK_CONFIG_DIR}/${PROGRAM_NAME}"
userconfd="${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}"
olduserconfd="${NETDATA_USER_CONFIG_DIR}"
chartsd="$pluginsd/../charts.d"
myconfig="$confd/$PROGRAM_NAME.conf"
minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}"
update_every=${minimum_update_frequency} # this will be overwritten by the command line
@ -231,22 +232,33 @@ mysleep="sleep"
# if found and included, this file overwrites loopsleepms()
# and current_time_ms() with a high resolution timer function
# for precise looping.
. "$pluginsd/loopsleepms.sh.inc"
source "$pluginsd/loopsleepms.sh.inc"
[ $? -ne 0 ] && error "Failed to load '$pluginsd/loopsleepms.sh.inc'."
# -----------------------------------------------------------------------------
# load my configuration
if [ -f "$myconfig" ]
for myconfig in "${NETDATA_STOCK_CONFIG_DIR}/${PROGRAM_NAME}.conf" "${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf"
do
if [ -f "$myconfig" ]
then
. "$myconfig"
[ $? -ne 0 ] && fatal "cannot load $myconfig"
source "$myconfig"
if [ $? -ne 0 ]
then
error "Config file '$myconfig' loaded with errors."
else
info "Configuration file '$myconfig' loaded."
fi
else
warning "Configuration file '$myconfig' not found."
fi
done
# make sure time_divisor is right
time_divisor=$((time_divisor))
[ $time_divisor -lt 10 ] && time_divisor=10
[ $time_divisor -gt 100 ] && time_divisor=100
time_divisor=$((time_divisor))
[ $time_divisor -lt 10 ] && time_divisor=10
[ $time_divisor -gt 100 ] && time_divisor=100
else
info "configuration file '$myconfig' not found. Using defaults."
fi
# we check for the timeout command, after we load our
# configuration, so that the user may overwrite the
@ -412,7 +424,7 @@ all_enabled_charts() {
if [ ! "${enabled}" = "${required}" ]
then
info "is disabled. Add a line with $chart=$required in $myconfig to enable it (or remove the line that disables it)."
info "is disabled. Add a line with $chart=$required in '${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf' to enable it (or remove the line that disables it)."
else
debug "is enabled for auto-detection."
local charts="$charts $chart"
@ -448,18 +460,18 @@ all_enabled_charts() {
fi
# check its config
#if [ -f "$confd/$chart.conf" ]
#if [ -f "$userconfd/$chart.conf" ]
#then
# if [ ! -z "$( cat "$confd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ]
# if [ ! -z "$( cat "$userconfd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_undescore" )" ]
# then
# error "module's $chart config $confd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
# error "module's $chart config $userconfd/$chart.conf should only have lines starting with $chart$charts_undescore . Disabling it."
# continue
# fi
#fi
#if [ $dryrunner -eq 1 ]
# then
# "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$confd/$chart.conf" >/dev/null
# "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$userconfd/$chart.conf" >/dev/null
# if [ $? -ne 0 ]
# then
# error "module's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
@ -487,18 +499,35 @@ do
debug "loading module: '$chartsd/$chart.chart.sh'"
. "$chartsd/$chart.chart.sh"
source "$chartsd/$chart.chart.sh"
[ $? -ne 0 ] && warning "Module '$chartsd/$chart.chart.sh' loaded with errors."
if [ -f "$confd/$PROGRAM_NAME/$chart.conf" ]
# first load the stock config
if [ -f "$stockconfd/$chart.conf" ]
then
debug "loading module configuration: '$confd/$PROGRAM_NAME/$chart.conf'"
. "$confd/$PROGRAM_NAME/$chart.conf"
elif [ -f "$confd/$chart.conf" ]
then
debug "loading module configuration: '$confd/$chart.conf'"
. "$confd/$chart.conf"
debug "loading module configuration: '$stockconfd/$chart.conf'"
source "$stockconfd/$chart.conf"
[ $? -ne 0 ] && warning "Config file '$stockconfd/$chart.conf' loaded with errors."
else
warning "configuration file '$confd/$PROGRAM_NAME/$chart.conf' not found. Using defaults."
debug "not found module configuration: '$stockconfd/$chart.conf'"
fi
# then load the user config (it overwrites the stock)
if [ -f "$userconfd/$chart.conf" ]
then
debug "loading module configuration: '$userconfd/$chart.conf'"
source "$userconfd/$chart.conf"
[ $? -ne 0 ] && warning "Config file '$userconfd/$chart.conf' loaded with errors."
else
debug "not found module configuration: '$userconfd/$chart.conf'"
if [ -f "$olduserconfd/$chart.conf" ]
then
# support for very old netdata that had the charts.d module configs in /etc/netdata
info "loading module configuration from obsolete location: '$olduserconfd/$chart.conf'"
source "$olduserconfd/$chart.conf"
[ $? -ne 0 ] && warning "Config file '$olduserconfd/$chart.conf' loaded with errors."
fi
fi
eval "dt=\$$chart$suffix_update_every"

View file

@ -130,7 +130,8 @@ update_every="${1-1}"
# the netdata configuration directory
# passed by netdata as an environment variable
[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata"
[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
# -----------------------------------------------------------------------------
# configuration options
@ -153,23 +154,33 @@ ping_every="$((update_every * 1000 / 5))"
fping_opts="-R -b 56 -i 1 -r 0 -t 5000"
# -----------------------------------------------------------------------------
# load the configuration file
# load the configuration files
if [ ! -f "${NETDATA_CONFIG_DIR}/${plugin}.conf" ]
then
fatal "configuration file '${NETDATA_CONFIG_DIR}/${plugin}.conf' not found - nothing to do."
fi
source "${NETDATA_CONFIG_DIR}/${plugin}.conf"
for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/${plugin}.conf" "${NETDATA_USER_CONFIG_DIR}/${plugin}.conf"
do
if [ -f "${CONFIG}" ]
then
info "Loading config file '${CONFIG}'..."
source "${CONFIG}"
[ $? -ne 0 ] && error "Failed to load config file '${CONFIG}'."
else
warning "Cannot find file '${CONFIG}'."
fi
done
if [ -z "${hosts}" ]
then
fatal "no hosts configured in '${NETDATA_CONFIG_DIR}/${plugin}.conf' - nothing to do."
fatal "no hosts configured - nothing to do."
fi
if [ -z "${fping}" -o ! -x "${fping}" ]
if [ -z "${fping}" ]
then
fatal "command '${fping}' is not found or is not executable - cannot proceed."
fatal "fping command is not found. Please set its full path in '${NETDATA_USER_CONFIG_DIR}/${plugin}.conf'"
fi
if [ ! -x "${fping}" ]
then
fatal "fping command '${fping}' is not executable - cannot proceed."
fi
if [ ${ping_every} -lt 20 ]
@ -186,4 +197,4 @@ info "starting fping: ${fping} ${options[*]}"
exec "${fping}" "${options[@]}"
# if we cannot execute fping, stop
fatal "command '${fping} ${options[@]}' failed to be executed."
fatal "command '${fping} ${options[*]}' failed to be executed (returned code $?)."

View file

@ -21,7 +21,8 @@
// get NETDATA environment variables
var NETDATA_PLUGINS_DIR = process.env.NETDATA_PLUGINS_DIR || __dirname;
var NETDATA_CONFIG_DIR = process.env.NETDATA_CONFIG_DIR || __dirname + '/../../../../etc/netdata';
var NETDATA_USER_CONFIG_DIR = process.env.NETDATA_USER_CONFIG_DIR || '@configdir_POST@';
var NETDATA_STOCK_CONFIG_DIR = process.env.NETDATA_STOCK_CONFIG_DIR || '@libconfigdir_POST@';
var NETDATA_UPDATE_EVERY = process.env.NETDATA_UPDATE_EVERY || 1;
var NODE_D_DIR = NETDATA_PLUGINS_DIR + '/../node.d';
@ -45,20 +46,42 @@ var netdata = require('netdata');
// --------------------------------------------------------------------------------------------------------------------
// configuration
function pluginConfig(filename) {
var f = path.basename(filename);
function netdata_read_json_config_file(module_filename) {
var f = path.basename(module_filename);
var ufilename, sfilename;
// node.d.plugin configuration
var m = f.match('.plugin' + '$');
if(m !== null)
return netdata.options.paths.config + '/' + f.substring(0, m.index) + '.conf';
if(m !== null) {
ufilename = netdata.options.paths.config + '/' + f.substring(0, m.index) + '.conf';
sfilename = netdata.options.paths.stock_config + '/' + f.substring(0, m.index) + '.conf';
}
// node.d modules configuration
m = f.match('.node.js' + '$');
if(m !== null)
return netdata.options.paths.config + '/node.d/' + f.substring(0, m.index) + '.conf';
if(m !== null) {
ufilename = netdata.options.paths.config + '/node.d/' + f.substring(0, m.index) + '.conf';
sfilename = netdata.options.paths.stock_config + '/node.d/' + f.substring(0, m.index) + '.conf';
}
return netdata.options.paths.config + '/node.d/' + f + '.conf';
try {
netdata.debug('loading module\'s ' + module_filename + ' user-config ' + ufilename);
return JSON.parse(fs.readFileSync(ufilename, 'utf8'));
}
catch(e) {
netdata.error('Cannot read user-configuration file ' + ufilename + ': ' + e.message + '.');
dumpError(e);
}
try {
netdata.debug('loading module\'s ' + module_filename + ' stock-config ' + sfilename);
return JSON.parse(fs.readFileSync(sfilename, 'utf8'));
}
catch(e) {
netdata.error('Cannot read stock-configuration file ' + sfilename + ': ' + e.message + ', using internal defaults.');
dumpError(e);
}
return {};
}
// internal defaults
@ -69,35 +92,31 @@ extend(true, netdata.options, {
paths: {
plugins: NETDATA_PLUGINS_DIR,
config: NETDATA_CONFIG_DIR,
modules: [],
config: NETDATA_USER_CONFIG_DIR,
stock_config: NETDATA_STOCK_CONFIG_DIR,
modules: []
},
modules_enable_autodetect: true,
modules_enable_all: true,
modules: {},
modules: {}
});
netdata.options.config_filename = pluginConfig(__filename);
// load configuration file
try {
netdata.options_loaded = JSON.parse(fs.readFileSync(netdata.options.config_filename, 'utf8'));
extend(true, netdata.options, netdata.options_loaded);
netdata.options_loaded = netdata_read_json_config_file(__filename);
extend(true, netdata.options, netdata.options_loaded);
if(!netdata.options.paths.plugins)
netdata.options.paths.plugins = NETDATA_PLUGINS_DIR;
if(!netdata.options.paths.plugins)
netdata.options.paths.plugins = NETDATA_PLUGINS_DIR;
if(!netdata.options.paths.config)
netdata.options.paths.config = NETDATA_CONFIG_DIR;
if(!netdata.options.paths.config)
netdata.options.paths.config = NETDATA_USER_CONFIG_DIR;
// console.error('merged netdata object:');
// console.error(util.inspect(netdata, {depth: 10}));
}
catch(e) {
netdata.error('Cannot read configuration file ' + netdata.options.config_filename + ': ' + e.message + ', using internal defaults.');
netdata.options_loaded = undefined;
dumpError(e);
}
if(!netdata.options.paths.stock_config)
netdata.options.paths.stock_config = NETDATA_STOCK_CONFIG_DIR;
// console.error('merged netdata object:');
// console.error(util.inspect(netdata, {depth: 10}));
// apply module paths to node.js process
@ -206,9 +225,6 @@ function findModules() {
netdata.options.modules[n].filename = NODE_D_DIR + '/' + files[len];
netdata.options.modules[n].loaded = false;
if(typeof(netdata.options.modules[n].config_filename) !== 'string')
netdata.options.modules[n].config_filename = pluginConfig(files[len]);
// load the module
try {
netdata.debug('loading module ' + netdata.options.modules[n].filename);
@ -228,16 +244,9 @@ function findModules() {
enable_autodetect: netdata.options.modules_enable_autodetect,
update_every: netdata.options.update_every
};
try {
netdata.debug('loading module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
var c2 = JSON.parse(fs.readFileSync(netdata.options.modules[n].config_filename, 'utf8'));
extend(true, c, c2);
netdata.debug('loaded module\'s ' + netdata.options.modules[n].name + ' config ' + netdata.options.modules[n].config_filename);
}
catch(e) {
netdata.error('Cannot load module\'s ' + netdata.options.modules[n].name + ' config from ' + netdata.options.modules[n].config_filename + ' exception: ' + e + ', using internal defaults.');
dumpError(e);
}
var c2 = netdata_read_json_config_file(files[len]);
extend(true, c, c2);
// call module auto-detection / configuration
try {

View file

@ -21,10 +21,20 @@ GC_RUN = True
GC_COLLECT_EVERY = 300
PY_VERSION = version_info[:2]
PLUGIN_CONFIG_DIR = os.getenv('NETDATA_CONFIG_DIR', os.path.dirname(__file__) + '/../../../../etc/netdata') + '/'
CHARTS_PY_DIR = os.path.abspath(os.getenv('NETDATA_PLUGINS_DIR', os.path.dirname(__file__)) + '/../python.d') + '/'
CHARTS_PY_CONFIG_DIR = PLUGIN_CONFIG_DIR + 'python.d/'
PYTHON_MODULES_DIR = CHARTS_PY_DIR + 'python_modules'
USER_CONFIG_DIR = os.getenv('NETDATA_USER_CONFIG_DIR', '@configdir_POST@')
STOCK_CONFIG_DIR = os.getenv('NETDATA_STOCK_CONFIG_DIR', '@libconfigdir_POST@')
PLUGINS_USER_CONFIG_DIR = os.path.join(USER_CONFIG_DIR, 'python.d')
PLUGINS_STOCK_CONFIG_DIR = os.path.join(STOCK_CONFIG_DIR, 'python.d')
PLUGINS_DIR = os.path.abspath(os.getenv(
'NETDATA_PLUGINS_DIR',
os.path.dirname(__file__)) + '/../python.d')
PYTHON_MODULES_DIR = os.path.join(PLUGINS_DIR, 'python_modules')
sys.path.append(PYTHON_MODULES_DIR)
@ -53,7 +63,7 @@ def module_ok(m):
return m.endswith(MODULE_EXTENSION) and m[:-len(MODULE_EXTENSION)] not in OBSOLETE_MODULES
ALL_MODULES = [m for m in sorted(os.listdir(CHARTS_PY_DIR)) if module_ok(m)]
ALL_MODULES = [m for m in sorted(os.listdir(PLUGINS_DIR)) if module_ok(m)]
def parse_cmd():
@ -69,10 +79,9 @@ def multi_job_check(config):
class RawModule:
def __init__(self, name, path, conf_path, explicitly_enabled=True):
def __init__(self, name, path, explicitly_enabled=True):
self.name = name
self.path = path
self.conf_path = conf_path
self.explicitly_enabled = explicitly_enabled
@ -202,9 +211,20 @@ class Plugin(object):
self.modules = OrderedDict()
self.sleep_time = 1
self.runs_counter = 0
self.config, error = self.loader.load_config_from_file(PLUGIN_CONFIG_DIR + 'python.d.conf')
user_config = os.path.join(USER_CONFIG_DIR, 'python.d.conf')
stock_config = os.path.join(STOCK_CONFIG_DIR, 'python.d.conf')
Logger.debug("loading '{0}'".format(user_config))
self.config, error = self.loader.load_config_from_file(user_config)
if error:
Logger.error('"python.d.conf" configuration file not found. Using defaults.')
Logger.error("cannot load '{0}': {1}. Will try stock version.".format(user_config, error))
Logger.debug("loading '{0}'".format(stock_config))
self.config, error = self.loader.load_config_from_file(stock_config)
if error:
Logger.error("cannot load '{0}': {1}".format(stock_config, error))
self.do_gc = self.config.get("gc_run", GC_RUN)
self.gc_interval = self.config.get("gc_interval", GC_COLLECT_EVERY)
@ -233,8 +253,7 @@ class Plugin(object):
def enabled_modules(self):
for mod in MODULES_TO_RUN:
mod_name = mod[:-len(MODULE_EXTENSION)]
mod_path = CHARTS_PY_DIR + mod
conf_path = ''.join([CHARTS_PY_CONFIG_DIR, mod_name, '.conf'])
mod_path = os.path.join(PLUGINS_DIR, mod)
if any(
[
self.config.get('default_run', True) and self.config.get(mod_name, True),
@ -244,7 +263,6 @@ class Plugin(object):
yield RawModule(
name=mod_name,
path=mod_path,
conf_path=conf_path,
explicitly_enabled=self.config.get(mod_name),
)
@ -261,12 +279,18 @@ class Plugin(object):
continue
# Load module config from file ------------------------------------------------------
loaded_config, error = self.loader.load_config_from_file(mod.conf_path)
log = Logger.error if error else Logger.debug
log("module load config: '{module_name}' => [{status}]".format(status='FAILED' if error else 'OK',
module_name=mod.name))
user_config = os.path.join(PLUGINS_USER_CONFIG_DIR, mod.name + '.conf')
stock_config = os.path.join(PLUGINS_STOCK_CONFIG_DIR, mod.name + '.conf')
Logger.debug("loading '{0}'".format(user_config))
loaded_config, error = self.loader.load_config_from_file(user_config)
if error:
Logger.error('load config error : {0}'.format(error))
Logger.error("cannot load '{0}' : {1}. Will try stock version.".format(user_config, error))
Logger.debug("loading '{0}'".format(stock_config))
loaded_config, error = self.loader.load_config_from_file(stock_config)
if error:
Logger.error("cannot load '{0}': {1}".format(stock_config, error))
# Skip disabled modules
if getattr(loaded_module, 'disabled_by_default', False) and not mod.explicitly_enabled:

View file

@ -107,10 +107,10 @@ fi
# -----------------------------------------------------------------------------
[ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")"
[ -z "${NETDATA_CONFIG_DIR}" ] && NETDATA_CONFIG_DIR="$(dirname "${0}")/../../../../etc/netdata"
[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
plugins_dir="${NETDATA_PLUGINS_DIR}"
config_dir="${NETDATA_CONFIG_DIR}"
tc="$(which tc 2>/dev/null || command -v tc 2>/dev/null)"
@ -139,10 +139,17 @@ update_every=$((t))
# -----------------------------------------------------------------------------
# allow the user to override our defaults
if [ -f "${config_dir}/tc-qos-helper.conf" ]
then
source "${config_dir}/tc-qos-helper.conf"
fi
for CONFIG in "${NETDATA_STOCK_CONFIG_DIR}/tc-qos-helper.conf" "${NETDATA_USER_CONFIG_DIR}/tc-qos-helper.conf"
do
if [ -f "${CONFIG}" ]
then
info "Loading config file '${CONFIG}'..."
source "${CONFIG}"
[ $? -ne 0 ] && error "Failed to load config file '${CONFIG}'."
else
warning "Cannot find file '${CONFIG}'."
fi
done
case "${tc_show}" in
qdisc|class)

View file

@ -8,6 +8,7 @@ AM_CPPFLAGS = \
-DVARLIB_DIR="\"$(varlibdir)\"" \
-DCACHE_DIR="\"$(cachedir)\"" \
-DCONFIG_DIR="\"$(configdir)\"" \
-DLIBCONFIG_DIR="\"$(libconfigdir)\"" \
-DLOG_DIR="\"$(logdir)\"" \
-DPLUGINS_DIR="\"$(pluginsdir)\"" \
-DRUN_DIR="\"$(localstatedir)/run/netdata\"" \

View file

@ -100,8 +100,9 @@ static int
include_exited_childs = 1;
// will be changed to getenv(NETDATA_CONFIG_DIR) if it exists
static char *config_dir = CONFIG_DIR;
// will be changed to getenv(NETDATA_USER_CONFIG_DIR) if it exists
static char *user_config_dir = CONFIG_DIR;
static char *stock_config_dir = LIBCONFIG_DIR;
// ----------------------------------------------------------------------------
// internal flags
@ -631,11 +632,11 @@ static struct target *get_apps_groups_target(const char *id, struct target *targ
}
// read the apps_groups.conf file
static int read_apps_groups_conf(const char *file)
static int read_apps_groups_conf(const char *path, const char *file)
{
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", config_dir, file);
snprintfz(filename, FILENAME_MAX, "%s/apps_%s.conf", path, file);
debug_log("process groups file: '%s'", filename);
@ -3569,10 +3570,18 @@ static void parse_args(int argc, char **argv)
if(freq > 0) update_every = freq;
if(read_apps_groups_conf("groups")) {
error("Cannot read process groups '%s/apps_groups.conf'. There are no internal defaults. Failing.", config_dir);
exit(1);
if(read_apps_groups_conf(user_config_dir, "groups")) {
error("Cannot read process groups configuration file '%s/apps_groups.conf'. Will try '%s/apps_groups.conf'", user_config_dir, stock_config_dir);
if(read_apps_groups_conf(stock_config_dir, "groups")) {
error("Cannot read process groups '%s/apps_groups.conf'. There are no internal defaults. Failing.", stock_config_dir);
exit(1);
}
else
info("Loaded config file '%s/apps_groups.conf'", stock_config_dir);
}
else
info("Loaded config file '%s/apps_groups.conf'", user_config_dir);
}
static int am_i_running_as_root() {
@ -3658,12 +3667,19 @@ int main(int argc, char **argv) {
netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
if(verify_netdata_host_prefix() == -1) exit(1);
config_dir = getenv("NETDATA_CONFIG_DIR");
if(config_dir == NULL) {
user_config_dir = getenv("NETDATA_USER_CONFIG_DIR");
if(user_config_dir == NULL) {
// info("NETDATA_CONFIG_DIR is not passed from netdata");
config_dir = CONFIG_DIR;
user_config_dir = CONFIG_DIR;
}
// else info("Found NETDATA_CONFIG_DIR='%s'", config_dir);
// else info("Found NETDATA_USER_CONFIG_DIR='%s'", user_config_dir);
stock_config_dir = getenv("NETDATA_STOCK_CONFIG_DIR");
if(stock_config_dir == NULL) {
// info("NETDATA_CONFIG_DIR is not passed from netdata");
stock_config_dir = LIBCONFIG_DIR;
}
// else info("Found NETDATA_USER_CONFIG_DIR='%s'", user_config_dir);
#ifdef NETDATA_INTERNAL_CHECKS
if(debug_flags != 0) {

View file

@ -10,16 +10,17 @@
# define MADV_DONTFORK INHERIT_NONE
#endif /* __FreeBSD__ || __APPLE__*/
char *netdata_configured_hostname = NULL;
char *netdata_configured_config_dir = NULL;
char *netdata_configured_log_dir = NULL;
char *netdata_configured_plugins_dir = NULL;
char *netdata_configured_web_dir = NULL;
char *netdata_configured_cache_dir = NULL;
char *netdata_configured_varlib_dir = NULL;
char *netdata_configured_home_dir = NULL;
char *netdata_configured_host_prefix = NULL;
char *netdata_configured_timezone = NULL;
char *netdata_configured_hostname = NULL;
char *netdata_configured_user_config_dir = CONFIG_DIR;
char *netdata_configured_stock_config_dir = LIBCONFIG_DIR;
char *netdata_configured_log_dir = LOG_DIR;
char *netdata_configured_plugins_dir = NULL;
char *netdata_configured_web_dir = WEB_DIR;
char *netdata_configured_cache_dir = CACHE_DIR;
char *netdata_configured_varlib_dir = VARLIB_DIR;
char *netdata_configured_home_dir = CACHE_DIR;
char *netdata_configured_host_prefix = NULL;
char *netdata_configured_timezone = NULL;
struct rlimit rlimit_nofile = { .rlim_cur = 1024, .rlim_max = 1024 };
int enable_ksm = 1;
@ -1431,3 +1432,191 @@ failed:
netdata_configured_host_prefix = "";
return -1;
}
char *strdupz_path_subpath(const char *path, const char *subpath) {
if(unlikely(!path || !*path)) path = ".";
if(unlikely(!subpath)) subpath = "";
// skip trailing slashes in path
size_t len = strlen(path);
while(len > 0 && path[len - 1] == '/') len--;
// skip leading slashes in subpath
while(subpath && subpath[0] == '/') subpath++;
// if the last character in path is / and (there is a subpath or path is now empty)
// keep the trailing slash in path and remove the additional slash
char *slash = "/";
if(path[len] == '/' && (*subpath || len == 0)) {
slash = "";
len++;
}
else if(!*subpath) {
// there is no subpath
// no need for trailing slash
slash = "";
}
char buffer[FILENAME_MAX + 1];
snprintfz(buffer, FILENAME_MAX, "%.*s%s%s", (int)len, path, slash, (subpath)?subpath:"");
return strdupz(buffer);
}
int path_is_dir(const char *path, const char *subpath) {
char *s = strdupz_path_subpath(path, subpath);
size_t max_links = 100;
int is_dir = 0;
struct stat statbuf;
while(max_links-- && stat(s, &statbuf) == 0) {
if((statbuf.st_mode & S_IFMT) == S_IFDIR) {
is_dir = 1;
break;
}
else if((statbuf.st_mode & S_IFMT) == S_IFLNK) {
char buffer[FILENAME_MAX + 1];
ssize_t l = readlink(s, buffer, FILENAME_MAX);
if(l > 0) {
buffer[l] = '\0';
freez(s);
s = strdupz(buffer);
continue;
}
else {
is_dir = 0;
break;
}
}
else {
is_dir = 0;
break;
}
}
freez(s);
return is_dir;
}
int path_is_file(const char *path, const char *subpath) {
char *s = strdupz_path_subpath(path, subpath);
size_t max_links = 100;
int is_file = 0;
struct stat statbuf;
while(max_links-- && stat(s, &statbuf) == 0) {
if((statbuf.st_mode & S_IFMT) == S_IFREG) {
is_file = 1;
break;
}
else if((statbuf.st_mode & S_IFMT) == S_IFLNK) {
char buffer[FILENAME_MAX + 1];
ssize_t l = readlink(s, buffer, FILENAME_MAX);
if(l > 0) {
buffer[l] = '\0';
freez(s);
s = strdupz(buffer);
continue;
}
else {
is_file = 0;
break;
}
}
else {
is_file = 0;
break;
}
}
freez(s);
return is_file;
}
void recursive_config_double_dir_load(const char *user_path, const char *stock_path, const char *subpath, int (*callback)(const char *filename, void *data), void *data) {
char *udir = strdupz_path_subpath(user_path, subpath);
char *sdir = strdupz_path_subpath(stock_path, subpath);
debug(D_HEALTH, "Configuration traversing user-config directory '%s', stock config directory '%s'", udir, sdir);
DIR *dir = opendir(udir);
if (!dir) {
error("Configuration cannot open user-config directory '%s'.", udir);
}
else {
struct dirent *de = NULL;
while((de = readdir(dir))) {
if(de->d_type == DT_DIR
&& ( (de->d_name[0] == '.' && de->d_name[1] == '\0')
|| (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
)) {
debug(D_HEALTH, "Configuration ignoring user-config directory '%s/%s'", udir, de->d_name);
continue;
}
if(path_is_dir(udir, de->d_name)) {
recursive_config_double_dir_load(udir, sdir, de->d_name, callback, data);
continue;
}
size_t len = strlen(de->d_name);
if(path_is_file(udir, de->d_name) &&
len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
char *filename = strdupz_path_subpath(udir, de->d_name);
callback(filename, data);
freez(filename);
}
else
debug(D_HEALTH, "Health ignoring user config file '%s/%s'", udir, de->d_name);
}
closedir(dir);
}
debug(D_HEALTH, "Health configuration traversing stock config directory '%s', user config directory '%s'", sdir, udir);
dir = opendir(sdir);
if (!dir) {
error("Health configuration cannot open stock config directory '%s'.", sdir);
}
else {
struct dirent *de = NULL;
while((de = readdir(dir))) {
if(de->d_type == DT_DIR
&& ( (de->d_name[0] == '.' && de->d_name[1] == '\0')
|| (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
)) {
debug(D_HEALTH, "Health ignoring stock config directory '%s/%s'", sdir, de->d_name);
continue;
}
if(path_is_dir(sdir, de->d_name)) {
// we recurse in stock subdirectory, only when there is no corresponding
// user subdirectory - to avoid reading the files twice
if(!path_is_dir(udir, de->d_name))
recursive_config_double_dir_load(udir, sdir, de->d_name, callback, data);
continue;
}
size_t len = strlen(de->d_name);
if(path_is_file(sdir, de->d_name) && !path_is_file(udir, de->d_name) &&
len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
char *filename = strdupz_path_subpath(sdir, de->d_name);
callback(filename, data);
freez(filename);
}
else
debug(D_HEALTH, "Health ignoring stock config file '%s/%s'", sdir, de->d_name);
}
closedir(dir);
}
freez(udir);
freez(sdir);
}

View file

@ -42,6 +42,7 @@
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <libgen.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@ -349,7 +350,8 @@ typedef enum rrdcalc_status {
#include "web_api_v1.h"
extern char *netdata_configured_hostname;
extern char *netdata_configured_config_dir;
extern char *netdata_configured_user_config_dir;
extern char *netdata_configured_stock_config_dir;
extern char *netdata_configured_log_dir;
extern char *netdata_configured_plugins_dir_base;
extern char *netdata_configured_plugins_dir;
@ -426,6 +428,12 @@ extern const char *os_type;
extern const char *program_version;
extern char *strdupz_path_subpath(const char *path, const char *subpath);
extern int path_is_dir(const char *path, const char *subpath);
extern int path_is_file(const char *path, const char *subpath);
extern void recursive_config_double_dir_load(const char *user_path, const char *stock_path, const char *subpath
, int (*callback)(const char *filename, void *data), void *data);
/* fix for alpine linux */
#ifndef RUSAGE_THREAD
#ifdef RUSAGE_CHILDREN

View file

@ -8,12 +8,18 @@ unsigned int default_health_enabled = 1;
// ----------------------------------------------------------------------------
// health initialization
inline char *health_config_dir(void) {
inline char *health_user_config_dir(void) {
char buffer[FILENAME_MAX + 1];
snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_config_dir);
snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_user_config_dir);
return config_get(CONFIG_SECTION_HEALTH, "health configuration directory", buffer);
}
inline char *health_stock_config_dir(void) {
char buffer[FILENAME_MAX + 1];
snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_stock_config_dir);
return config_get(CONFIG_SECTION_HEALTH, "stock health configuration directory", buffer);
}
void health_init(void) {
debug(D_HEALTH, "Health configuration initializing");
@ -30,7 +36,8 @@ void health_reload_host(RRDHOST *host) {
if(unlikely(!host->health_enabled))
return;
char *path = health_config_dir();
char *user_path = health_user_config_dir();
char *stock_path = health_stock_config_dir();
// free all running alarms
rrdhost_wrlock(host);
@ -61,7 +68,7 @@ void health_reload_host(RRDHOST *host) {
// load the new alarms
rrdhost_wrlock(host);
health_readdir(host, path);
health_readdir(host, user_path, stock_path, NULL);
// link the loaded alarms to their charts
rrdset_foreach_write(st, host) {

View file

@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "common.h"
#ifndef NETDATA_HEALTH_H
#define NETDATA_HEALTH_H 1
@ -415,8 +417,9 @@ extern void health_alarm_log(
uint32_t flags
);
extern void health_readdir(RRDHOST *host, const char *path);
extern char *health_config_dir(void);
extern void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath);
extern char *health_user_config_dir(void);
extern char *health_stock_config_dir(void);
extern void health_reload_host(RRDHOST *host);
extern void health_alarm_log_free(RRDHOST *host);

View file

@ -178,7 +178,7 @@ static inline int health_parse_duration(char *string, int *result) {
}
static inline int health_parse_delay(
size_t line, const char *path, const char *file, char *string,
size_t line, const char *filename, char *string,
int *delay_up_duration,
int *delay_down_duration,
int *delay_max_duration,
@ -204,36 +204,36 @@ static inline int health_parse_delay(
if(!strcasecmp(key, "up")) {
if (!health_parse_duration(value, delay_up_duration)) {
error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
line, path, file, value, key);
error("Health configuration at line %zu of file '%s': invalid value '%s' for '%s' keyword",
line, filename, value, key);
}
else given_up = 1;
}
else if(!strcasecmp(key, "down")) {
if (!health_parse_duration(value, delay_down_duration)) {
error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
line, path, file, value, key);
error("Health configuration at line %zu of file '%s': invalid value '%s' for '%s' keyword",
line, filename, value, key);
}
else given_down = 1;
}
else if(!strcasecmp(key, "multiplier")) {
*delay_multiplier = strtof(value, NULL);
if(isnan(*delay_multiplier) || isinf(*delay_multiplier) || islessequal(*delay_multiplier, 0)) {
error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
line, path, file, value, key);
error("Health configuration at line %zu of file '%s': invalid value '%s' for '%s' keyword",
line, filename, value, key);
}
else given_multiplier = 1;
}
else if(!strcasecmp(key, "max")) {
if (!health_parse_duration(value, delay_max_duration)) {
error("Health configuration at line %zu of file '%s/%s': invalid value '%s' for '%s' keyword",
line, path, file, value, key);
error("Health configuration at line %zu of file '%s': invalid value '%s' for '%s' keyword",
line, filename, value, key);
}
else given_max = 1;
}
else {
error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
line, path, file, key);
error("Health configuration at line %zu of file '%s': unknown keyword '%s'",
line, filename, key);
}
}
@ -287,11 +287,11 @@ static inline uint32_t health_parse_options(const char *s) {
}
static inline int health_parse_db_lookup(
size_t line, const char *path, const char *file, char *string,
size_t line, const char *filename, char *string,
int *group_method, int *after, int *before, int *every,
uint32_t *options, char **dimensions
) {
debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s/%s: %s", line, path, file, string);
debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s: %s", line, filename, string);
if(*dimensions) freez(*dimensions);
*dimensions = NULL;
@ -307,14 +307,14 @@ static inline int health_parse_db_lookup(
while(*s && !isspace(*s)) s++;
while(*s && isspace(*s)) *s++ = '\0';
if(!*s) {
error("Health configuration invalid chart calculation at line %zu of file '%s/%s': expected group method followed by the 'after' time, but got '%s'",
line, path, file, key);
error("Health configuration invalid chart calculation at line %zu of file '%s': expected group method followed by the 'after' time, but got '%s'",
line, filename, key);
return 0;
}
if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
error("Health configuration at line %zu of file '%s/%s': invalid group method '%s'",
line, path, file, key);
error("Health configuration at line %zu of file '%s': invalid group method '%s'",
line, filename, key);
return 0;
}
@ -324,8 +324,8 @@ static inline int health_parse_db_lookup(
while(*s && isspace(*s)) *s++ = '\0';
if(!health_parse_duration(key, after)) {
error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' after group method",
line, path, file, key);
error("Health configuration at line %zu of file '%s': invalid duration '%s' after group method",
line, filename, key);
return 0;
}
@ -345,8 +345,8 @@ static inline int health_parse_db_lookup(
while(*s && isspace(*s)) *s++ = '\0';
if (!health_parse_duration(value, before)) {
error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
line, path, file, value, key);
error("Health configuration at line %zu of file '%s': invalid duration '%s' for '%s' keyword",
line, filename, value, key);
}
}
else if(!strcasecmp(key, HEALTH_EVERY_KEY)) {
@ -355,8 +355,8 @@ static inline int health_parse_db_lookup(
while(*s && isspace(*s)) *s++ = '\0';
if (!health_parse_duration(value, every)) {
error("Health configuration at line %zu of file '%s/%s': invalid duration '%s' for '%s' keyword",
line, path, file, value, key);
error("Health configuration at line %zu of file '%s': invalid duration '%s' for '%s' keyword",
line, filename, value, key);
}
}
else if(!strcasecmp(key, "absolute") || !strcasecmp(key, "abs") || !strcasecmp(key, "absolute_sum")) {
@ -386,17 +386,17 @@ static inline int health_parse_db_lookup(
break;
}
else {
error("Health configuration at line %zu of file '%s/%s': unknown keyword '%s'",
line, path, file, key);
error("Health configuration at line %zu of file '%s': unknown keyword '%s'",
line, filename, key);
}
}
return 1;
}
static inline char *health_source_file(size_t line, const char *path, const char *filename) {
static inline char *health_source_file(size_t line, const char *file) {
char buffer[FILENAME_MAX + 1];
snprintfz(buffer, FILENAME_MAX, "%zu@%s/%s", line, path, filename);
snprintfz(buffer, FILENAME_MAX, "%zu@%s", line, file);
return strdupz(buffer);
}
@ -407,8 +407,10 @@ static inline void strip_quotes(char *s) {
}
}
int health_readfile(RRDHOST *host, const char *path, const char *filename) {
debug(D_HEALTH, "Health configuration reading file '%s/%s'", path, filename);
static int health_readfile(const char *filename, void *data) {
RRDHOST *host = (RRDHOST *)data;
debug(D_HEALTH, "Health configuration reading file '%s'", filename);
static uint32_t
hash_alarm = 0,
@ -455,10 +457,9 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
hash_options = simple_uhash(HEALTH_OPTIONS_KEY);
}
snprintfz(buffer, HEALTH_CONF_MAX_LINE, "%s/%s", path, filename);
FILE *fp = fopen(buffer, "r");
FILE *fp = fopen(filename, "r");
if(!fp) {
error("Health configuration cannot read file '%s'.", buffer);
error("Health configuration cannot read file '%s'.", filename);
return 0;
}
@ -481,7 +482,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
if(append < HEALTH_CONF_MAX_LINE)
continue;
else {
error("Health configuration has too long muli-line at line %zu of file '%s/%s'.", line, path, filename);
error("Health configuration has too long muli-line at line %zu of file '%s'.", line, filename);
}
}
append = 0;
@ -489,7 +490,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
char *key = s;
while(*s && *s != ':') s++;
if(!*s) {
error("Health configuration has invalid line %zu of file '%s/%s'. It does not contain a ':'. Ignoring it.", line, path, filename);
error("Health configuration has invalid line %zu of file '%s'. It does not contain a ':'. Ignoring it.", line, filename);
continue;
}
*s = '\0';
@ -500,12 +501,12 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
value = trim_all(value);
if(!key) {
error("Health configuration has invalid line %zu of file '%s/%s'. Keyword is empty. Ignoring it.", line, path, filename);
error("Health configuration has invalid line %zu of file '%s'. Keyword is empty. Ignoring it.", line, filename);
continue;
}
if(!value) {
error("Health configuration has invalid line %zu of file '%s/%s'. value is empty. Ignoring it.", line, path, filename);
error("Health configuration has invalid line %zu of file '%s'. value is empty. Ignoring it.", line, filename);
continue;
}
@ -526,7 +527,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
rc->next_event_id = 1;
rc->name = strdupz(value);
rc->hash = simple_hash(rc->name);
rc->source = health_source_file(line, path, filename);
rc->source = health_source_file(line, filename);
rc->green = NAN;
rc->red = NAN;
rc->value = NAN;
@ -552,7 +553,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
rt = callocz(1, sizeof(RRDCALCTEMPLATE));
rt->name = strdupz(value);
rt->hash_name = simple_hash(rt->name);
rt->source = health_source_file(line, path, filename);
rt->source = health_source_file(line, filename);
rt->green = NAN;
rt->red = NAN;
rt->delay_multiplier = 1.0;
@ -568,10 +569,10 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
if(!simple_pattern_matches(os_pattern, host->os)) {
if(rc)
debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s/%s: host O/S does not match '%s'", host->hostname, rc->name, line, path, filename, os_match);
debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: host O/S does not match '%s'", host->hostname, rc->name, line, filename, os_match);
if(rt)
debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s/%s: host O/S does not match '%s'", host->hostname, rt->name, line, path, filename, os_match);
debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: host O/S does not match '%s'", host->hostname, rt->name, line, filename, os_match);
ignore_this = 1;
}
@ -584,10 +585,10 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
if(!simple_pattern_matches(host_pattern, host->hostname)) {
if(rc)
debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s/%s: hostname does not match '%s'", host->hostname, rc->name, line, path, filename, host_match);
debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: hostname does not match '%s'", host->hostname, rc->name, line, filename, host_match);
if(rt)
debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s/%s: hostname does not match '%s'", host->hostname, rt->name, line, path, filename, host_match);
debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: hostname does not match '%s'", host->hostname, rt->name, line, filename, host_match);
ignore_this = 1;
}
@ -598,8 +599,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
if(rc->chart) {
if(strcmp(rc->chart, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->chart, value, value);
error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rc->name, key, rc->chart, value, value);
freez(rc->chart);
}
@ -607,29 +608,29 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
rc->hash_chart = simple_hash(rc->chart);
}
else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
health_parse_db_lookup(line, path, filename, value, &rc->group, &rc->after, &rc->before,
health_parse_db_lookup(line, filename, value, &rc->group, &rc->after, &rc->before,
&rc->update_every,
&rc->options, &rc->dimensions);
}
else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
if(!health_parse_duration(value, &rc->update_every))
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
line, path, filename, rc->name, key, value);
error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
line, filename, rc->name, key, value);
}
else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
char *e;
rc->green = str2ld(value, &e);
if(e && *e) {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rc->name, key, e);
error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
line, filename, rc->name, key, e);
}
}
else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
char *e;
rc->red = str2ld(value, &e);
if(e && *e) {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rc->name, key, e);
error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
line, filename, rc->name, key, e);
}
}
else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
@ -637,8 +638,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
int error = 0;
rc->calculation = expression_parse(value, &failed_at, &error);
if(!rc->calculation) {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, filename, rc->name, key, value, expression_strerror(error), failed_at);
}
}
else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
@ -646,8 +647,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
int error = 0;
rc->warning = expression_parse(value, &failed_at, &error);
if(!rc->warning) {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, filename, rc->name, key, value, expression_strerror(error), failed_at);
}
}
else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
@ -655,15 +656,15 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
int error = 0;
rc->critical = expression_parse(value, &failed_at, &error);
if(!rc->critical) {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, path, filename, rc->name, key, value, expression_strerror(error), failed_at);
error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, filename, rc->name, key, value, expression_strerror(error), failed_at);
}
}
else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
if(rc->exec) {
if(strcmp(rc->exec, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->exec, value, value);
error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rc->name, key, rc->exec, value, value);
freez(rc->exec);
}
@ -672,8 +673,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
if(rc->recipient) {
if(strcmp(rc->recipient, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->recipient, value, value);
error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rc->name, key, rc->recipient, value, value);
freez(rc->recipient);
}
@ -682,8 +683,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
if(rc->units) {
if(strcmp(rc->units, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->units, value, value);
error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rc->name, key, rc->units, value, value);
freez(rc->units);
}
@ -693,8 +694,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
if(rc->info) {
if(strcmp(rc->info, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rc->name, key, rc->info, value, value);
error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rc->name, key, rc->info, value, value);
freez(rc->info);
}
@ -702,22 +703,22 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
strip_quotes(rc->info);
}
else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
health_parse_delay(line, path, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
health_parse_delay(line, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
}
else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
rc->options |= health_parse_options(value);
}
else {
error("Health configuration at line %zu of file '%s/%s' for alarm '%s' has unknown key '%s'.",
line, path, filename, rc->name, key);
error("Health configuration at line %zu of file '%s' for alarm '%s' has unknown key '%s'.",
line, filename, rc->name, key);
}
}
else if(rt) {
if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
if(rt->context) {
if(strcmp(rt->context, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->context, value, value);
error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rt->name, key, rt->context, value, value);
freez(rt->context);
}
@ -732,28 +733,28 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
rt->family_pattern = simple_pattern_create(rt->family_match, NULL, SIMPLE_PATTERN_EXACT);
}
else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
health_parse_db_lookup(line, path, filename, value, &rt->group, &rt->after, &rt->before,
health_parse_db_lookup(line, filename, value, &rt->group, &rt->after, &rt->before,
&rt->update_every, &rt->options, &rt->dimensions);
}
else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
if(!health_parse_duration(value, &rt->update_every))
error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
line, path, filename, rt->name, key, value);
error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
line, filename, rt->name, key, value);
}
else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
char *e;
rt->green = str2ld(value, &e);
if(e && *e) {
error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rt->name, key, e);
error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
line, filename, rt->name, key, e);
}
}
else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
char *e;
rt->red = str2ld(value, &e);
if(e && *e) {
error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
line, path, filename, rt->name, key, e);
error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
line, filename, rt->name, key, e);
}
}
else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
@ -761,8 +762,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
int error = 0;
rt->calculation = expression_parse(value, &failed_at, &error);
if(!rt->calculation) {
error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, filename, rt->name, key, value, expression_strerror(error), failed_at);
}
}
else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
@ -770,8 +771,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
int error = 0;
rt->warning = expression_parse(value, &failed_at, &error);
if(!rt->warning) {
error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, filename, rt->name, key, value, expression_strerror(error), failed_at);
}
}
else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
@ -779,15 +780,15 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
int error = 0;
rt->critical = expression_parse(value, &failed_at, &error);
if(!rt->critical) {
error("Health configuration at line %zu of file '%s/%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, path, filename, rt->name, key, value, expression_strerror(error), failed_at);
error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
line, filename, rt->name, key, value, expression_strerror(error), failed_at);
}
}
else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
if(rt->exec) {
if(strcmp(rt->exec, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->exec, value, value);
error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rt->name, key, rt->exec, value, value);
freez(rt->exec);
}
@ -796,8 +797,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
if(rt->recipient) {
if(strcmp(rt->recipient, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->recipient, value, value);
error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rt->name, key, rt->recipient, value, value);
freez(rt->recipient);
}
@ -806,8 +807,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
if(rt->units) {
if(strcmp(rt->units, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->units, value, value);
error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rt->name, key, rt->units, value, value);
freez(rt->units);
}
@ -817,8 +818,8 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
if(rt->info) {
if(strcmp(rt->info, value) != 0)
error("Health configuration at line %zu of file '%s/%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, path, filename, rt->name, key, rt->info, value, value);
error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
line, filename, rt->name, key, rt->info, value, value);
freez(rt->info);
}
@ -826,19 +827,19 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
strip_quotes(rt->info);
}
else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
health_parse_delay(line, path, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
health_parse_delay(line, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
}
else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
rt->options |= health_parse_options(value);
}
else {
error("Health configuration at line %zu of file '%s/%s' for template '%s' has unknown key '%s'.",
line, path, filename, rt->name, key);
error("Health configuration at line %zu of file '%s' for template '%s' has unknown key '%s'.",
line, filename, rt->name, key);
}
}
else {
error("Health configuration at line %zu of file '%s/%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
line, path, filename, key);
error("Health configuration at line %zu of file '%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
line, filename, key);
}
}
@ -852,51 +853,7 @@ int health_readfile(RRDHOST *host, const char *path, const char *filename) {
return 1;
}
void health_readdir(RRDHOST *host, const char *path) {
if(!host->health_enabled) return;
size_t pathlen = strlen(path);
debug(D_HEALTH, "Health configuration reading directory '%s'", path);
DIR *dir = opendir(path);
if (!dir) {
error("Health configuration cannot open directory '%s'.", path);
return;
}
struct dirent *de = NULL;
while ((de = readdir(dir))) {
size_t len = strlen(de->d_name);
if(de->d_type == DT_DIR
&& (
(de->d_name[0] == '.' && de->d_name[1] == '\0')
|| (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
)) {
debug(D_HEALTH, "Ignoring directory '%s'", de->d_name);
continue;
}
else if(de->d_type == DT_DIR) {
char *s = mallocz(pathlen + strlen(de->d_name) + 2);
strcpy(s, path);
strcat(s, "/");
strcat(s, de->d_name);
health_readdir(host, s);
freez(s);
continue;
}
else if((de->d_type == DT_LNK || de->d_type == DT_REG || de->d_type == DT_UNKNOWN) &&
len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
health_readfile(host, path, de->d_name);
}
else debug(D_HEALTH, "Ignoring file '%s'", de->d_name);
}
closedir(dir);
void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath) {
if(unlikely(!host->health_enabled)) return;
recursive_config_double_dir_load(user_path, stock_path, subpath, health_readfile, (void *) host);
}

View file

@ -460,12 +460,13 @@ static void get_netdata_configured_variables() {
// ------------------------------------------------------------------------
// get system paths
netdata_configured_config_dir = config_get(CONFIG_SECTION_GLOBAL, "config directory", CONFIG_DIR);
netdata_configured_log_dir = config_get(CONFIG_SECTION_GLOBAL, "log directory", LOG_DIR);
netdata_configured_web_dir = config_get(CONFIG_SECTION_GLOBAL, "web files directory", WEB_DIR);
netdata_configured_cache_dir = config_get(CONFIG_SECTION_GLOBAL, "cache directory", CACHE_DIR);
netdata_configured_varlib_dir = config_get(CONFIG_SECTION_GLOBAL, "lib directory", VARLIB_DIR);
netdata_configured_home_dir = config_get(CONFIG_SECTION_GLOBAL, "home directory", CACHE_DIR);
netdata_configured_user_config_dir = config_get(CONFIG_SECTION_GLOBAL, "config directory", netdata_configured_user_config_dir);
netdata_configured_stock_config_dir = config_get(CONFIG_SECTION_GLOBAL, "stock config directory", netdata_configured_stock_config_dir);
netdata_configured_log_dir = config_get(CONFIG_SECTION_GLOBAL, "log directory", netdata_configured_log_dir);
netdata_configured_web_dir = config_get(CONFIG_SECTION_GLOBAL, "web files directory", netdata_configured_web_dir);
netdata_configured_cache_dir = config_get(CONFIG_SECTION_GLOBAL, "cache directory", netdata_configured_cache_dir);
netdata_configured_varlib_dir = config_get(CONFIG_SECTION_GLOBAL, "lib directory", netdata_configured_varlib_dir);
netdata_configured_home_dir = config_get(CONFIG_SECTION_GLOBAL, "home directory", netdata_configured_home_dir);
{
char plugins_dirs[(FILENAME_MAX * 2) + 1];
@ -596,15 +597,17 @@ void set_global_environment() {
setenv("NETDATA_UPDATE_EVERY", b, 1);
}
setenv("NETDATA_HOSTNAME" , netdata_configured_hostname, 1);
setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_config_dir), 1);
setenv("NETDATA_PLUGINS_DIR", verify_required_directory(netdata_configured_plugins_dir), 1);
setenv("NETDATA_WEB_DIR" , verify_required_directory(netdata_configured_web_dir), 1);
setenv("NETDATA_CACHE_DIR" , verify_required_directory(netdata_configured_cache_dir), 1);
setenv("NETDATA_LIB_DIR" , verify_required_directory(netdata_configured_varlib_dir), 1);
setenv("NETDATA_LOG_DIR" , verify_required_directory(netdata_configured_log_dir), 1);
setenv("HOME" , verify_required_directory(netdata_configured_home_dir), 1);
setenv("NETDATA_HOST_PREFIX", netdata_configured_host_prefix, 1);
setenv("NETDATA_HOSTNAME" , netdata_configured_hostname, 1);
setenv("NETDATA_CONFIG_DIR" , verify_required_directory(netdata_configured_user_config_dir), 1);
setenv("NETDATA_USER_CONFIG_DIR" , verify_required_directory(netdata_configured_user_config_dir), 1);
setenv("NETDATA_STOCK_CONFIG_DIR" , verify_required_directory(netdata_configured_stock_config_dir), 1);
setenv("NETDATA_PLUGINS_DIR" , verify_required_directory(netdata_configured_plugins_dir), 1);
setenv("NETDATA_WEB_DIR" , verify_required_directory(netdata_configured_web_dir), 1);
setenv("NETDATA_CACHE_DIR" , verify_required_directory(netdata_configured_cache_dir), 1);
setenv("NETDATA_LIB_DIR" , verify_required_directory(netdata_configured_varlib_dir), 1);
setenv("NETDATA_LOG_DIR" , verify_required_directory(netdata_configured_log_dir), 1);
setenv("HOME" , verify_required_directory(netdata_configured_home_dir), 1);
setenv("NETDATA_HOST_PREFIX" , netdata_configured_host_prefix, 1);
get_system_timezone();
@ -626,6 +629,33 @@ void set_global_environment() {
setenv("LC_ALL", "C", 1);
}
static int load_netdata_conf(char *filename, char overwrite_used) {
if(filename)
return config_load(filename, overwrite_used);
filename = strdupz_path_subpath(netdata_configured_user_config_dir, "netdata.conf");
int ret = config_load(filename, overwrite_used);
if(!ret) {
freez(filename);
filename = strdupz_path_subpath(netdata_configured_stock_config_dir, "netdata.conf");
ret = config_load(filename, overwrite_used);
}
freez(filename);
return ret;
}
static void load_stream_conf() {
char *filename = strdupz_path_subpath(netdata_configured_user_config_dir, "stream.conf");
if(!appconfig_load(&stream_config, filename, 0)) {
freez(filename);
filename = strdupz_path_subpath(netdata_configured_stock_config_dir, "stream.conf");
appconfig_load(&stream_config, filename, 0);
}
freez(filename);
}
int main(int argc, char **argv) {
int i;
int config_loaded = 0;
@ -686,7 +716,7 @@ int main(int argc, char **argv) {
while( (opt = getopt(argc, argv, optstring)) != -1 ) {
switch(opt) {
case 'c':
if(config_load(optarg, 1) != 1) {
if(load_netdata_conf(optarg, 1) != 1) {
error("Cannot load configuration file %s.", optarg);
return 1;
}
@ -731,9 +761,6 @@ int main(int argc, char **argv) {
if(strcmp(optarg, "unittest") == 0) {
if(unit_test_buffer()) return 1;
if(unit_test_str2ld()) return 1;
//default_rrd_update_every = 1;
//default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
//if(!config_loaded) config_load(NULL, 0);
get_netdata_configured_variables();
default_rrd_update_every = 1;
default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
@ -841,7 +868,7 @@ int main(int argc, char **argv) {
if(!config_loaded) {
fprintf(stderr, "warning: no configuration file has been loaded. Use -c CONFIG_FILE, before -W get. Using default config.\n");
config_load(NULL, 0);
load_netdata_conf(NULL, 0);
}
backwards_compatible_config();
@ -879,7 +906,7 @@ int main(int argc, char **argv) {
#endif
if(!config_loaded)
config_load(NULL, 0);
load_netdata_conf(NULL, 0);
// ------------------------------------------------------------------------
// initialize netdata
@ -902,8 +929,8 @@ int main(int argc, char **argv) {
// work while we are cd into config_dir
// to allow the plugins refer to their config
// files using relative filenames
if(chdir(netdata_configured_config_dir) == -1)
fatal("Cannot cd to '%s'", netdata_configured_config_dir);
if(chdir(netdata_configured_user_config_dir) == -1)
fatal("Cannot cd to '%s'", netdata_configured_user_config_dir);
}
char *user = NULL;
@ -938,11 +965,7 @@ int main(int argc, char **argv) {
// --------------------------------------------------------------------
// load stream.conf
{
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/stream.conf", netdata_configured_config_dir);
appconfig_load(&stream_config, filename, 0);
}
load_stream_conf();
// --------------------------------------------------------------------

View file

@ -240,7 +240,7 @@ RRDHOST *rrdhost_create(const char *hostname,
health_alarm_log_open(host);
rrdhost_wrlock(host);
health_readdir(host, health_config_dir());
health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL);
rrdhost_unlock(host);
}

View file

@ -1031,7 +1031,7 @@ void *statsd_collector_thread(void *ptr) {
#define STATSD_CONF_LINE_MAX 8192
static STATSD_APP_CHART_DIM_VALUE_TYPE string2valuetype(const char *type, size_t line, const char *path, const char *filename) {
static STATSD_APP_CHART_DIM_VALUE_TYPE string2valuetype(const char *type, size_t line, const char *filename) {
if(!type || !*type) type = "last";
if(!strcmp(type, "events")) return STATSD_APP_CHART_DIM_VALUE_TYPE_EVENTS;
@ -1044,7 +1044,7 @@ static STATSD_APP_CHART_DIM_VALUE_TYPE string2valuetype(const char *type, size_t
else if(!strcmp(type, "stddev")) return STATSD_APP_CHART_DIM_VALUE_TYPE_STDDEV;
else if(!strcmp(type, "percentile")) return STATSD_APP_CHART_DIM_VALUE_TYPE_PERCENTILE;
error("STATSD: invalid type '%s' at line %zu of file '%s/%s'. Using 'last'.", type, line, path, filename);
error("STATSD: invalid type '%s' at line %zu of file '%s'. Using 'last'.", type, line, filename);
return STATSD_APP_CHART_DIM_VALUE_TYPE_LAST;
}
@ -1110,19 +1110,14 @@ static STATSD_APP_CHART_DIM *add_dimension_to_app_chart(
return dim;
}
static int statsd_readfile(const char *path, const char *filename, STATSD_APP *app, STATSD_APP_CHART *chart, DICTIONARY *dict) {
debug(D_STATSD, "STATSD configuration reading file '%s/%s'", path, filename);
static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHART *chart, DICTIONARY *dict) {
debug(D_STATSD, "STATSD configuration reading file '%s'", filename);
char *buffer = mallocz(STATSD_CONF_LINE_MAX + 1);
if(filename[0] == '/')
strncpyz(buffer, filename, STATSD_CONF_LINE_MAX);
else
snprintfz(buffer, STATSD_CONF_LINE_MAX, "%s/%s", path, filename);
FILE *fp = fopen(buffer, "r");
FILE *fp = fopen(filename, "r");
if(!fp) {
error("STATSD: cannot open file '%s'.", buffer);
error("STATSD: cannot open file '%s'.", filename);
freez(buffer);
return -1;
}
@ -1135,18 +1130,31 @@ static int statsd_readfile(const char *path, const char *filename, STATSD_APP *a
s = trim(buffer);
if (!s || *s == '#') {
debug(D_STATSD, "STATSD: ignoring line %zu of file '%s/%s', it is empty.", line, path, filename);
debug(D_STATSD, "STATSD: ignoring line %zu of file '%s', it is empty.", line, filename);
continue;
}
debug(D_STATSD, "STATSD: processing line %zu of file '%s/%s': %s", line, path, filename, buffer);
debug(D_STATSD, "STATSD: processing line %zu of file '%s': %s", line, filename, buffer);
if(*s == 'i' && strncmp(s, "include", 7) == 0) {
s = trim(&s[7]);
if(s && *s)
statsd_readfile(path, s, app, chart, dict);
if(s && *s) {
char *tmp;
if(*s == '/')
tmp = strdupz(s);
else {
// the file to be included is relative to current file
// find the directory name from the file we already read
char *filename2 = strdupz(filename); // copy filename, since dirname() will change it
char *dir = dirname(filename2); // find the directory part of the filename
tmp = strdupz_path_subpath(dir, s); // compose the new filename to read;
freez(filename2); // free the filename we copied
}
statsd_readfile(tmp, app, chart, dict);
freez(tmp);
}
else
error("STATSD: ignoring line %zu of file '%s/%s', include filename is empty", line, path, s);
error("STATSD: ignoring line %zu of file '%s', include filename is empty", line, filename);
continue;
}
@ -1208,20 +1216,20 @@ static int statsd_readfile(const char *path, const char *filename, STATSD_APP *a
}
}
else
error("STATSD: ignoring line %zu ('%s') of file '%s/%s', [app] is not defined.", line, s, path, filename);
error("STATSD: ignoring line %zu ('%s') of file '%s', [app] is not defined.", line, s, filename);
continue;
}
if(!app) {
error("STATSD: ignoring line %zu ('%s') of file '%s/%s', it is outside all sections.", line, s, path, filename);
error("STATSD: ignoring line %zu ('%s') of file '%s', it is outside all sections.", line, s, filename);
continue;
}
char *name = s;
char *value = strchr(s, '=');
if(!value) {
error("STATSD: ignoring line %zu ('%s') of file '%s/%s', there is no = in it.", line, s, path, filename);
error("STATSD: ignoring line %zu ('%s') of file '%s', there is no = in it.", line, s, filename);
continue;
}
*value = '\0';
@ -1231,11 +1239,11 @@ static int statsd_readfile(const char *path, const char *filename, STATSD_APP *a
value = trim(value);
if(!name || *name == '#') {
error("STATSD: ignoring line %zu of file '%s/%s', name is empty.", line, path, filename);
error("STATSD: ignoring line %zu of file '%s', name is empty.", line, filename);
continue;
}
if(!value) {
debug(D_CONFIG, "STATSD: ignoring line %zu of file '%s/%s', value is empty.", line, path, filename);
debug(D_CONFIG, "STATSD: ignoring line %zu of file '%s', value is empty.", line, filename);
continue;
}
@ -1275,7 +1283,7 @@ static int statsd_readfile(const char *path, const char *filename, STATSD_APP *a
app->rrd_history_entries = 5;
}
else {
error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [app] section.", line, name, path, filename);
error("STATSD: ignoring line %zu ('%s') of file '%s'. Unknown keyword for the [app] section.", line, name, filename);
continue;
}
}
@ -1360,14 +1368,14 @@ static int statsd_readfile(const char *path, const char *filename, STATSD_APP *a
, (multipler && *multipler)?str2l(multipler):1
, (divisor && *divisor)?str2l(divisor):1
, flags
, string2valuetype(type, line, path, filename)
, string2valuetype(type, line, filename)
);
if(pattern)
dim->metric_pattern = simple_pattern_create(dim->metric, NULL, SIMPLE_PATTERN_EXACT);
}
else {
error("STATSD: ignoring line %zu ('%s') of file '%s/%s'. Unknown keyword for the [%s] section.", line, name, path, filename, chart->id);
error("STATSD: ignoring line %zu ('%s') of file '%s'. Unknown keyword for the [%s] section.", line, name, filename, chart->id);
continue;
}
}
@ -1378,49 +1386,13 @@ static int statsd_readfile(const char *path, const char *filename, STATSD_APP *a
return 0;
}
static void statsd_readdir(const char *path) {
size_t pathlen = strlen(path);
static int statsd_file_callback(const char *filename, void *data) {
(void)data;
return statsd_readfile(filename, NULL, NULL, NULL);
}
debug(D_STATSD, "STATSD configuration reading directory '%s'", path);
DIR *dir = opendir(path);
if (!dir) {
error("STATSD configuration cannot open directory '%s'.", path);
return;
}
struct dirent *de = NULL;
while ((de = readdir(dir))) {
size_t len = strlen(de->d_name);
if(de->d_type == DT_DIR
&& (
(de->d_name[0] == '.' && de->d_name[1] == '\0')
|| (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
)) {
debug(D_STATSD, "STATSD: ignoring directory '%s'", de->d_name);
continue;
}
else if(de->d_type == DT_DIR) {
char *s = mallocz(pathlen + strlen(de->d_name) + 2);
strcpy(s, path);
strcat(s, "/");
strcat(s, de->d_name);
statsd_readdir(s);
freez(s);
continue;
}
else if((de->d_type == DT_LNK || de->d_type == DT_REG || de->d_type == DT_UNKNOWN) &&
len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
statsd_readfile(path, de->d_name, NULL, NULL, NULL);
}
else debug(D_STATSD, "STATSD: ignoring file '%s'", de->d_name);
}
closedir(dir);
static inline void statsd_readdir(const char *user_path, const char *stock_path, const char *subpath) {
recursive_config_double_dir_load(user_path, stock_path, subpath, statsd_file_callback, NULL);
}
// --------------------------------------------------------------------------------------------------------------------
@ -2243,11 +2215,7 @@ void *statsd_main(void *ptr) {
#endif
// read custom application definitions
{
char filename[FILENAME_MAX + 1];
snprintfz(filename, FILENAME_MAX, "%s/statsd.d", netdata_configured_config_dir);
statsd_readdir(filename);
}
statsd_readdir(netdata_configured_user_config_dir, netdata_configured_stock_config_dir, "statsd.d");
// ----------------------------------------------------------------------------------------------------------------
// statsd setup

View file

@ -1199,8 +1199,50 @@ static int test_variable_renames(void) {
return 1;
}
int check_strdupz_path_subpath() {
struct strdupz_path_subpath_checks {
const char *path;
const char *subpath;
const char *result;
} checks[] = {
{ "", "", "." },
{ "/", "", "/" },
{ "/etc/netdata", "", "/etc/netdata" },
{ "/etc/netdata///", "", "/etc/netdata" },
{ "/etc/netdata///", "health.d", "/etc/netdata/health.d" },
{ "/etc/netdata///", "///health.d", "/etc/netdata/health.d" },
{ "/etc/netdata", "///health.d", "/etc/netdata/health.d" },
{ "", "///health.d", "./health.d" },
{ "/", "///health.d", "/health.d" },
// terminator
{ NULL, NULL, NULL }
};
size_t i;
for(i = 0; checks[i].result ; i++) {
char *s = strdupz_path_subpath(checks[i].path, checks[i].subpath);
fprintf(stderr, "strdupz_path_subpath(\"%s\", \"%s\") = \"%s\": ", checks[i].path, checks[i].subpath, s);
if(!s || strcmp(s, checks[i].result) != 0) {
freez(s);
fprintf(stderr, "FAILED\n");
return 1;
}
else {
freez(s);
fprintf(stderr, "OK\n");
}
}
return 0;
}
int run_all_mockup_tests(void)
{
if(check_strdupz_path_subpath())
return 1;
if(check_number_printing())
return 1;