summaryrefslogtreecommitdiffstats
path: root/collectors/plugins.d
diff options
context:
space:
mode:
Diffstat (limited to 'collectors/plugins.d')
-rw-r--r--collectors/plugins.d/Makefile.in702
-rw-r--r--collectors/plugins.d/README.md46
-rw-r--r--collectors/plugins.d/plugins_d.c712
-rw-r--r--collectors/plugins.d/plugins_d.h12
-rw-r--r--collectors/plugins.d/pluginsd_parser.c738
-rw-r--r--collectors/plugins.d/pluginsd_parser.h40
6 files changed, 932 insertions, 1318 deletions
diff --git a/collectors/plugins.d/Makefile.in b/collectors/plugins.d/Makefile.in
deleted file mode 100644
index 856ec294e..000000000
--- a/collectors/plugins.d/Makefile.in
+++ /dev/null
@@ -1,702 +0,0 @@
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
-
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-VPATH = @srcdir@
-am__is_gnu_make = { \
- if test -z '$(MAKELEVEL)'; then \
- false; \
- elif test -n '$(MAKE_HOST)'; then \
- true; \
- elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
- true; \
- else \
- false; \
- fi; \
-}
-am__make_running_with_option = \
- case $${target_option-} in \
- ?) ;; \
- *) echo "am__make_running_with_option: internal error: invalid" \
- "target option '$${target_option-}' specified" >&2; \
- exit 1;; \
- esac; \
- has_opt=no; \
- sane_makeflags=$$MAKEFLAGS; \
- if $(am__is_gnu_make); then \
- sane_makeflags=$$MFLAGS; \
- else \
- case $$MAKEFLAGS in \
- *\\[\ \ ]*) \
- bs=\\; \
- sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
- | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
- esac; \
- fi; \
- skip_next=no; \
- strip_trailopt () \
- { \
- flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
- }; \
- for flg in $$sane_makeflags; do \
- test $$skip_next = yes && { skip_next=no; continue; }; \
- case $$flg in \
- *=*|--*) continue;; \
- -*I) strip_trailopt 'I'; skip_next=yes;; \
- -*I?*) strip_trailopt 'I';; \
- -*O) strip_trailopt 'O'; skip_next=yes;; \
- -*O?*) strip_trailopt 'O';; \
- -*l) strip_trailopt 'l'; skip_next=yes;; \
- -*l?*) strip_trailopt 'l';; \
- -[dEDm]) skip_next=yes;; \
- -[JT]) skip_next=yes;; \
- esac; \
- case $$flg in \
- *$$target_option*) has_opt=yes; break;; \
- esac; \
- done; \
- test $$has_opt = yes
-am__make_dryrun = (target_option=n; $(am__make_running_with_option))
-am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
-pkgdatadir = $(datadir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-subdir = collectors/plugins.d
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/build/m4/ax_c___atomic.m4 \
- $(top_srcdir)/build/m4/ax_c__generic.m4 \
- $(top_srcdir)/build/m4/ax_c_lto.m4 \
- $(top_srcdir)/build/m4/ax_c_mallinfo.m4 \
- $(top_srcdir)/build/m4/ax_c_mallopt.m4 \
- $(top_srcdir)/build/m4/ax_check_compile_flag.m4 \
- $(top_srcdir)/build/m4/ax_gcc_func_attribute.m4 \
- $(top_srcdir)/build/m4/ax_pthread.m4 \
- $(top_srcdir)/build/m4/jemalloc.m4 \
- $(top_srcdir)/build/m4/tcmalloc.m4 $(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
- $(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_DATA) \
- $(am__DIST_COMMON)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/config.h
-CONFIG_CLEAN_FILES =
-CONFIG_CLEAN_VPATH_FILES =
-AM_V_P = $(am__v_P_@AM_V@)
-am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
-am__v_P_0 = false
-am__v_P_1 = :
-AM_V_GEN = $(am__v_GEN_@AM_V@)
-am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
-am__v_GEN_0 = @echo " GEN " $@;
-am__v_GEN_1 =
-AM_V_at = $(am__v_at_@AM_V@)
-am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
-am__v_at_0 = @
-am__v_at_1 =
-SOURCES =
-DIST_SOURCES =
-RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
- ctags-recursive dvi-recursive html-recursive info-recursive \
- install-data-recursive install-dvi-recursive \
- install-exec-recursive install-html-recursive \
- install-info-recursive install-pdf-recursive \
- install-ps-recursive install-recursive installcheck-recursive \
- installdirs-recursive pdf-recursive ps-recursive \
- tags-recursive uninstall-recursive
-am__can_run_installinfo = \
- case $$AM_UPDATE_INFO_DIR in \
- n|no|NO) false;; \
- *) (install-info --version) >/dev/null 2>&1;; \
- esac
-DATA = $(dist_noinst_DATA)
-RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
- distclean-recursive maintainer-clean-recursive
-am__recursive_targets = \
- $(RECURSIVE_TARGETS) \
- $(RECURSIVE_CLEAN_TARGETS) \
- $(am__extra_recursive_targets)
-AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
- distdir
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-# Read a list of newline-separated strings from the standard input,
-# and print each of them once, without duplicates. Input order is
-# *not* preserved.
-am__uniquify_input = $(AWK) '\
- BEGIN { nonempty = 0; } \
- { items[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in items) print i; }; } \
-'
-# Make sure the list of sources is unique. This is necessary because,
-# e.g., the same source file might be shared among _SOURCES variables
-# for different programs/libraries.
-am__define_uniq_tagged_files = \
- list='$(am__tagged_files)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
-DIST_SUBDIRS = $(SUBDIRS)
-am__DIST_COMMON = $(srcdir)/Makefile.in
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-am__relativize = \
- dir0=`pwd`; \
- sed_first='s,^\([^/]*\)/.*$$,\1,'; \
- sed_rest='s,^[^/]*/*,,'; \
- sed_last='s,^.*/\([^/]*\)$$,\1,'; \
- sed_butlast='s,/*[^/]*$$,,'; \
- while test -n "$$dir1"; do \
- first=`echo "$$dir1" | sed -e "$$sed_first"`; \
- if test "$$first" != "."; then \
- if test "$$first" = ".."; then \
- dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
- dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
- else \
- first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
- if test "$$first2" = "$$first"; then \
- dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
- else \
- dir2="../$$dir2"; \
- fi; \
- dir0="$$dir0"/"$$first"; \
- fi; \
- fi; \
- dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
- done; \
- reldir="$$dir2"
-ACLOCAL = @ACLOCAL@
-AMTAR = @AMTAR@
-AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
-CMOCKA_LIBS = @CMOCKA_LIBS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CUPSCONFIG = @CUPSCONFIG@
-CXX = @CXX@
-CXXDEPMODE = @CXXDEPMODE@
-CXXFLAGS = @CXXFLAGS@
-CXX_BINARY = @CXX_BINARY@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-ENABLE_UNITTESTS = @ENABLE_UNITTESTS@
-EXEEXT = @EXEEXT@
-GREP = @GREP@
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-IPMIMONITORING_CFLAGS = @IPMIMONITORING_CFLAGS@
-IPMIMONITORING_LIBS = @IPMIMONITORING_LIBS@
-JSON_CFLAGS = @JSON_CFLAGS@
-JSON_LIBS = @JSON_LIBS@
-LDFLAGS = @LDFLAGS@
-LIBCAP_CFLAGS = @LIBCAP_CFLAGS@
-LIBCAP_LIBS = @LIBCAP_LIBS@
-LIBCRYPTO_CFLAGS = @LIBCRYPTO_CFLAGS@
-LIBCRYPTO_LIBS = @LIBCRYPTO_LIBS@
-LIBCURL_CFLAGS = @LIBCURL_CFLAGS@
-LIBCURL_LIBS = @LIBCURL_LIBS@
-LIBMNL_CFLAGS = @LIBMNL_CFLAGS@
-LIBMNL_LIBS = @LIBMNL_LIBS@
-LIBMONGOC_CFLAGS = @LIBMONGOC_CFLAGS@
-LIBMONGOC_LIBS = @LIBMONGOC_LIBS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LIBSSL_CFLAGS = @LIBSSL_CFLAGS@
-LIBSSL_LIBS = @LIBSSL_LIBS@
-LTLIBOBJS = @LTLIBOBJS@
-MAINT = @MAINT@
-MAKEINFO = @MAKEINFO@
-MATH_CFLAGS = @MATH_CFLAGS@
-MATH_LIBS = @MATH_LIBS@
-MKDIR_P = @MKDIR_P@
-NFACCT_CFLAGS = @NFACCT_CFLAGS@
-NFACCT_LIBS = @NFACCT_LIBS@
-OBJEXT = @OBJEXT@
-OPTIONAL_CUPS_CFLAGS = @OPTIONAL_CUPS_CFLAGS@
-OPTIONAL_CUPS_LIBS = @OPTIONAL_CUPS_LIBS@
-OPTIONAL_IPMIMONITORING_CFLAGS = @OPTIONAL_IPMIMONITORING_CFLAGS@
-OPTIONAL_IPMIMONITORING_LIBS = @OPTIONAL_IPMIMONITORING_LIBS@
-OPTIONAL_JSONC_LIBS = @OPTIONAL_JSONC_LIBS@
-OPTIONAL_JUDY_LIBS = @OPTIONAL_JUDY_LIBS@
-OPTIONAL_KINESIS_CFLAGS = @OPTIONAL_KINESIS_CFLAGS@
-OPTIONAL_KINESIS_LIBS = @OPTIONAL_KINESIS_LIBS@
-OPTIONAL_LIBCAP_CFLAGS = @OPTIONAL_LIBCAP_CFLAGS@
-OPTIONAL_LIBCAP_LIBS = @OPTIONAL_LIBCAP_LIBS@
-OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@
-OPTIONAL_MATH_CFLAGS = @OPTIONAL_MATH_CFLAGS@
-OPTIONAL_MATH_LIBS = @OPTIONAL_MATH_LIBS@
-OPTIONAL_MONGOC_CFLAGS = @OPTIONAL_MONGOC_CFLAGS@
-OPTIONAL_MONGOC_LIBS = @OPTIONAL_MONGOC_LIBS@
-OPTIONAL_NFACCT_CFLAGS = @OPTIONAL_NFACCT_CFLAGS@
-OPTIONAL_NFACCT_LIBS = @OPTIONAL_NFACCT_LIBS@
-OPTIONAL_PROMETHEUS_REMOTE_WRITE_CFLAGS = @OPTIONAL_PROMETHEUS_REMOTE_WRITE_CFLAGS@
-OPTIONAL_PROMETHEUS_REMOTE_WRITE_LIBS = @OPTIONAL_PROMETHEUS_REMOTE_WRITE_LIBS@
-OPTIONAL_SSL_LIBS = @OPTIONAL_SSL_LIBS@
-OPTIONAL_UUID_CFLAGS = @OPTIONAL_UUID_CFLAGS@
-OPTIONAL_UUID_LIBS = @OPTIONAL_UUID_LIBS@
-OPTIONAL_UV_LIBS = @OPTIONAL_UV_LIBS@
-OPTIONAL_XENSTAT_CFLAGS = @OPTIONAL_XENSTAT_CFLAGS@
-OPTIONAL_XENSTAT_LIBS = @OPTIONAL_XENSTAT_LIBS@
-OPTIONAL_ZLIB_CFLAGS = @OPTIONAL_ZLIB_CFLAGS@
-OPTIONAL_ZLIB_LIBS = @OPTIONAL_ZLIB_LIBS@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_RPM_VERSION = @PACKAGE_RPM_VERSION@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_URL = @PACKAGE_URL@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-PKG_CONFIG = @PKG_CONFIG@
-PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
-PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
-PROTOBUF_CFLAGS = @PROTOBUF_CFLAGS@
-PROTOBUF_LIBS = @PROTOBUF_LIBS@
-PROTOC = @PROTOC@
-PTHREAD_CC = @PTHREAD_CC@
-PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
-PTHREAD_LIBS = @PTHREAD_LIBS@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-SSE_CANDIDATE = @SSE_CANDIDATE@
-STRIP = @STRIP@
-TEST_CFLAGS = @TEST_CFLAGS@
-TEST_LIBS = @TEST_LIBS@
-UUID_CFLAGS = @UUID_CFLAGS@
-UUID_LIBS = @UUID_LIBS@
-VERSION = @VERSION@
-XENLIGHT_CFLAGS = @XENLIGHT_CFLAGS@
-XENLIGHT_LIBS = @XENLIGHT_LIBS@
-YAJL_CFLAGS = @YAJL_CFLAGS@
-YAJL_LIBS = @YAJL_LIBS@
-ZLIB_CFLAGS = @ZLIB_CFLAGS@
-ZLIB_LIBS = @ZLIB_LIBS@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_CXX = @ac_ct_CXX@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-ax_pthread_config = @ax_pthread_config@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_target = @build_target@
-build_vendor = @build_vendor@
-builddir = @builddir@
-cachedir = @cachedir@
-chartsdir = @chartsdir@
-configdir = @configdir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-exec_prefix = @exec_prefix@
-has_jemalloc = @has_jemalloc@
-has_tcmalloc = @has_tcmalloc@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-install_sh = @install_sh@
-libconfigdir = @libconfigdir@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-logdir = @logdir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-nodedir = @nodedir@
-oldincludedir = @oldincludedir@
-pdfdir = @pdfdir@
-pluginsdir = @pluginsdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-pythondir = @pythondir@
-registrydir = @registrydir@
-runstatedir = @runstatedir@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_build_prefix = @top_build_prefix@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-varlibdir = @varlibdir@
-webdir = @webdir@
-AUTOMAKE_OPTIONS = subdir-objects
-MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
-SUBDIRS = \
- $(NULL)
-
-dist_noinst_DATA = \
- README.md \
- $(NULL)
-
-all: all-recursive
-
-.SUFFIXES:
-$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
- @for dep in $?; do \
- case '$(am__configure_deps)' in \
- *$$dep*) \
- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
- && { if test -f $@; then exit 0; else break; fi; }; \
- exit 1;; \
- esac; \
- done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu collectors/plugins.d/Makefile'; \
- $(am__cd) $(top_srcdir) && \
- $(AUTOMAKE) --gnu collectors/plugins.d/Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
- @case '$?' in \
- *config.status*) \
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
- *) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
- esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(am__aclocal_m4_deps):
-
-# This directory's subdirectories are mostly independent; you can cd
-# into them and run 'make' without going through this Makefile.
-# To change the values of 'make' variables: instead of editing Makefiles,
-# (1) if the variable is set in 'config.status', edit 'config.status'
-# (which will cause the Makefiles to be regenerated when you run 'make');
-# (2) otherwise, pass the desired values on the 'make' command line.
-$(am__recursive_targets):
- @fail=; \
- if $(am__make_keepgoing); then \
- failcom='fail=yes'; \
- else \
- failcom='exit 1'; \
- fi; \
- dot_seen=no; \
- target=`echo $@ | sed s/-recursive//`; \
- case "$@" in \
- distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
- *) list='$(SUBDIRS)' ;; \
- esac; \
- for subdir in $$list; do \
- echo "Making $$target in $$subdir"; \
- if test "$$subdir" = "."; then \
- dot_seen=yes; \
- local_target="$$target-am"; \
- else \
- local_target="$$target"; \
- fi; \
- ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || eval $$failcom; \
- done; \
- if test "$$dot_seen" = "no"; then \
- $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
- fi; test -z "$$fail"
-
-ID: $(am__tagged_files)
- $(am__define_uniq_tagged_files); mkid -fID $$unique
-tags: tags-recursive
-TAGS: tags
-
-tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
- set x; \
- here=`pwd`; \
- if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
- include_option=--etags-include; \
- empty_fix=.; \
- else \
- include_option=--include; \
- empty_fix=; \
- fi; \
- list='$(SUBDIRS)'; for subdir in $$list; do \
- if test "$$subdir" = .; then :; else \
- test ! -f $$subdir/TAGS || \
- set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
- fi; \
- done; \
- $(am__define_uniq_tagged_files); \
- shift; \
- if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
- test -n "$$unique" || unique=$$empty_fix; \
- if test $$# -gt 0; then \
- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
- "$$@" $$unique; \
- else \
- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
- $$unique; \
- fi; \
- fi
-ctags: ctags-recursive
-
-CTAGS: ctags
-ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
- $(am__define_uniq_tagged_files); \
- test -z "$(CTAGS_ARGS)$$unique" \
- || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
- $$unique
-
-GTAGS:
- here=`$(am__cd) $(top_builddir) && pwd` \
- && $(am__cd) $(top_srcdir) \
- && gtags -i $(GTAGS_ARGS) "$$here"
-cscopelist: cscopelist-recursive
-
-cscopelist-am: $(am__tagged_files)
- list='$(am__tagged_files)'; \
- case "$(srcdir)" in \
- [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
- *) sdir=$(subdir)/$(srcdir) ;; \
- esac; \
- for i in $$list; do \
- if test -f "$$i"; then \
- echo "$(subdir)/$$i"; \
- else \
- echo "$$sdir/$$i"; \
- fi; \
- done >> $(top_builddir)/cscope.files
-
-distclean-tags:
- -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
-distdir: $(DISTFILES)
- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
- list='$(DISTFILES)'; \
- dist_files=`for file in $$list; do echo $$file; done | \
- sed -e "s|^$$srcdirstrip/||;t" \
- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
- case $$dist_files in \
- */*) $(MKDIR_P) `echo "$$dist_files" | \
- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
- sort -u` ;; \
- esac; \
- for file in $$dist_files; do \
- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
- if test -d $$d/$$file; then \
- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
- if test -d "$(distdir)/$$file"; then \
- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
- fi; \
- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
- fi; \
- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
- else \
- test -f "$(distdir)/$$file" \
- || cp -p $$d/$$file "$(distdir)/$$file" \
- || exit 1; \
- fi; \
- done
- @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
- if test "$$subdir" = .; then :; else \
- $(am__make_dryrun) \
- || test -d "$(distdir)/$$subdir" \
- || $(MKDIR_P) "$(distdir)/$$subdir" \
- || exit 1; \
- dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
- $(am__relativize); \
- new_distdir=$$reldir; \
- dir1=$$subdir; dir2="$(top_distdir)"; \
- $(am__relativize); \
- new_top_distdir=$$reldir; \
- echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
- echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
- ($(am__cd) $$subdir && \
- $(MAKE) $(AM_MAKEFLAGS) \
- top_distdir="$$new_top_distdir" \
- distdir="$$new_distdir" \
- am__remove_distdir=: \
- am__skip_length_check=: \
- am__skip_mode_fix=: \
- distdir) \
- || exit 1; \
- fi; \
- done
-check-am: all-am
-check: check-recursive
-all-am: Makefile $(DATA)
-installdirs: installdirs-recursive
-installdirs-am:
-install: install-recursive
-install-exec: install-exec-recursive
-install-data: install-data-recursive
-uninstall: uninstall-recursive
-
-install-am: all-am
- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-recursive
-install-strip:
- if test -z '$(STRIP)'; then \
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- install; \
- else \
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
- fi
-mostlyclean-generic:
-
-clean-generic:
-
-distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-
-maintainer-clean-generic:
- @echo "This command is intended for maintainers to use"
- @echo "it deletes files that may require special tools to rebuild."
- -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-clean: clean-recursive
-
-clean-am: clean-generic mostlyclean-am
-
-distclean: distclean-recursive
- -rm -f Makefile
-distclean-am: clean-am distclean-generic distclean-tags
-
-dvi: dvi-recursive
-
-dvi-am:
-
-html: html-recursive
-
-html-am:
-
-info: info-recursive
-
-info-am:
-
-install-data-am:
-
-install-dvi: install-dvi-recursive
-
-install-dvi-am:
-
-install-exec-am:
-
-install-html: install-html-recursive
-
-install-html-am:
-
-install-info: install-info-recursive
-
-install-info-am:
-
-install-man:
-
-install-pdf: install-pdf-recursive
-
-install-pdf-am:
-
-install-ps: install-ps-recursive
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-recursive
- -rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-recursive
-
-mostlyclean-am: mostlyclean-generic
-
-pdf: pdf-recursive
-
-pdf-am:
-
-ps: ps-recursive
-
-ps-am:
-
-uninstall-am:
-
-.MAKE: $(am__recursive_targets) install-am install-strip
-
-.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
- check-am clean clean-generic cscopelist-am ctags ctags-am \
- distclean distclean-generic distclean-tags distdir dvi dvi-am \
- html html-am info info-am install install-am install-data \
- install-data-am install-dvi install-dvi-am install-exec \
- install-exec-am install-html install-html-am install-info \
- install-info-am install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
- pdf-am ps ps-am tags tags-am uninstall uninstall-am
-
-.PRECIOUS: Makefile
-
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/collectors/plugins.d/README.md b/collectors/plugins.d/README.md
index 289b8c1a0..c166e11e3 100644
--- a/collectors/plugins.d/README.md
+++ b/collectors/plugins.d/README.md
@@ -1,3 +1,8 @@
+<!--
+title: "External plugins overview"
+custom_edit_url: https://github.com/netdata/netdata/edit/master/collectors/plugins.d/README.md
+-->
+
# External plugins overview
`plugins.d` is the Netdata internal plugin that collects metrics
@@ -7,18 +12,18 @@ from external processes, thus allowing Netdata to use **external plugins**.
|plugin|language|O/S|description|
|:----:|:------:|:-:|:----------|
-|[apps.plugin](../apps.plugin/)|`C`|linux, freebsd|monitors the whole process tree on Linux and FreeBSD and breaks down system resource usage by **process**, **user** and **user group**.|
-|[charts.d.plugin](../charts.d.plugin/)|`BASH`|all|a **plugin orchestrator** for data collection modules written in `BASH` v4+.|
-|[cups.plugin](../cups.plugin/)|`C`|all|monitors **CUPS**|
-|[fping.plugin](../fping.plugin/)|`C`|all|measures network latency, jitter and packet loss between the monitored node and any number of remote network end points.|
-|[ioping.plugin](../ioping.plugin/)|`C`|all|measures disk latency.|
-|[freeipmi.plugin](../freeipmi.plugin/)|`C`|linux|collects metrics from enterprise hardware sensors, on Linux servers.|
-|[nfacct.plugin](../nfacct.plugin/)|`C`|linux|collects netfilter firewall, connection tracker and accounting metrics using `libmnl` and `libnetfilter_acct`.|
-|[xenstat.plugin](../xenstat.plugin/)|`C`|linux|collects XenServer and XCP-ng metrics using `lxenstat`.|
-|[perf.plugin](../perf.plugin/)|`C`|linux|collects CPU performance metrics using performance monitoring units (PMU).|
-|[node.d.plugin](../node.d.plugin/)|`node.js`|all|a **plugin orchestrator** for data collection modules written in `node.js`.|
-|[python.d.plugin](../python.d.plugin/)|`python`|all|a **plugin orchestrator** for data collection modules written in `python` v2 or v3 (both are supported).|
-|[slabinfo.plugin](../slabinfo.plugin/)|`C`|linux|collects kernel internal cache objects (SLAB) metrics.|
+|[apps.plugin](/collectors/apps.plugin/README.md)|`C`|linux, freebsd|monitors the whole process tree on Linux and FreeBSD and breaks down system resource usage by **process**, **user** and **user group**.|
+|[charts.d.plugin](/collectors/charts.d.plugin/README.md)|`BASH`|all|a **plugin orchestrator** for data collection modules written in `BASH` v4+.|
+|[cups.plugin](/collectors/cups.plugin/README.md)|`C`|all|monitors **CUPS**|
+|[fping.plugin](/collectors/fping.plugin/README.md)|`C`|all|measures network latency, jitter and packet loss between the monitored node and any number of remote network end points.|
+|[ioping.plugin](/collectors/ioping.plugin/README.md)|`C`|all|measures disk latency.|
+|[freeipmi.plugin](/collectors/freeipmi.plugin/README.md)|`C`|linux|collects metrics from enterprise hardware sensors, on Linux servers.|
+|[nfacct.plugin](/collectors/nfacct.plugin/README.md)|`C`|linux|collects netfilter firewall, connection tracker and accounting metrics using `libmnl` and `libnetfilter_acct`.|
+|[xenstat.plugin](/collectors/xenstat.plugin/README.md)|`C`|linux|collects XenServer and XCP-ng metrics using `lxenstat`.|
+|[perf.plugin](/collectors/perf.plugin/README.md)|`C`|linux|collects CPU performance metrics using performance monitoring units (PMU).|
+|[node.d.plugin](/collectors/node.d.plugin/README.md)|`node.js`|all|a **plugin orchestrator** for data collection modules written in `node.js`.|
+|[python.d.plugin](/collectors/python.d.plugin/README.md)|`python`|all|a **plugin orchestrator** for data collection modules written in `python` v2 or v3 (both are supported).|
+|[slabinfo.plugin](/collectors/slabinfo.plugin/README.md)|`C`|linux|collects kernel internal cache objects (SLAB) metrics.|
Plugin orchestrators may also be described as **modular plugins**. They are modular since they accept custom made modules to be included. Writing modules for these plugins is easier than accessing the native Netdata API directly. You will find modules already available for each orchestrator under the directory of the particular modular plugin (e.g. under python.d.plugin for the python orchestrator).
Each of these modular plugins has each own methods for defining modules. Please check the examples and their documentation.
@@ -57,7 +62,7 @@ Plugins can create any number of charts with any number of dimensions each. Each
Netdata will supply the environment variables `NETDATA_USER_CONFIG_DIR` (for user supplied) and `NETDATA_STOCK_CONFIG_DIR` (for Netdata supplied) configuration files to identify the directory where configuration files are stored. It is up to the plugin to read the configuration it needs.
-The `netdata.conf` section [plugins] section contains a list of all the plugins found at the system where Netdata runs, with a boolean setting to enable them or not.
+The `netdata.conf` section `[plugins]` section contains a list of all the plugins found at the system where Netdata runs, with a boolean setting to enable them or not.
Example:
@@ -74,10 +79,10 @@ Example:
```
The setting `enable running new plugins` sets the default behavior for all external plugins. It can be
-overriden for distinct plugins by modifying the appropriate plugin value configuration to either `yes` or `now`.
+overridden for distinct plugins by modifying the appropriate plugin value configuration to either `yes` or `no`.
-The setting `check for new plugins every` sets the interval between scans of the directory `/usr/libexec/netdata/plugins.d`.
-New plugins can be added anytime and netdata will detect them in a timely manner.
+The setting `check for new plugins every` sets the interval between scans of the directory
+`/usr/libexec/netdata/plugins.d`. New plugins can be added any time, and Netdata will detect them in a timely manner.
For each of the external plugins enabled, another `netdata.conf` section
is created, in the form of `[plugin:NAME]`, where `NAME` is the name of the external plugin.
@@ -378,15 +383,18 @@ or do not output the line at all.
## Modular Plugins
-1. **python**, use `python.d.plugin`, there are many examples in the [python.d directory](../python.d.plugin/)
+1. **python**, use `python.d.plugin`, there are many examples in the [python.d
+ directory](/collectors/python.d.plugin/README.md)
python is ideal for Netdata plugins. It is a simple, yet powerful way to collect data, it has a very small memory footprint, although it is not the most CPU efficient way to do it.
-2. **node.js**, use `node.d.plugin`, there are a few examples in the [node.d directory](../node.d.plugin/)
+2. **node.js**, use `node.d.plugin`, there are a few examples in the [node.d
+ directory](/collectors/node.d.plugin/README.md)
node.js is the fastest scripting language for collecting data. If your plugin needs to do a lot of work, compute values, etc, node.js is probably the best choice before moving to compiled code. Keep in mind though that node.js is not memory efficient; it will probably need more RAM compared to python.
-3. **BASH**, use `charts.d.plugin`, there are many examples in the [charts.d directory](../charts.d.plugin/)
+3. **BASH**, use `charts.d.plugin`, there are many examples in the [charts.d
+ directory](/collectors/charts.d.plugin/README.md)
BASH is the simplest scripting language for collecting values. It is the less efficient though in terms of CPU resources. You can use it to collect data quickly, but extensive use of it might use a lot of system resources.
diff --git a/collectors/plugins.d/plugins_d.c b/collectors/plugins.d/plugins_d.c
index 32c584032..42889fa8c 100644
--- a/collectors/plugins.d/plugins_d.c
+++ b/collectors/plugins.d/plugins_d.c
@@ -1,11 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "plugins_d.h"
+#include "pluginsd_parser.h"
char *plugin_directories[PLUGINSD_MAX_DIRECTORIES] = { NULL };
struct plugind *pluginsd_root = NULL;
-static inline int pluginsd_space(char c) {
+inline int pluginsd_space(char c) {
switch(c) {
case ' ':
case '\t':
@@ -19,8 +20,9 @@ static inline int pluginsd_space(char c) {
}
}
-inline int config_isspace(char c) {
- switch(c) {
+inline int config_isspace(char c)
+{
+ switch (c) {
case ' ':
case '\t':
case '\r':
@@ -34,15 +36,18 @@ inline int config_isspace(char c) {
}
// split a text into words, respecting quotes
-static inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char)) {
+static inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover)
+{
char *s = str, quote = 0;
- int i = 0, j;
+ int i = 0, j, rec = 0;
+ char *recover = recover_input;
// skip all white space
- while(unlikely(custom_isspace(*s))) s++;
+ while (unlikely(custom_isspace(*s)))
+ s++;
// check for quote
- if(unlikely(*s == '\'' || *s == '"')) {
+ if (unlikely(*s == '\'' || *s == '"')) {
quote = *s; // remember the quote
s++; // skip the quote
}
@@ -51,595 +56,95 @@ static inline int quoted_strings_splitter(char *str, char **words, int max_words
words[i++] = s;
// while we have something
- while(likely(*s)) {
+ while (likely(*s)) {
// if it is escape
- if(unlikely(*s == '\\' && s[1])) {
+ if (unlikely(*s == '\\' && s[1])) {
s += 2;
continue;
}
// if it is quote
- else if(unlikely(*s == quote)) {
+ else if (unlikely(*s == quote)) {
quote = 0;
+ if (recover && rec < max_recover) {
+ recover_location[rec++] = s;
+ *recover++ = *s;
+ }
*s = ' ';
continue;
}
// if it is a space
- else if(unlikely(quote == 0 && custom_isspace(*s))) {
-
+ else if (unlikely(quote == 0 && custom_isspace(*s))) {
// terminate the word
+ if (recover && rec < max_recover) {
+ if (!rec || (rec && recover_location[rec-1] != s)) {
+ recover_location[rec++] = s;
+ *recover++ = *s;
+ }
+ }
*s++ = '\0';
// skip all white space
- while(likely(custom_isspace(*s))) s++;
+ while (likely(custom_isspace(*s)))
+ s++;
// check for quote
- if(unlikely(*s == '\'' || *s == '"')) {
+ if (unlikely(*s == '\'' || *s == '"')) {
quote = *s; // remember the quote
s++; // skip the quote
}
// if we reached the end, stop
- if(unlikely(!*s)) break;
+ if (unlikely(!*s))
+ break;
// store the next word
- if(likely(i < max_words)) words[i++] = s;
- else break;
+ if (likely(i < max_words))
+ words[i++] = s;
+ else
+ break;
}
// anything else
- else s++;
+ else
+ s++;
}
// terminate the words
j = i;
- while(likely(j < max_words)) words[j++] = NULL;
+ while (likely(j < max_words))
+ words[j++] = NULL;
return i;
}
-inline int pluginsd_initialize_plugin_directories() {
+inline int pluginsd_initialize_plugin_directories()
+{
char plugins_dirs[(FILENAME_MAX * 2) + 1];
static char *plugins_dir_list = NULL;
// Get the configuration entry
- if(likely(!plugins_dir_list)) {
+ if (likely(!plugins_dir_list)) {
snprintfz(plugins_dirs, FILENAME_MAX * 2, "\"%s\" \"%s/custom-plugins.d\"", PLUGINS_DIR, CONFIG_DIR);
- plugins_dir_list = strdupz(config_get(CONFIG_SECTION_GLOBAL, "plugins directory", plugins_dirs));
+ plugins_dir_list = strdupz(config_get(CONFIG_SECTION_GLOBAL, "plugins directory", plugins_dirs));
}
// Parse it and store it to plugin directories
- return quoted_strings_splitter(plugins_dir_list, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace);
-}
-
-inline int pluginsd_split_words(char *str, char **words, int max_words) {
- return quoted_strings_splitter(str, words, max_words, pluginsd_space);
-}
-
-#ifdef ENABLE_HTTPS
-/**
- * Update Buffer
- *
- * Update the temporary buffer used to parse data received from slave
- *
- * @param output is a pointer to the vector where I will store the data
- * @param ssl is the connection pointer with the server
- *
- * @return it returns the total of bytes read on success and a negative number otherwise
- */
-int pluginsd_update_buffer(char *output, SSL *ssl) {
- ERR_clear_error();
- int bytesleft = SSL_read(ssl, output, PLUGINSD_LINE_MAX_SSL_READ);
- if(bytesleft <= 0) {
- int sslerrno = SSL_get_error(ssl, bytesleft);
- switch(sslerrno) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- {
- break;
- }
- default:
- {
- u_long err;
- char buf[256];
- int counter = 0;
- while ((err = ERR_get_error()) != 0) {
- ERR_error_string_n(err, buf, sizeof(buf));
- info("%d SSL Handshake error (%s) on socket %d ", counter++, ERR_error_string((long)SSL_get_error(ssl, bytesleft), NULL), SSL_get_fd(ssl));
- }
- }
-
- }
- } else {
- output[bytesleft] = '\0';
- }
-
- return bytesleft;
+ return quoted_strings_splitter(plugins_dir_list, plugin_directories, PLUGINSD_MAX_DIRECTORIES, config_isspace, NULL, NULL, 0);
}
-/**
- * Get from Buffer
- *
- * Get data to process from buffer
- *
- * @param output is the output vector that will be used to parse the string.
- * @param bytesread the amount of bytes read in the previous iteration.
- * @param input the input vector where there are data to process
- * @param ssl a pointer to the connection with the server
- * @param src the first address of the input, because sometime will be necessary to restart the addr with it.
- *
- * @return It returns a pointer for the next iteration on success and NULL otherwise.
- */
-char * pluginsd_get_from_buffer(char *output, int *bytesread, char *input, SSL *ssl, char *src) {
- int copying = 1;
- char *endbuffer;
- size_t length;
- while(copying) {
- if(*bytesread > 0) {
- endbuffer = strchr(input, '\n');
- if(endbuffer) {
- copying = 0;
- endbuffer++; //Advance due the fact I wanna copy '\n'
- length = endbuffer - input;
- *bytesread -= length;
-
- memcpy(output, input, length);
- output += length;
- *output = '\0';
- input += length;
- }else {
- length = strlen(input);
- memcpy(output, input, length);
- output += length;
- input = src;
-
- *bytesread = pluginsd_update_buffer(input, ssl);
- if(*bytesread <= 0) {
- input = NULL;
- copying = 0;
- }
- }
- }else {
- //reduce sample of bytes read, print the length
- *bytesread = pluginsd_update_buffer(input, ssl);
- if(*bytesread <= 0) {
- input = NULL;
- copying = 0;
- }
- }
- }
-
- return input;
+inline int pluginsd_split_words(char *str, char **words, int max_words, char *recover_input, char **recover_location, int max_recover)
+{
+ return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover);
}
-#endif
-
-inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations) {
- int enabled = cd->enabled;
-
- if(!fp || !enabled) {
- cd->enabled = 0;
- return 0;
- }
-
- size_t count = 0;
-
- char line[PLUGINSD_LINE_MAX + 1];
-
- char *words[PLUGINSD_MAX_WORDS] = { NULL };
- uint32_t BEGIN_HASH = simple_hash(PLUGINSD_KEYWORD_BEGIN);
- uint32_t END_HASH = simple_hash(PLUGINSD_KEYWORD_END);
- uint32_t FLUSH_HASH = simple_hash(PLUGINSD_KEYWORD_FLUSH);
- uint32_t CHART_HASH = simple_hash(PLUGINSD_KEYWORD_CHART);
- uint32_t DIMENSION_HASH = simple_hash(PLUGINSD_KEYWORD_DIMENSION);
- uint32_t DISABLE_HASH = simple_hash(PLUGINSD_KEYWORD_DISABLE);
- uint32_t VARIABLE_HASH = simple_hash(PLUGINSD_KEYWORD_VARIABLE);
-
- RRDSET *st = NULL;
- uint32_t hash;
-
- errno = 0;
- clearerr(fp);
-
- if(unlikely(fileno(fp) == -1)) {
- error("file descriptor given is not a valid stream");
- goto cleanup;
- }
-
-#ifdef ENABLE_HTTPS
- int bytesleft = 0;
- char tmpbuffer[PLUGINSD_LINE_MAX];
- char *readfrom = NULL;
-#endif
- char *r = NULL;
- while(!ferror(fp)) {
- if(unlikely(netdata_exit)) break;
-
-#ifdef ENABLE_HTTPS
- int normalread = 1;
- if(netdata_srv_ctx) {
- if(host->stream_ssl.conn && !host->stream_ssl.flags) {
- if(!bytesleft) {
- r = line;
- readfrom = tmpbuffer;
- bytesleft = pluginsd_update_buffer(readfrom, host->stream_ssl.conn);
- if(bytesleft <= 0) {
- break;
- }
- }
-
- readfrom = pluginsd_get_from_buffer(line, &bytesleft, readfrom, host->stream_ssl.conn, tmpbuffer);
- if(!readfrom) {
- r = NULL;
- }
-
- normalread = 0;
- }
- }
-
- if(normalread) {
- r = fgets(line, PLUGINSD_LINE_MAX, fp);
- }
-#else
- r = fgets(line, PLUGINSD_LINE_MAX, fp);
-#endif
- if(unlikely(!r)) {
- if(feof(fp))
- error("read failed: end of file");
- else if(ferror(fp))
- error("read failed: input error");
- else
- error("read failed: unknown error");
- break;
- }
-
- if(unlikely(netdata_exit)) break;
-
- line[PLUGINSD_LINE_MAX] = '\0';
-
- int w = pluginsd_split_words(line, words, PLUGINSD_MAX_WORDS);
- char *s = words[0];
- if(unlikely(!s || !*s || !w)) {
- continue;
- }
-
- // debug(D_PLUGINSD, "PLUGINSD: words 0='%s' 1='%s' 2='%s' 3='%s' 4='%s' 5='%s' 6='%s' 7='%s' 8='%s' 9='%s'", words[0], words[1], words[2], words[3], words[4], words[5], words[6], words[7], words[8], words[9]);
-
- if(likely(!simple_hash_strcmp(s, "SET", &hash))) {
- char *dimension = words[1];
- char *value = words[2];
-
- if(unlikely(!dimension || !*dimension)) {
- error("requested a SET on chart '%s' of host '%s', without a dimension. Disabling it.", st->id, host->hostname);
- enabled = 0;
- break;
- }
-
- if(unlikely(!value || !*value)) value = NULL;
-
- if(unlikely(!st)) {
- error("requested a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.", dimension, value?value:"<nothing>", host->hostname);
- enabled = 0;
- break;
- }
-
- if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
- debug(D_PLUGINSD, "is setting dimension %s/%s to %s", st->id, dimension, value?value:"<nothing>");
-
- if(value) {
- RRDDIM *rd = rrddim_find(st, dimension);
- if(unlikely(!rd)) {
- error("requested a SET to dimension with id '%s' on stats '%s' (%s) on host '%s', which does not exist. Disabling it.", dimension, st->name, st->id, st->rrdhost->hostname);
- enabled = 0;
- break;
- }
- else
- rrddim_set_by_pointer(st, rd, strtoll(value, NULL, 0));
- }
- }
- else if(likely(hash == BEGIN_HASH && !strcmp(s, PLUGINSD_KEYWORD_BEGIN))) {
- char *id = words[1];
- char *microseconds_txt = words[2];
-
- if(unlikely(!id)) {
- error("requested a BEGIN without a chart id for host '%s'. Disabling it.", host->hostname);
- enabled = 0;
- break;
- }
-
- st = rrdset_find(host, id);
- if(unlikely(!st)) {
- error("requested a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", id, host->hostname);
- enabled = 0;
- break;
- }
-
- if(likely(st->counter_done)) {
- usec_t microseconds = 0;
- if(microseconds_txt && *microseconds_txt) microseconds = str2ull(microseconds_txt);
-
- if(likely(microseconds)) {
- if(trust_durations)
- rrdset_next_usec_unfiltered(st, microseconds);
- else
- rrdset_next_usec(st, microseconds);
- }
- else rrdset_next(st);
- }
- }
- else if(likely(hash == END_HASH && !strcmp(s, PLUGINSD_KEYWORD_END))) {
- if(unlikely(!st)) {
- error("requested an END, without a BEGIN on host '%s'. Disabling it.", host->hostname);
- enabled = 0;
- break;
- }
-
- if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
- debug(D_PLUGINSD, "requested an END on chart %s", st->id);
-
- rrdset_done(st);
- st = NULL;
- count++;
- }
- else if(likely(hash == CHART_HASH && !strcmp(s, PLUGINSD_KEYWORD_CHART))) {
- st = NULL;
-
- char *type = words[1];
- char *name = words[2];
- char *title = words[3];
- char *units = words[4];
- char *family = words[5];
- char *context = words[6];
- char *chart = words[7];
- char *priority_s = words[8];
- char *update_every_s = words[9];
- char *options = words[10];
- char *plugin = words[11];
- char *module = words[12];
-
- // parse the id from type
- char *id = NULL;
- if(likely(type && (id = strchr(type, '.')))) {
- *id = '\0';
- id++;
- }
-
- // make sure we have the required variables
- if(unlikely(!type || !*type || !id || !*id)) {
- error("requested a CHART, without a type.id, on host '%s'. Disabling it.", host->hostname);
- enabled = 0;
- break;
- }
-
- // parse the name, and make sure it does not include 'type.'
- if(unlikely(name && *name)) {
- // when data are coming from slaves
- // name will be type.name
- // so we have to remove 'type.' from name too
- size_t len = strlen(type);
- if(strncmp(type, name, len) == 0 && name[len] == '.')
- name = &name[len + 1];
-
- // if the name is the same with the id,
- // or is just 'NULL', clear it.
- if(unlikely(strcmp(name, id) == 0 || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0))
- name = NULL;
- }
-
- int priority = 1000;
- if(likely(priority_s && *priority_s)) priority = str2i(priority_s);
-
- int update_every = cd->update_every;
- if(likely(update_every_s && *update_every_s)) update_every = str2i(update_every_s);
- if(unlikely(!update_every)) update_every = cd->update_every;
-
- RRDSET_TYPE chart_type = RRDSET_TYPE_LINE;
- if(unlikely(chart)) chart_type = rrdset_type_id(chart);
-
- if(unlikely(name && !*name)) name = NULL;
- if(unlikely(family && !*family)) family = NULL;
- if(unlikely(context && !*context)) context = NULL;
- if(unlikely(!title)) title = "";
- if(unlikely(!units)) units = "unknown";
-
- debug(D_PLUGINSD, "creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d"
- , type, id
- , name?name:""
- , family?family:""
- , context?context:""
- , rrdset_type_name(chart_type)
- , priority
- , update_every
- );
-
- st = rrdset_create(
- host
- , type
- , id
- , name
- , family
- , context
- , title
- , units
- , (plugin && *plugin)?plugin:cd->filename
- , module
- , priority
- , update_every
- , chart_type
- );
-
- if(options && *options) {
- if(strstr(options, "obsolete"))
- rrdset_is_obsolete(st);
- else
- rrdset_isnot_obsolete(st);
-
- if(strstr(options, "detail"))
- rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
- else
- rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
-
- if(strstr(options, "hidden"))
- rrdset_flag_set(st, RRDSET_FLAG_HIDDEN);
- else
- rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN);
-
- if(strstr(options, "store_first"))
- rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST);
- else
- rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
- }
- else {
- rrdset_isnot_obsolete(st);
- rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
- rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
- }
- }
- else if(likely(hash == DIMENSION_HASH && !strcmp(s, PLUGINSD_KEYWORD_DIMENSION))) {
- char *id = words[1];
- char *name = words[2];
- char *algorithm = words[3];
- char *multiplier_s = words[4];
- char *divisor_s = words[5];
- char *options = words[6];
-
- if(unlikely(!id || !*id)) {
- error("requested a DIMENSION, without an id, host '%s' and chart '%s'. Disabling it.", host->hostname, st?st->id:"UNSET");
- enabled = 0;
- break;
- }
- if(unlikely(!st)) {
- error("requested a DIMENSION, without a CHART, on host '%s'. Disabling it.", host->hostname);
- enabled = 0;
- break;
- }
-
- long multiplier = 1;
- if(multiplier_s && *multiplier_s) multiplier = strtol(multiplier_s, NULL, 0);
- if(unlikely(!multiplier)) multiplier = 1;
-
- long divisor = 1;
- if(likely(divisor_s && *divisor_s)) divisor = strtol(divisor_s, NULL, 0);
- if(unlikely(!divisor)) divisor = 1;
-
- if(unlikely(!algorithm || !*algorithm)) algorithm = "absolute";
-
- if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
- debug(D_PLUGINSD, "creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'"
- , st->id
- , id
- , name?name:""
- , rrd_algorithm_name(rrd_algorithm_id(algorithm))
- , multiplier
- , divisor
- , options?options:""
- );
-
- RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, rrd_algorithm_id(algorithm));
- rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN);
- rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
- if(options && *options) {
- if(strstr(options, "obsolete") != NULL)
- rrddim_is_obsolete(st, rd);
- else
- rrddim_isnot_obsolete(st, rd);
- if(strstr(options, "hidden") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
- if(strstr(options, "noreset") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
- if(strstr(options, "nooverflow") != NULL) rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
- }
- else {
- rrddim_isnot_obsolete(st, rd);
- }
- }
- else if(likely(hash == VARIABLE_HASH && !strcmp(s, PLUGINSD_KEYWORD_VARIABLE))) {
- char *name = words[1];
- char *value = words[2];
- int global = (st)?0:1;
-
- if(name && *name) {
- if((strcmp(name, "GLOBAL") == 0 || strcmp(name, "HOST") == 0)) {
- global = 1;
- name = words[2];
- value = words[3];
- }
- else if((strcmp(name, "LOCAL") == 0 || strcmp(name, "CHART") == 0)) {
- global = 0;
- name = words[2];
- value = words[3];
- }
- }
-
- if(unlikely(!name || !*name)) {
- error("requested a VARIABLE on host '%s', without a variable name. Disabling it.", host->hostname);
- enabled = 0;
- break;
- }
-
- if(unlikely(!value || !*value))
- value = NULL;
-
- if(value) {
- char *endptr = NULL;
- calculated_number v = (calculated_number)str2ld(value, &endptr);
-
- if(unlikely(endptr && *endptr)) {
- if(endptr == value)
- error("the value '%s' of VARIABLE '%s' on host '%s' cannot be parsed as a number", value, name, host->hostname);
- else
- error("the value '%s' of VARIABLE '%s' on host '%s' has leftovers: '%s'", value, name, host->hostname, endptr);
- }
-
- if(global) {
- RRDVAR *rv = rrdvar_custom_host_variable_create(host, name);
- if (rv) rrdvar_custom_host_variable_set(host, rv, v);
- else error("cannot find/create HOST VARIABLE '%s' on host '%s'", name, host->hostname);
- }
- else if(st) {
- RRDSETVAR *rs = rrdsetvar_custom_chart_variable_create(st, name);
- if (rs) rrdsetvar_custom_chart_variable_set(rs, v);
- else error("cannot find/create CHART VARIABLE '%s' on host '%s', chart '%s'", name, host->hostname, st->id);
- }
- else
- error("cannot find/create CHART VARIABLE '%s' on host '%s' without a chart", name, host->hostname);
- }
- else
- error("cannot set %s VARIABLE '%s' on host '%s' to an empty value", (global)?"HOST":"CHART", name, host->hostname);
- }
- else if(likely(hash == FLUSH_HASH && !strcmp(s, PLUGINSD_KEYWORD_FLUSH))) {
- debug(D_PLUGINSD, "requested a FLUSH");
- st = NULL;
- }
- else if(unlikely(hash == DISABLE_HASH && !strcmp(s, PLUGINSD_KEYWORD_DISABLE))) {
- info("called DISABLE. Disabling it.");
- enabled = 0;
- break;
- }
- else {
- error("sent command '%s' which is not known by netdata, for host '%s'. Disabling it.", s, host->hostname);
- enabled = 0;
- break;
- }
- }
-
-cleanup:
- cd->enabled = enabled;
-
- if(likely(count)) {
- cd->successful_collections += count;
- cd->serial_failures = 0;
- }
- else
- cd->serial_failures++;
-
- return count;
-}
-
-static void pluginsd_worker_thread_cleanup(void *arg) {
+static void pluginsd_worker_thread_cleanup(void *arg)
+{
struct plugind *cd = (struct plugind *)arg;
- if(cd->enabled && !cd->obsolete) {
+ if (cd->enabled && !cd->obsolete) {
cd->obsolete = 1;
info("data collection thread exiting");
@@ -649,7 +154,7 @@ static void pluginsd_worker_thread_cleanup(void *arg) {
info("killing child process pid %d", cd->pid);
if (killpid(cd->pid) != -1) {
info("waiting for child process pid %d to exit...", cd->pid);
- waitid(P_PID, (id_t) cd->pid, &info, WEXITED);
+ waitid(P_PID, (id_t)cd->pid, &info, WEXITED);
}
cd->pid = 0;
}
@@ -657,24 +162,25 @@ static void pluginsd_worker_thread_cleanup(void *arg) {
}
#define SERIAL_FAILURES_THRESHOLD 10
-static void pluginsd_worker_thread_handle_success(struct plugind *cd) {
+static void pluginsd_worker_thread_handle_success(struct plugind *cd)
+{
if (likely(cd->successful_collections)) {
- sleep((unsigned int) cd->update_every);
+ sleep((unsigned int)cd->update_every);
return;
}
- if(likely(cd->serial_failures <= SERIAL_FAILURES_THRESHOLD)) {
- info("'%s' (pid %d) does not generate useful output but it reports success (exits with 0). %s.",
+ if (likely(cd->serial_failures <= SERIAL_FAILURES_THRESHOLD)) {
+ info(
+ "'%s' (pid %d) does not generate useful output but it reports success (exits with 0). %s.",
cd->fullfilename, cd->pid,
- cd->enabled ?
- "Waiting a bit before starting it again." :
- "Will not start it again - it is now disabled.");
- sleep((unsigned int) (cd->update_every * 10));
+ cd->enabled ? "Waiting a bit before starting it again." : "Will not start it again - it is now disabled.");
+ sleep((unsigned int)(cd->update_every * 10));
return;
}
if (cd->serial_failures > SERIAL_FAILURES_THRESHOLD) {
- error("'%s' (pid %d) does not generate useful output, although it reports success (exits with 0)."
+ error(
+ "'%s' (pid %d) does not generate useful output, although it reports success (exits with 0)."
"We have tried to collect something %zu times - unsuccessfully. Disabling it.",
cd->fullfilename, cd->pid, cd->serial_failures);
cd->enabled = 0;
@@ -684,7 +190,8 @@ static void pluginsd_worker_thread_handle_success(struct plugind *cd) {
return;
}
-static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_ret_code) {
+static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_ret_code)
+{
if (worker_ret_code == -1) {
info("'%s' (pid %d) was killed with SIGTERM. Disabling it.", cd->fullfilename, cd->pid);
cd->enabled = 0;
@@ -692,24 +199,25 @@ static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_r
}
if (!cd->successful_collections) {
- error("'%s' (pid %d) exited with error code %d and haven't collected any data. Disabling it.",
- cd->fullfilename, cd->pid, worker_ret_code);
+ error(
+ "'%s' (pid %d) exited with error code %d and haven't collected any data. Disabling it.", cd->fullfilename,
+ cd->pid, worker_ret_code);
cd->enabled = 0;
return;
}
if (cd->serial_failures <= SERIAL_FAILURES_THRESHOLD) {
- error("'%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times). %s",
+ error(
+ "'%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times). %s",
cd->fullfilename, cd->pid, worker_ret_code, cd->successful_collections,
- cd->enabled ?
- "Waiting a bit before starting it again." :
- "Will not start it again - it is disabled.");
- sleep((unsigned int) (cd->update_every * 10));
+ cd->enabled ? "Waiting a bit before starting it again." : "Will not start it again - it is disabled.");
+ sleep((unsigned int)(cd->update_every * 10));
return;
}
if (cd->serial_failures > SERIAL_FAILURES_THRESHOLD) {
- error("'%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times)."
+ error(
+ "'%s' (pid %d) exited with error code %d, but has given useful output in the past (%zu times)."
"We tried to restart it %zu times, but it failed to generate data. Disabling it.",
cd->fullfilename, cd->pid, worker_ret_code, cd->successful_collections, cd->serial_failures);
cd->enabled = 0;
@@ -720,7 +228,8 @@ static void pluginsd_worker_thread_handle_error(struct plugind *cd, int worker_r
}
#undef SERIAL_FAILURES_THRESHOLD
-void *pluginsd_worker_thread(void *arg) {
+void *pluginsd_worker_thread(void *arg)
+{
netdata_thread_cleanup_push(pluginsd_worker_thread_cleanup, arg);
struct plugind *cd = (struct plugind *)arg;
@@ -728,9 +237,9 @@ void *pluginsd_worker_thread(void *arg) {
cd->obsolete = 0;
size_t count = 0;
- while(!netdata_exit) {
+ while (!netdata_exit) {
FILE *fp = mypopen(cd->cmd, &cd->pid);
- if(unlikely(!fp)) {
+ if (unlikely(!fp)) {
error("Cannot popen(\"%s\", \"r\").", cd->cmd);
break;
}
@@ -748,14 +257,16 @@ void *pluginsd_worker_thread(void *arg) {
pluginsd_worker_thread_handle_error(cd, worker_ret_code);
cd->pid = 0;
- if(unlikely(!cd->enabled)) break;
+ if (unlikely(!cd->enabled))
+ break;
}
netdata_thread_cleanup_pop(1);
return NULL;
}
-static void pluginsd_main_cleanup(void *data) {
+static void pluginsd_main_cleanup(void *data)
+{
struct netdata_static_thread *static_thread = (struct netdata_static_thread *)data;
static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
info("cleaning up...");
@@ -772,31 +283,34 @@ static void pluginsd_main_cleanup(void *data) {
static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
}
-void *pluginsd_main(void *ptr) {
+void *pluginsd_main(void *ptr)
+{
netdata_thread_cleanup_push(pluginsd_main_cleanup, ptr);
int automatic_run = config_get_boolean(CONFIG_SECTION_PLUGINS, "enable running new plugins", 1);
- int scan_frequency = (int) config_get_number(CONFIG_SECTION_PLUGINS, "check for new plugins every", 60);
- if(scan_frequency < 1) scan_frequency = 1;
+ int scan_frequency = (int)config_get_number(CONFIG_SECTION_PLUGINS, "check for new plugins every", 60);
+ if (scan_frequency < 1)
+ scan_frequency = 1;
// disable some plugins by default
config_get_boolean(CONFIG_SECTION_PLUGINS, "slabinfo", CONFIG_BOOLEAN_NO);
// store the errno for each plugins directory
// so that we don't log broken directories on each loop
- int directory_errors[PLUGINSD_MAX_DIRECTORIES] = { 0 };
+ int directory_errors[PLUGINSD_MAX_DIRECTORIES] = { 0 };
- while(!netdata_exit) {
+ while (!netdata_exit) {
int idx;
const char *directory_name;
- for( idx = 0; idx < PLUGINSD_MAX_DIRECTORIES && (directory_name = plugin_directories[idx]) ; idx++ ) {
- if(unlikely(netdata_exit)) break;
+ for (idx = 0; idx < PLUGINSD_MAX_DIRECTORIES && (directory_name = plugin_directories[idx]); idx++) {
+ if (unlikely(netdata_exit))
+ break;
errno = 0;
DIR *dir = opendir(directory_name);
- if(unlikely(!dir)) {
- if(directory_errors[idx] != errno) {
+ if (unlikely(!dir)) {
+ if (directory_errors[idx] != errno) {
directory_errors[idx] = errno;
error("cannot open plugins directory '%s'", directory_name);
}
@@ -804,16 +318,19 @@ void *pluginsd_main(void *ptr) {
}
struct dirent *file = NULL;
- while(likely((file = readdir(dir)))) {
- if(unlikely(netdata_exit)) break;
+ while (likely((file = readdir(dir)))) {
+ if (unlikely(netdata_exit))
+ break;
debug(D_PLUGINSD, "examining file '%s'", file->d_name);
- if(unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0)) continue;
+ if (unlikely(strcmp(file->d_name, ".") == 0 || strcmp(file->d_name, "..") == 0))
+ continue;
- int len = (int) strlen(file->d_name);
- if(unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN)) continue;
- if(unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
+ int len = (int)strlen(file->d_name);
+ if (unlikely(len <= (int)PLUGINSD_FILE_SUFFIX_LEN))
+ continue;
+ if (unlikely(strcmp(PLUGINSD_FILE_SUFFIX, &file->d_name[len - (int)PLUGINSD_FILE_SUFFIX_LEN]) != 0)) {
debug(D_PLUGINSD, "file '%s' does not end in '%s'", file->d_name, PLUGINSD_FILE_SUFFIX);
continue;
}
@@ -822,24 +339,25 @@ void *pluginsd_main(void *ptr) {
snprintfz(pluginname, CONFIG_MAX_NAME, "%.*s", (int)(len - PLUGINSD_FILE_SUFFIX_LEN), file->d_name);
int enabled = config_get_boolean(CONFIG_SECTION_PLUGINS, pluginname, automatic_run);
- if(unlikely(!enabled)) {
+ if (unlikely(!enabled)) {
debug(D_PLUGINSD, "plugin '%s' is not enabled", file->d_name);
continue;
}
// check if it runs already
struct plugind *cd;
- for(cd = pluginsd_root ; cd ; cd = cd->next)
- if(unlikely(strcmp(cd->filename, file->d_name) == 0)) break;
+ for (cd = pluginsd_root; cd; cd = cd->next)
+ if (unlikely(strcmp(cd->filename, file->d_name) == 0))
+ break;
- if(likely(cd && !cd->obsolete)) {
+ if (likely(cd && !cd->obsolete)) {
debug(D_PLUGINSD, "plugin '%s' is already running", cd->filename);
continue;
}
// it is not running
// allocate a new one, or use the obsolete one
- if(unlikely(!cd)) {
+ if (unlikely(!cd)) {
cd = callocz(sizeof(struct plugind), 1);
snprintfz(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
@@ -848,24 +366,28 @@ void *pluginsd_main(void *ptr) {
snprintfz(cd->fullfilename, FILENAME_MAX, "%s/%s", directory_name, cd->filename);
cd->enabled = enabled;
- cd->update_every = (int) config_get_number(cd->id, "update every", localhost->rrd_update_every);
+ cd->update_every = (int)config_get_number(cd->id, "update every", localhost->rrd_update_every);
cd->started_t = now_realtime_sec();
char *def = "";
- snprintfz(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
+ snprintfz(
+ cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every,
+ config_get(cd->id, "command options", def));
// link it
- if(likely(pluginsd_root)) cd->next = pluginsd_root;
+ if (likely(pluginsd_root))
+ cd->next = pluginsd_root;
pluginsd_root = cd;
// it is not currently running
cd->obsolete = 1;
- if(cd->enabled) {
+ if (cd->enabled) {
char tag[NETDATA_THREAD_TAG_MAX + 1];
snprintfz(tag, NETDATA_THREAD_TAG_MAX, "PLUGINSD[%s]", pluginname);
// spawn a new thread for it
- netdata_thread_create(&cd->thread, tag, NETDATA_THREAD_OPTION_DEFAULT, pluginsd_worker_thread, cd);
+ netdata_thread_create(
+ &cd->thread, tag, NETDATA_THREAD_OPTION_DEFAULT, pluginsd_worker_thread, cd);
}
}
}
@@ -873,7 +395,7 @@ void *pluginsd_main(void *ptr) {
closedir(dir);
}
- sleep((unsigned int) scan_frequency);
+ sleep((unsigned int)scan_frequency);
}
netdata_thread_cleanup_pop(1);
diff --git a/collectors/plugins.d/plugins_d.h b/collectors/plugins.d/plugins_d.h
index 7d5c7dda4..fd99b3584 100644
--- a/collectors/plugins.d/plugins_d.h
+++ b/collectors/plugins.d/plugins_d.h
@@ -29,6 +29,13 @@
#define PLUGINSD_KEYWORD_FLUSH "FLUSH"
#define PLUGINSD_KEYWORD_DISABLE "DISABLE"
#define PLUGINSD_KEYWORD_VARIABLE "VARIABLE"
+#define PLUGINSD_KEYWORD_LABEL "LABEL"
+#define PLUGINSD_KEYWORD_OVERWRITE "OVERWRITE"
+#define PLUGINSD_KEYWORD_GUID "GUID"
+#define PLUGINSD_KEYWORD_CONTEXT "CONTEXT"
+#define PLUGINSD_KEYWORD_TOMBSTONE "TOMBSTONE"
+#define PLUGINSD_KEYWORD_HOST "HOST"
+
#define PLUGINSD_LINE_MAX 1024
#define PLUGINSD_LINE_MAX_SSL_READ 512
@@ -58,7 +65,7 @@ struct plugind {
volatile sig_atomic_t enabled; // if this is enabled or not
time_t started_t;
-
+ uint32_t version;
struct plugind *next;
};
@@ -67,10 +74,11 @@ extern struct plugind *pluginsd_root;
extern void *pluginsd_main(void *ptr);
extern size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations);
-extern int pluginsd_split_words(char *str, char **words, int max_words);
+extern int pluginsd_split_words(char *str, char **words, int max_words, char *recover_string, char **recover_location, int max_recover);
extern int pluginsd_initialize_plugin_directories();
extern int config_isspace(char c);
+extern int pluginsd_space(char c);
#endif /* NETDATA_PLUGINS_D_H */
diff --git a/collectors/plugins.d/pluginsd_parser.c b/collectors/plugins.d/pluginsd_parser.c
new file mode 100644
index 000000000..4a97c5535
--- /dev/null
+++ b/collectors/plugins.d/pluginsd_parser.c
@@ -0,0 +1,738 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "pluginsd_parser.h"
+
+/*
+ * This is the action defined for the FLUSH command
+ */
+PARSER_RC pluginsd_set_action(void *user, RRDSET *st, RRDDIM *rd, long long int value)
+{
+ UNUSED(user);
+
+ rrddim_set_by_pointer(st, rd, value);
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_flush_action(void *user, RRDSET *st)
+{
+ UNUSED(user);
+ UNUSED(st);
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_begin_action(void *user, RRDSET *st, usec_t microseconds, int trust_durations)
+{
+ UNUSED(user);
+ if (likely(st->counter_done)) {
+ if (likely(microseconds)) {
+ if (trust_durations)
+ rrdset_next_usec_unfiltered(st, microseconds);
+ else
+ rrdset_next_usec(st, microseconds);
+ } else
+ rrdset_next(st);
+ }
+ return PARSER_RC_OK;
+}
+
+
+PARSER_RC pluginsd_end_action(void *user, RRDSET *st)
+{
+ UNUSED(user);
+
+ rrdset_done(st);
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, char *context, char *title, char *units, char *plugin,
+ char *module, int priority, int update_every, RRDSET_TYPE chart_type, char *options)
+{
+ RRDSET *st = NULL;
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+
+ st = rrdset_create(
+ host, type, id, name, family, context, title, units,
+ plugin, module, priority, update_every,
+ chart_type);
+
+ if (options && *options) {
+ if (strstr(options, "obsolete"))
+ rrdset_is_obsolete(st);
+ else
+ rrdset_isnot_obsolete(st);
+
+ if (strstr(options, "detail"))
+ rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
+ else
+ rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
+
+ if (strstr(options, "hidden"))
+ rrdset_flag_set(st, RRDSET_FLAG_HIDDEN);
+ else
+ rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN);
+
+ if (strstr(options, "store_first"))
+ rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST);
+ else
+ rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
+ } else {
+ rrdset_isnot_obsolete(st);
+ rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
+ rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
+ }
+ ((PARSER_USER_OBJECT *)user)->st = st;
+
+ return PARSER_RC_OK;
+}
+
+
+PARSER_RC pluginsd_disable_action(void *user)
+{
+ UNUSED(user);
+
+ info("called DISABLE. Disabling it.");
+ ((PARSER_USER_OBJECT *) user)->enabled = 0;
+ return PARSER_RC_ERROR;
+}
+
+
+PARSER_RC pluginsd_variable_action(void *user, RRDHOST *host, RRDSET *st, char *name, int global, calculated_number value)
+{
+ UNUSED(user);
+
+ if (global) {
+ RRDVAR *rv = rrdvar_custom_host_variable_create(host, name);
+ if (rv)
+ rrdvar_custom_host_variable_set(host, rv, value);
+ else
+ error("cannot find/create HOST VARIABLE '%s' on host '%s'", name, host->hostname);
+ } else {
+ RRDSETVAR *rs = rrdsetvar_custom_chart_variable_create(st, name);
+ if (rs)
+ rrdsetvar_custom_chart_variable_set(rs, value);
+ else
+ error("cannot find/create CHART VARIABLE '%s' on host '%s', chart '%s'", name, host->hostname, st->id);
+ }
+ return PARSER_RC_OK;
+}
+
+
+
+PARSER_RC pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm, long multiplier, long divisor, char *options,
+ RRD_ALGORITHM algorithm_type)
+{
+ UNUSED(user);
+ UNUSED(algorithm);
+
+ RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, algorithm_type);
+ rrddim_flag_clear(rd, RRDDIM_FLAG_HIDDEN);
+ rrddim_flag_clear(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
+ if (options && *options) {
+ if (strstr(options, "obsolete") != NULL)
+ rrddim_is_obsolete(st, rd);
+ else
+ rrddim_isnot_obsolete(st, rd);
+ if (strstr(options, "hidden") != NULL)
+ rrddim_flag_set(rd, RRDDIM_FLAG_HIDDEN);
+ if (strstr(options, "noreset") != NULL)
+ rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
+ if (strstr(options, "nooverflow") != NULL)
+ rrddim_flag_set(rd, RRDDIM_FLAG_DONT_DETECT_RESETS_OR_OVERFLOWS);
+ } else {
+ rrddim_isnot_obsolete(st, rd);
+ }
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_label_action(void *user, char *key, char *value, LABEL_SOURCE source)
+{
+
+ ((PARSER_USER_OBJECT *) user)->new_labels = add_label_to_list(((PARSER_USER_OBJECT *) user)->new_labels, key, value, source);
+
+ return PARSER_RC_OK;
+}
+
+
+PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, struct label *new_labels)
+{
+ UNUSED(user);
+
+ if (!host->labels.head) {
+ host->labels.head = new_labels;
+ } else {
+ rrdhost_rdlock(host);
+ replace_label_list(&host->labels, new_labels);
+ rrdhost_unlock(host);
+ }
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_set(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *dimension = words[1];
+ char *value = words[2];
+
+ RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+
+ if (unlikely(!dimension || !*dimension)) {
+ error("requested a SET on chart '%s' of host '%s', without a dimension. Disabling it.", st->id, host->hostname);
+ goto disable;
+ }
+
+ if (unlikely(!value || !*value))
+ value = NULL;
+
+ if (unlikely(!st)) {
+ error(
+ "requested a SET on dimension %s with value %s on host '%s', without a BEGIN. Disabling it.", dimension,
+ value ? value : "<nothing>", host->hostname);
+ goto disable;
+ }
+
+ if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+ debug(D_PLUGINSD, "is setting dimension %s/%s to %s", st->id, dimension, value ? value : "<nothing>");
+
+ if (value) {
+ RRDDIM *rd = rrddim_find(st, dimension);
+ if (unlikely(!rd)) {
+ error(
+ "requested a SET to dimension with id '%s' on stats '%s' (%s) on host '%s', which does not exist. Disabling it.",
+ dimension, st->name, st->id, st->rrdhost->hostname);
+ goto disable;
+ } else {
+ if (plugins_action->set_action) {
+ return plugins_action->set_action(
+ user, st, rd, strtoll(value, NULL, 0));
+ }
+ }
+ }
+ return PARSER_RC_OK;
+
+disable:
+ ((PARSER_USER_OBJECT *) user)->enabled = 0;
+ return PARSER_RC_ERROR;
+}
+
+PARSER_RC pluginsd_begin(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *id = words[1];
+ char *microseconds_txt = words[2];
+
+ RRDSET *st = NULL;
+ RRDHOST *host = ((PARSER_USER_OBJECT *)user)->host;
+
+ if (unlikely(!id)) {
+ error("requested a BEGIN without a chart id for host '%s'. Disabling it.", host->hostname);
+ goto disable;
+ }
+
+ st = rrdset_find(host, id);
+ if (unlikely(!st)) {
+ error("requested a BEGIN on chart '%s', which does not exist on host '%s'. Disabling it.", id, host->hostname);
+ goto disable;
+ }
+ ((PARSER_USER_OBJECT *)user)->st = st;
+
+ usec_t microseconds = 0;
+ if (microseconds_txt && *microseconds_txt)
+ microseconds = str2ull(microseconds_txt);
+
+ if (plugins_action->begin_action) {
+ return plugins_action->begin_action(user, st, microseconds,
+ ((PARSER_USER_OBJECT *)user)->trust_durations);
+ }
+ return PARSER_RC_OK;
+disable:
+ ((PARSER_USER_OBJECT *)user)->enabled = 0;
+ return PARSER_RC_ERROR;
+}
+
+PARSER_RC pluginsd_end(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ UNUSED(words);
+ RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+
+ if (unlikely(!st)) {
+ error("requested an END, without a BEGIN on host '%s'. Disabling it.", host->hostname);
+ ((PARSER_USER_OBJECT *) user)->enabled = 0;
+ return PARSER_RC_ERROR;
+ }
+
+ if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+ debug(D_PLUGINSD, "requested an END on chart %s", st->id);
+
+ ((PARSER_USER_OBJECT *) user)->st = NULL;
+ ((PARSER_USER_OBJECT *) user)->count++;
+ if (plugins_action->end_action) {
+ return plugins_action->end_action(user, st);
+ }
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_chart(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+ if (unlikely(!host && !((PARSER_USER_OBJECT *) user)->host_exists)) {
+ debug(D_PLUGINSD, "Ignoring chart belonging to missing or ignored host.");
+ return PARSER_RC_OK;
+ }
+
+ char *type = words[1];
+ char *name = words[2];
+ char *title = words[3];
+ char *units = words[4];
+ char *family = words[5];
+ char *context = words[6];
+ char *chart = words[7];
+ char *priority_s = words[8];
+ char *update_every_s = words[9];
+ char *options = words[10];
+ char *plugin = words[11];
+ char *module = words[12];
+
+ int have_action = ((plugins_action->chart_action) != NULL);
+
+ // parse the id from type
+ char *id = NULL;
+ if (likely(type && (id = strchr(type, '.')))) {
+ *id = '\0';
+ id++;
+ }
+
+ // make sure we have the required variables
+ if (unlikely((!type || !*type || !id || !*id))) {
+ if (likely(host))
+ error("requested a CHART, without a type.id, on host '%s'. Disabling it.", host->hostname);
+ else
+ error("requested a CHART, without a type.id. Disabling it.");
+ ((PARSER_USER_OBJECT *) user)->enabled = 0;
+ return PARSER_RC_ERROR;
+ }
+
+ // parse the name, and make sure it does not include 'type.'
+ if (unlikely(name && *name)) {
+ // when data are streamed from child nodes
+ // name will be type.name
+ // so we have to remove 'type.' from name too
+ size_t len = strlen(type);
+ if (strncmp(type, name, len) == 0 && name[len] == '.')
+ name = &name[len + 1];
+
+ // if the name is the same with the id,
+ // or is just 'NULL', clear it.
+ if (unlikely(strcmp(name, id) == 0 || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0))
+ name = NULL;
+ }
+
+ int priority = 1000;
+ if (likely(priority_s && *priority_s))
+ priority = str2i(priority_s);
+
+ int update_every = ((PARSER_USER_OBJECT *) user)->cd->update_every;
+ if (likely(update_every_s && *update_every_s))
+ update_every = str2i(update_every_s);
+ if (unlikely(!update_every))
+ update_every = ((PARSER_USER_OBJECT *) user)->cd->update_every;
+
+ RRDSET_TYPE chart_type = RRDSET_TYPE_LINE;
+ if (unlikely(chart))
+ chart_type = rrdset_type_id(chart);
+
+ if (unlikely(name && !*name))
+ name = NULL;
+ if (unlikely(family && !*family))
+ family = NULL;
+ if (unlikely(context && !*context))
+ context = NULL;
+ if (unlikely(!title))
+ title = "";
+ if (unlikely(!units))
+ units = "unknown";
+
+ debug(
+ D_PLUGINSD,
+ "creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d",
+ type, id, name ? name : "", family ? family : "", context ? context : "", rrdset_type_name(chart_type),
+ priority, update_every);
+
+ if (have_action) {
+ return plugins_action->chart_action(
+ user, type, id, name, family, context, title, units,
+ (plugin && *plugin) ? plugin : ((PARSER_USER_OBJECT *)user)->cd->filename, module, priority, update_every,
+ chart_type, options);
+ }
+
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_dimension(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *id = words[1];
+ char *name = words[2];
+ char *algorithm = words[3];
+ char *multiplier_s = words[4];
+ char *divisor_s = words[5];
+ char *options = words[6];
+
+ RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+ if (unlikely(!host && !((PARSER_USER_OBJECT *) user)->host_exists)) {
+ debug(D_PLUGINSD, "Ignoring dimension belonging to missing or ignored host.");
+ return PARSER_RC_OK;
+ }
+
+ if (unlikely(!id)) {
+ error(
+ "requested a DIMENSION, without an id, host '%s' and chart '%s'. Disabling it.", host->hostname,
+ st ? st->id : "UNSET");
+ goto disable;
+ }
+
+ if (unlikely(!st && !((PARSER_USER_OBJECT *) user)->st_exists)) {
+ error("requested a DIMENSION, without a CHART, on host '%s'. Disabling it.", host->hostname);
+ goto disable;
+ }
+
+ long multiplier = 1;
+ if (multiplier_s && *multiplier_s) {
+ multiplier = strtol(multiplier_s, NULL, 0);
+ if (unlikely(!multiplier))
+ multiplier = 1;
+ }
+
+ long divisor = 1;
+ if (likely(divisor_s && *divisor_s)) {
+ divisor = strtol(divisor_s, NULL, 0);
+ if (unlikely(!divisor))
+ divisor = 1;
+ }
+
+ if (unlikely(!algorithm || !*algorithm))
+ algorithm = "absolute";
+
+ if (unlikely(st && rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
+ debug(
+ D_PLUGINSD,
+ "creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'",
+ st->id, id, name ? name : "", rrd_algorithm_name(rrd_algorithm_id(algorithm)), multiplier, divisor,
+ options ? options : "");
+
+ if (plugins_action->dimension_action) {
+ return plugins_action->dimension_action(
+ user, st, id, name, algorithm,
+ multiplier, divisor, (options && *options)?options:NULL, rrd_algorithm_id(algorithm));
+ }
+
+ return PARSER_RC_OK;
+disable:
+ ((PARSER_USER_OBJECT *)user)->enabled = 0;
+ return PARSER_RC_ERROR;
+}
+
+PARSER_RC pluginsd_variable(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *name = words[1];
+ char *value = words[2];
+ calculated_number v;
+
+ RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+
+ int global = (st) ? 0 : 1;
+
+ if (name && *name) {
+ if ((strcmp(name, "GLOBAL") == 0 || strcmp(name, "HOST") == 0)) {
+ global = 1;
+ name = words[2];
+ value = words[3];
+ } else if ((strcmp(name, "LOCAL") == 0 || strcmp(name, "CHART") == 0)) {
+ global = 0;
+ name = words[2];
+ value = words[3];
+ }
+ }
+
+ if (unlikely(!name || !*name)) {
+ error("requested a VARIABLE on host '%s', without a variable name. Disabling it.", host->hostname);
+ ((PARSER_USER_OBJECT *)user)->enabled = 0;
+ return PARSER_RC_ERROR;
+ }
+
+ if (unlikely(!value || !*value))
+ value = NULL;
+
+ if (unlikely(!value)) {
+ error("cannot set %s VARIABLE '%s' on host '%s' to an empty value", (global) ? "HOST" : "CHART", name,
+ host->hostname);
+ return PARSER_RC_OK;
+ }
+
+ if (!global && !st) {
+ error("cannot find/create CHART VARIABLE '%s' on host '%s' without a chart", name, host->hostname);
+ return PARSER_RC_OK;
+ }
+
+ char *endptr = NULL;
+ v = (calculated_number)str2ld(value, &endptr);
+ if (unlikely(endptr && *endptr)) {
+ if (endptr == value)
+ error(
+ "the value '%s' of VARIABLE '%s' on host '%s' cannot be parsed as a number", value, name,
+ host->hostname);
+ else
+ error(
+ "the value '%s' of VARIABLE '%s' on host '%s' has leftovers: '%s'", value, name, host->hostname,
+ endptr);
+ }
+
+ if (plugins_action->variable_action) {
+ return plugins_action->variable_action(user, host, st, name, global, v);
+ }
+
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_flush(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ UNUSED(words);
+ debug(D_PLUGINSD, "requested a FLUSH");
+ RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
+ ((PARSER_USER_OBJECT *) user)->st = NULL;
+ if (plugins_action->flush_action) {
+ return plugins_action->flush_action(user, st);
+ }
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_disable(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ UNUSED(user);
+ UNUSED(words);
+
+ if (plugins_action->disable_action) {
+ return plugins_action->disable_action(user);
+ }
+ return PARSER_RC_ERROR;
+}
+
+PARSER_RC pluginsd_label(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *store;
+
+ if (!words[1] || !words[2] || !words[3]) {
+ error("Ignoring malformed or empty LABEL command.");
+ return PARSER_RC_OK;
+ }
+ if (!words[4])
+ store = words[3];
+ else {
+ store = callocz(PLUGINSD_LINE_MAX + 1, sizeof(char));
+ size_t remaining = PLUGINSD_LINE_MAX;
+ char *move = store;
+ int i = 3;
+ while (i < PLUGINSD_MAX_WORDS) {
+ size_t length = strlen(words[i]);
+ if ((length + 1) >= remaining)
+ break;
+
+ remaining -= (length + 1);
+ memcpy(move, words[i], length);
+ move += length;
+ *move++ = ' ';
+
+ i++;
+ if (!words[i])
+ break;
+ }
+ }
+
+ if (plugins_action->label_action) {
+ PARSER_RC rc = plugins_action->label_action(user, words[1], store, strtol(words[2], NULL, 10));
+ if (store != words[3])
+ freez(store);
+ return rc;
+ }
+
+ if (store != words[3])
+ freez(store);
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_overwrite(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ UNUSED(words);
+
+ RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
+ debug(D_PLUGINSD, "requested a OVERWITE a variable");
+
+ struct label *new_labels = ((PARSER_USER_OBJECT *)user)->new_labels;
+ ((PARSER_USER_OBJECT *)user)->new_labels = NULL;
+
+ if (plugins_action->overwrite_action) {
+ return plugins_action->overwrite_action(user, host, new_labels);
+ }
+
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_guid(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *uuid_str = words[1];
+ uuid_t uuid;
+
+ if (unlikely(!uuid_str)) {
+ error("requested a GUID, without a uuid.");
+ return PARSER_RC_ERROR;
+ }
+ if (unlikely(strlen(uuid_str) != GUID_LEN || uuid_parse(uuid_str, uuid) == -1)) {
+ error("requested a GUID, without a valid uuid string.");
+ return PARSER_RC_ERROR;
+ }
+
+ debug(D_PLUGINSD, "Parsed uuid=%s", uuid_str);
+ if (plugins_action->guid_action) {
+ return plugins_action->guid_action(user, &uuid);
+ }
+
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_context(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *uuid_str = words[1];
+ uuid_t uuid;
+
+ if (unlikely(!uuid_str)) {
+ error("requested a CONTEXT, without a uuid.");
+ return PARSER_RC_ERROR;
+ }
+ if (unlikely(strlen(uuid_str) != GUID_LEN || uuid_parse(uuid_str, uuid) == -1)) {
+ error("requested a CONTEXT, without a valid uuid string.");
+ return PARSER_RC_ERROR;
+ }
+
+ debug(D_PLUGINSD, "Parsed uuid=%s", uuid_str);
+ if (plugins_action->context_action) {
+ return plugins_action->context_action(user, &uuid);
+ }
+
+ return PARSER_RC_OK;
+}
+
+PARSER_RC pluginsd_tombstone(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *uuid_str = words[1];
+ uuid_t uuid;
+
+ if (unlikely(!uuid_str)) {
+ error("requested a TOMBSTONE, without a uuid.");
+ return PARSER_RC_ERROR;
+ }
+ if (unlikely(strlen(uuid_str) != GUID_LEN || uuid_parse(uuid_str, uuid) == -1)) {
+ error("requested a TOMBSTONE, without a valid uuid string.");
+ return PARSER_RC_ERROR;
+ }
+
+ debug(D_PLUGINSD, "Parsed uuid=%s", uuid_str);
+ if (plugins_action->tombstone_action) {
+ return plugins_action->tombstone_action(user, &uuid);
+ }
+
+ return PARSER_RC_OK;
+}
+
+PARSER_RC metalog_pluginsd_host(char **words, void *user, PLUGINSD_ACTION *plugins_action)
+{
+ char *machine_guid = words[1];
+ char *hostname = words[2];
+ char *registry_hostname = words[3];
+ char *update_every_s = words[4];
+ char *os = words[5];
+ char *timezone = words[6];
+ char *tags = words[7];
+
+ int update_every = 1;
+ if (likely(update_every_s && *update_every_s))
+ update_every = str2i(update_every_s);
+ if (unlikely(!update_every))
+ update_every = 1;
+
+ debug(D_PLUGINSD, "HOST PARSED: guid=%s, hostname=%s, reg_host=%s, update=%d, os=%s, timezone=%s, tags=%s",
+ machine_guid, hostname, registry_hostname, update_every, os, timezone, tags);
+
+ if (plugins_action->host_action) {
+ return plugins_action->host_action(
+ user, machine_guid, hostname, registry_hostname, update_every, os, timezone, tags);
+ }
+
+ return PARSER_RC_OK;
+}
+
+// New plugins.d parser
+
+inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations)
+{
+ int enabled = cd->enabled;
+
+ if (!fp || !enabled) {
+ cd->enabled = 0;
+ return 0;
+ }
+
+ if (unlikely(fileno(fp) == -1)) {
+ error("file descriptor given is not a valid stream");
+ cd->serial_failures++;
+ return 0;
+ }
+ clearerr(fp);
+
+ PARSER_USER_OBJECT *user = callocz(1, sizeof(*user));
+ ((PARSER_USER_OBJECT *) user)->enabled = cd->enabled;
+ ((PARSER_USER_OBJECT *) user)->host = host;
+ ((PARSER_USER_OBJECT *) user)->cd = cd;
+ ((PARSER_USER_OBJECT *) user)->trust_durations = trust_durations;
+
+ PARSER *parser = parser_init(host, user, fp, PARSER_INPUT_SPLIT);
+
+ if (unlikely(!parser)) {
+ error("Failed to initialize parser");
+ cd->serial_failures++;
+ return 0;
+ }
+
+ parser->plugins_action->begin_action = &pluginsd_begin_action;
+ parser->plugins_action->flush_action = &pluginsd_flush_action;
+ parser->plugins_action->end_action = &pluginsd_end_action;
+ parser->plugins_action->disable_action = &pluginsd_disable_action;
+ parser->plugins_action->variable_action = &pluginsd_variable_action;
+ parser->plugins_action->dimension_action = &pluginsd_dimension_action;
+ parser->plugins_action->label_action = &pluginsd_label_action;
+ parser->plugins_action->overwrite_action = &pluginsd_overwrite_action;
+ parser->plugins_action->chart_action = &pluginsd_chart_action;
+ parser->plugins_action->set_action = &pluginsd_set_action;
+
+ user->parser = parser;
+
+ while (likely(!parser_next(parser))) {
+ if (unlikely(netdata_exit || parser_action(parser, NULL)))
+ break;
+ }
+ info("PARSER ended");
+
+ parser_destroy(parser);
+
+ cd->enabled = ((PARSER_USER_OBJECT *) user)->enabled;
+ size_t count = ((PARSER_USER_OBJECT *) user)->count;
+
+ freez(user);
+
+ if (likely(count)) {
+ cd->successful_collections += count;
+ cd->serial_failures = 0;
+ } else
+ cd->serial_failures++;
+
+ return count;
+}
diff --git a/collectors/plugins.d/pluginsd_parser.h b/collectors/plugins.d/pluginsd_parser.h
new file mode 100644
index 000000000..61e9c9bab
--- /dev/null
+++ b/collectors/plugins.d/pluginsd_parser.h
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef NETDATA_PLUGINSD_PARSER_H
+#define NETDATA_PLUGINSD_PARSER_H
+
+#include "../../parser/parser.h"
+
+
+typedef struct parser_user_object {
+ PARSER *parser;
+ RRDSET *st;
+ RRDHOST *host;
+ void *opaque;
+ struct plugind *cd;
+ int trust_durations;
+ struct label *new_labels;
+ size_t count;
+ int enabled;
+ uint8_t st_exists;
+ uint8_t host_exists;
+ void *private; // the user can set this for private use
+} PARSER_USER_OBJECT;
+
+extern PARSER_RC pluginsd_set_action(void *user, RRDSET *st, RRDDIM *rd, long long int value);
+extern PARSER_RC pluginsd_flush_action(void *user, RRDSET *st);
+extern PARSER_RC pluginsd_begin_action(void *user, RRDSET *st, usec_t microseconds, int trust_durations);
+extern PARSER_RC pluginsd_end_action(void *user, RRDSET *st);
+extern PARSER_RC pluginsd_chart_action(void *user, char *type, char *id, char *name, char *family, char *context,
+ char *title, char *units, char *plugin, char *module, int priority,
+ int update_every, RRDSET_TYPE chart_type, char *options);
+extern PARSER_RC pluginsd_disable_action(void *user);
+extern PARSER_RC pluginsd_variable_action(void *user, RRDHOST *host, RRDSET *st, char *name, int global,
+ calculated_number value);
+extern PARSER_RC pluginsd_dimension_action(void *user, RRDSET *st, char *id, char *name, char *algorithm,
+ long multiplier, long divisor, char *options, RRD_ALGORITHM algorithm_type);
+extern PARSER_RC pluginsd_label_action(void *user, char *key, char *value, LABEL_SOURCE source);
+extern PARSER_RC pluginsd_overwrite_action(void *user, RRDHOST *host, struct label *new_labels);
+
+
+#endif //NETDATA_PLUGINSD_PARSER_H