diff options
Diffstat (limited to 'src/lib/process')
44 files changed, 12684 insertions, 0 deletions
diff --git a/src/lib/process/Makefile.am b/src/lib/process/Makefile.am new file mode 100644 index 0000000..a35f0b4 --- /dev/null +++ b/src/lib/process/Makefile.am @@ -0,0 +1,100 @@ +SUBDIRS = . testutils tests +# DATA_DIR is the directory where to put PID files. +dhcp_data_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -DDATA_DIR="\"$(dhcp_data_dir)\"" + +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file is included in the distribution +EXTRA_DIST = process_messages.mes libprocess.dox + +CLEANFILES = *.gcno *.gcda + +# Generated by configure files +DISTCLEANFILES = spec_config.h.pre + +lib_LTLIBRARIES = libkea-process.la +libkea_process_la_SOURCES = cb_ctl_base.h +libkea_process_la_SOURCES += config_base.cc config_base.h +libkea_process_la_SOURCES += config_ctl_info.cc config_ctl_info.h +libkea_process_la_SOURCES += config_ctl_parser.cc config_ctl_parser.h +libkea_process_la_SOURCES += d_cfg_mgr.cc d_cfg_mgr.h +libkea_process_la_SOURCES += d_controller.cc d_controller.h +libkea_process_la_SOURCES += d_log.cc d_log.h +libkea_process_la_SOURCES += d_process.h +libkea_process_la_SOURCES += daemon.cc daemon.h +libkea_process_la_SOURCES += log_parser.cc log_parser.h +libkea_process_la_SOURCES += logging_info.cc logging_info.h +libkea_process_la_SOURCES += process_messages.cc process_messages.h +libkea_process_la_SOURCES += redact_config.cc redact_config.h + +libkea_process_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_process_la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_process_la_LDFLAGS = $(AM_LDFLAGS) +libkea_process_la_LDFLAGS += -no-undefined -version-info 40:0:0 + +libkea_process_la_LIBADD = +libkea_process_la_LIBADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/http/libkea-http.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_process_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_process_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f process_messages.h process_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: process_messages.h process_messages.cc + @echo Message files regenerated + +process_messages.h process_messages.cc: process_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/process/process_messages.mes + +else + +messages process_messages.h process_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + +# Specify the headers for copying into the installation directory tree. +libkea_process_includedir = $(pkgincludedir)/process +libkea_process_include_HEADERS = \ + cb_ctl_base.h \ + config_base.h \ + config_ctl_info.h \ + config_ctl_parser.h \ + daemon.h \ + d_cfg_mgr.h \ + d_controller.h \ + d_log.h \ + d_process.h \ + logging_info.h \ + log_parser.h \ + process_messages.h \ + redact_config.h diff --git a/src/lib/process/Makefile.in b/src/lib/process/Makefile.in new file mode 100644 index 0000000..2ebafe4 --- /dev/null +++ b/src/lib/process/Makefile.in @@ -0,0 +1,1122 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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@ + + +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 = src/lib/process +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(libkea_process_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libkea_process_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libkea_process_la_DEPENDENCIES = \ + $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ + $(top_builddir)/src/lib/config/libkea-cfgclient.la \ + $(top_builddir)/src/lib/http/libkea-http.la \ + $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libkea_process_la_OBJECTS = libkea_process_la-config_base.lo \ + libkea_process_la-config_ctl_info.lo \ + libkea_process_la-config_ctl_parser.lo \ + libkea_process_la-d_cfg_mgr.lo \ + libkea_process_la-d_controller.lo libkea_process_la-d_log.lo \ + libkea_process_la-daemon.lo libkea_process_la-log_parser.lo \ + libkea_process_la-logging_info.lo \ + libkea_process_la-process_messages.lo \ + libkea_process_la-redact_config.lo +libkea_process_la_OBJECTS = $(am_libkea_process_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libkea_process_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) \ + $(libkea_process_la_LDFLAGS) $(LDFLAGS) -o $@ +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 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libkea_process_la-config_base.Plo \ + ./$(DEPDIR)/libkea_process_la-config_ctl_info.Plo \ + ./$(DEPDIR)/libkea_process_la-config_ctl_parser.Plo \ + ./$(DEPDIR)/libkea_process_la-d_cfg_mgr.Plo \ + ./$(DEPDIR)/libkea_process_la-d_controller.Plo \ + ./$(DEPDIR)/libkea_process_la-d_log.Plo \ + ./$(DEPDIR)/libkea_process_la-daemon.Plo \ + ./$(DEPDIR)/libkea_process_la-log_parser.Plo \ + ./$(DEPDIR)/libkea_process_la-logging_info.Plo \ + ./$(DEPDIR)/libkea_process_la-process_messages.Plo \ + ./$(DEPDIR)/libkea_process_la-redact_config.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libkea_process_la_SOURCES) +DIST_SOURCES = $(libkea_process_la_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 +HEADERS = $(libkea_process_include_HEADERS) +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 distdir-am +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 $(top_srcdir)/depcomp +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@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +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@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +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@ +SUBDIRS = . testutils tests +# DATA_DIR is the directory where to put PID files. +dhcp_data_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + -DDATA_DIR="\"$(dhcp_data_dir)\"" $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file is included in the distribution +EXTRA_DIST = process_messages.mes libprocess.dox +CLEANFILES = *.gcno *.gcda + +# Generated by configure files +DISTCLEANFILES = spec_config.h.pre +lib_LTLIBRARIES = libkea-process.la +libkea_process_la_SOURCES = cb_ctl_base.h config_base.cc config_base.h \ + config_ctl_info.cc config_ctl_info.h config_ctl_parser.cc \ + config_ctl_parser.h d_cfg_mgr.cc d_cfg_mgr.h d_controller.cc \ + d_controller.h d_log.cc d_log.h d_process.h daemon.cc daemon.h \ + log_parser.cc log_parser.h logging_info.cc logging_info.h \ + process_messages.cc process_messages.h redact_config.cc \ + redact_config.h +libkea_process_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_process_la_CPPFLAGS = $(AM_CPPFLAGS) +libkea_process_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info \ + 40:0:0 +libkea_process_la_LIBADD = \ + $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ + $(top_builddir)/src/lib/config/libkea-cfgclient.la \ + $(top_builddir)/src/lib/http/libkea-http.la \ + $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ + $(top_builddir)/src/lib/database/libkea-database.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/hooks/libkea-hooks.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(LOG4CPLUS_LIBS) $(BOOST_LIBS) + +# Specify the headers for copying into the installation directory tree. +libkea_process_includedir = $(pkgincludedir)/process +libkea_process_include_HEADERS = \ + cb_ctl_base.h \ + config_base.h \ + config_ctl_info.h \ + config_ctl_parser.h \ + daemon.h \ + d_cfg_mgr.h \ + d_controller.h \ + d_log.h \ + d_process.h \ + logging_info.h \ + log_parser.h \ + process_messages.h \ + redact_config.h + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(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) --foreign src/lib/process/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/process/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libkea-process.la: $(libkea_process_la_OBJECTS) $(libkea_process_la_DEPENDENCIES) $(EXTRA_libkea_process_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libkea_process_la_LINK) -rpath $(libdir) $(libkea_process_la_OBJECTS) $(libkea_process_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-config_base.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-config_ctl_info.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-config_ctl_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-d_cfg_mgr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-d_controller.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-d_log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-daemon.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-log_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-logging_info.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-process_messages.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_process_la-redact_config.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libkea_process_la-config_base.lo: config_base.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-config_base.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-config_base.Tpo -c -o libkea_process_la-config_base.lo `test -f 'config_base.cc' || echo '$(srcdir)/'`config_base.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-config_base.Tpo $(DEPDIR)/libkea_process_la-config_base.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_base.cc' object='libkea_process_la-config_base.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-config_base.lo `test -f 'config_base.cc' || echo '$(srcdir)/'`config_base.cc + +libkea_process_la-config_ctl_info.lo: config_ctl_info.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-config_ctl_info.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-config_ctl_info.Tpo -c -o libkea_process_la-config_ctl_info.lo `test -f 'config_ctl_info.cc' || echo '$(srcdir)/'`config_ctl_info.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-config_ctl_info.Tpo $(DEPDIR)/libkea_process_la-config_ctl_info.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_ctl_info.cc' object='libkea_process_la-config_ctl_info.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-config_ctl_info.lo `test -f 'config_ctl_info.cc' || echo '$(srcdir)/'`config_ctl_info.cc + +libkea_process_la-config_ctl_parser.lo: config_ctl_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-config_ctl_parser.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-config_ctl_parser.Tpo -c -o libkea_process_la-config_ctl_parser.lo `test -f 'config_ctl_parser.cc' || echo '$(srcdir)/'`config_ctl_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-config_ctl_parser.Tpo $(DEPDIR)/libkea_process_la-config_ctl_parser.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_ctl_parser.cc' object='libkea_process_la-config_ctl_parser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-config_ctl_parser.lo `test -f 'config_ctl_parser.cc' || echo '$(srcdir)/'`config_ctl_parser.cc + +libkea_process_la-d_cfg_mgr.lo: d_cfg_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-d_cfg_mgr.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-d_cfg_mgr.Tpo -c -o libkea_process_la-d_cfg_mgr.lo `test -f 'd_cfg_mgr.cc' || echo '$(srcdir)/'`d_cfg_mgr.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-d_cfg_mgr.Tpo $(DEPDIR)/libkea_process_la-d_cfg_mgr.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_cfg_mgr.cc' object='libkea_process_la-d_cfg_mgr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-d_cfg_mgr.lo `test -f 'd_cfg_mgr.cc' || echo '$(srcdir)/'`d_cfg_mgr.cc + +libkea_process_la-d_controller.lo: d_controller.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-d_controller.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-d_controller.Tpo -c -o libkea_process_la-d_controller.lo `test -f 'd_controller.cc' || echo '$(srcdir)/'`d_controller.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-d_controller.Tpo $(DEPDIR)/libkea_process_la-d_controller.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_controller.cc' object='libkea_process_la-d_controller.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-d_controller.lo `test -f 'd_controller.cc' || echo '$(srcdir)/'`d_controller.cc + +libkea_process_la-d_log.lo: d_log.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-d_log.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-d_log.Tpo -c -o libkea_process_la-d_log.lo `test -f 'd_log.cc' || echo '$(srcdir)/'`d_log.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-d_log.Tpo $(DEPDIR)/libkea_process_la-d_log.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_log.cc' object='libkea_process_la-d_log.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-d_log.lo `test -f 'd_log.cc' || echo '$(srcdir)/'`d_log.cc + +libkea_process_la-daemon.lo: daemon.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-daemon.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-daemon.Tpo -c -o libkea_process_la-daemon.lo `test -f 'daemon.cc' || echo '$(srcdir)/'`daemon.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-daemon.Tpo $(DEPDIR)/libkea_process_la-daemon.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='daemon.cc' object='libkea_process_la-daemon.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-daemon.lo `test -f 'daemon.cc' || echo '$(srcdir)/'`daemon.cc + +libkea_process_la-log_parser.lo: log_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-log_parser.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-log_parser.Tpo -c -o libkea_process_la-log_parser.lo `test -f 'log_parser.cc' || echo '$(srcdir)/'`log_parser.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-log_parser.Tpo $(DEPDIR)/libkea_process_la-log_parser.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='log_parser.cc' object='libkea_process_la-log_parser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-log_parser.lo `test -f 'log_parser.cc' || echo '$(srcdir)/'`log_parser.cc + +libkea_process_la-logging_info.lo: logging_info.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-logging_info.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-logging_info.Tpo -c -o libkea_process_la-logging_info.lo `test -f 'logging_info.cc' || echo '$(srcdir)/'`logging_info.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-logging_info.Tpo $(DEPDIR)/libkea_process_la-logging_info.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='logging_info.cc' object='libkea_process_la-logging_info.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-logging_info.lo `test -f 'logging_info.cc' || echo '$(srcdir)/'`logging_info.cc + +libkea_process_la-process_messages.lo: process_messages.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-process_messages.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-process_messages.Tpo -c -o libkea_process_la-process_messages.lo `test -f 'process_messages.cc' || echo '$(srcdir)/'`process_messages.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-process_messages.Tpo $(DEPDIR)/libkea_process_la-process_messages.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='process_messages.cc' object='libkea_process_la-process_messages.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-process_messages.lo `test -f 'process_messages.cc' || echo '$(srcdir)/'`process_messages.cc + +libkea_process_la-redact_config.lo: redact_config.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_process_la-redact_config.lo -MD -MP -MF $(DEPDIR)/libkea_process_la-redact_config.Tpo -c -o libkea_process_la-redact_config.lo `test -f 'redact_config.cc' || echo '$(srcdir)/'`redact_config.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_process_la-redact_config.Tpo $(DEPDIR)/libkea_process_la-redact_config.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='redact_config.cc' object='libkea_process_la-redact_config.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_process_la_CPPFLAGS) $(CPPFLAGS) $(libkea_process_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_process_la-redact_config.lo `test -f 'redact_config.cc' || echo '$(srcdir)/'`redact_config.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libkea_process_includeHEADERS: $(libkea_process_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libkea_process_include_HEADERS)'; test -n "$(libkea_process_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libkea_process_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libkea_process_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libkea_process_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libkea_process_includedir)" || exit $$?; \ + done + +uninstall-libkea_process_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libkea_process_include_HEADERS)'; test -n "$(libkea_process_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libkea_process_includedir)'; $(am__uninstall_files_from_dir) + +# 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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libkea_process_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libkea_process_la-config_base.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-config_ctl_info.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-config_ctl_parser.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-d_cfg_mgr.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-d_controller.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-d_log.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-daemon.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-log_parser.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-logging_info.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-process_messages.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-redact_config.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-libkea_process_includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +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 ./$(DEPDIR)/libkea_process_la-config_base.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-config_ctl_info.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-config_ctl_parser.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-d_cfg_mgr.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-d_controller.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-d_log.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-daemon.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-log_parser.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-logging_info.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-process_messages.Plo + -rm -f ./$(DEPDIR)/libkea_process_la-redact_config.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic \ + maintainer-clean-local + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libkea_process_includeHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool 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-libLTLIBRARIES \ + install-libkea_process_includeHEADERS 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 \ + maintainer-clean-local mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-libkea_process_includeHEADERS + +.PRECIOUS: Makefile + + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f process_messages.h process_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +# Define rule to build logging source files from message file +@GENERATE_MESSAGES_TRUE@messages: process_messages.h process_messages.cc +@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated + +@GENERATE_MESSAGES_TRUE@process_messages.h process_messages.cc: process_messages.mes +@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/process/process_messages.mes + +@GENERATE_MESSAGES_FALSE@messages process_messages.h process_messages.cc: +@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +# 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/src/lib/process/cb_ctl_base.h b/src/lib/process/cb_ctl_base.h new file mode 100644 index 0000000..7baf8dd --- /dev/null +++ b/src/lib/process/cb_ctl_base.h @@ -0,0 +1,373 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CB_CTL_BASE_H +#define CB_CTL_BASE_H + +#include <database/audit_entry.h> +#include <database/backend_selector.h> +#include <database/server_selector.h> +#include <process/config_base.h> +#include <process/config_ctl_info.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <process/d_log.h> + +namespace isc { +namespace process { + + +/// @brief Base class for implementing server specific mechanisms to control +/// the use of the Configuration Backends. +/// +/// Every Kea server using Config Backend as a configuration repository +/// fetches configuration available for this server during startup and then +/// periodically while it is running. In both cases, the server has to +/// take into account that there is some existing configuration that the +/// server already knows, into which the configuration from the database +/// has to be merged. +/// +/// When the server starts up, the existing configuration is the one that +/// the server reads from the configuration file. Usually, the configuration +/// fetched from the file will be disjointed with the configuration in the +/// database, e.g. all subnets should be specified either in the configuration +/// file or a database, not in both. However, there may be other cases when +/// option definitions are held in the configuration file, but the DHCP +/// options using them are stored in the database. The typical configuration +/// sequence upon the server startup will be to build the staging +/// configuration from the data stored in the configuration file and then +/// build another partial configuration from the data fetched from the +/// database. Finally, both configurations should be merged and committed +/// if they are deemed sane. +/// +/// When the server is already running it may use "audit" (a.k.a. journal) +/// to periodically check if there are any pending configuration updates. +/// If changes are present, it will be fetched and used to create a new +/// configuration object (derived from the @c ConfigBase) holding this +/// partial configuration. This configuration has to be subsequently +/// merged into the current configuration that the server is using. +/// +/// Note the difference between these two use cases is that in the first +/// case the fetched configuration is fetched into the staging configuration +/// and then committed, and in the second case it has to be directly merged +/// into the running configuration. +/// +/// This class contains some common logic to facilitate both scenarios which +/// will be used by all server types. It also contains some pure virtual +/// methods to be implemented by specific servers. The common logic includes +/// the following operations: +/// - use the "config-control" specification to connect to the specified +/// databases via the configuration backends, +/// - fetch the audit trail to detect configuration updates, +/// - store the timestamp and id of the most recent audit entry fetched +/// from the database, so as next time it can fetch only the later updates. +/// +/// The server specific part to be implemented in derived classes must +/// correctly interpret the audit entries and make appropriate API calls +/// to fetch the indicated configuration changes. It should also merge +/// the fetched configuration into the staging or current configuration. +/// Note that this class doesn't recognize whether it is a staging or +/// current configuration it is merging into. It simply uses the instance +/// provided by the caller. +/// +/// @tparam ConfigBackendMgrType Type of the Config Backend Manager used +/// by the server implementing this class. For example, for the DHCPv4 +/// server it will be @c ConfigBackendDHCPv4Mgr. +template<typename ConfigBackendMgrType> +class CBControlBase { +public: + + /// @brief Fetch mode used in invocations to @c databaseConfigFetch. + /// + /// One of the values indicates that the entire configuration should + /// be fetched. The other one indicates that only configuration updates + /// should be fetched. + enum class FetchMode { + FETCH_ALL, + FETCH_UPDATE + }; + + /// @brief Constructor. + /// + /// Sets the time of the last fetched audit entry to Jan 1st, 2000, + /// with id 0. + CBControlBase() + : last_audit_revision_time_(getInitialAuditRevisionTime()), + last_audit_revision_id_(0) { + } + + /// @brief Virtual destructor. + /// + /// It is always needed when there are virtual methods. + virtual ~CBControlBase() { + databaseConfigDisconnect(); + } + + /// @brief Resets the state of this object. + /// + /// Disconnects the configuration backends resets the recorded last + /// audit revision time and id. + void reset() { + databaseConfigDisconnect(); + last_audit_revision_time_ = getInitialAuditRevisionTime(); + last_audit_revision_id_ = 0; + } + + /// @brief (Re)connects to the specified configuration backends. + /// + /// This method disconnects from any existing configuration backends + /// and then connects to those listed in the @c ConfigControlInfo + /// structure within the @c srv_cfg argument. This method is called + /// when the server starts up. It is not called when it merely + /// fetches configuration updates. + /// + /// @param srv_cfg Pointer to the staging server configuration. + /// + /// @return true if the server found at least one backend to connect to, + /// false if there are no backends available. + bool databaseConfigConnect(const ConfigPtr& srv_cfg) { + // We need to get rid of any existing backends. These would be any + // opened by previous configuration cycle. + databaseConfigDisconnect(); + + // Fetch the config-control info. + ConstConfigControlInfoPtr config_ctl = srv_cfg->getConfigControlInfo(); + if (!config_ctl || config_ctl->getConfigDatabases().empty()) { + // No config dbs, nothing to do. + return (false); + } + + // Iterate over the configured DBs and instantiate them. + for (auto db : config_ctl->getConfigDatabases()) { + LOG_INFO(dctl_logger, DCTL_OPEN_CONFIG_DB) + .arg(db.redactedAccessString()); + getMgr().addBackend(db.getAccessString()); + } + + // Let the caller know we have opened DBs. + return (true); + } + + /// @brief Disconnects from the configuration backends. + void databaseConfigDisconnect() { + getMgr().delAllBackends(); + } + + /// @brief Fetches the entire or partial configuration from the database. + /// + /// This method is called by the starting up server to fetch and merge + /// the entire configuration from the database or to fetch configuration + /// updates periodically, e.g. as a result of triggering an interval + /// timer callback. + /// + /// @param srv_cfg pointer to the staging configuration that should + /// hold the config backends list and other partial configuration read + /// from the file in case the method is called upon the server's start + /// up. It is a pointer to the current server configuration if the + /// method is called to fetch configuration updates. + /// @param fetch_mode value indicating if the method is called upon the + /// server start up or it is called to fetch configuration updates. + virtual void databaseConfigFetch(const ConfigPtr& srv_cfg, + const FetchMode& fetch_mode = FetchMode::FETCH_ALL) { + // If the server starts up we need to connect to the database(s). + // If there are no databases available simply do nothing. + if ((fetch_mode == FetchMode::FETCH_ALL) && !databaseConfigConnect(srv_cfg)) { + // There are no CB databases so we're done + return; + } + + LOG_INFO(dctl_logger, DCTL_CONFIG_FETCH); + + // For now we find data based on first backend that has it. + db::BackendSelector backend_selector(db::BackendSelector::Type::UNSPEC); + + // Use the server_tag if set, otherwise use ALL. + std::string server_tag = srv_cfg->getServerTag(); + db::ServerSelector server_selector = + (server_tag.empty()? db::ServerSelector::ALL() : db::ServerSelector::ONE(server_tag)); + + // This collection will hold the audit entries since the last update if + // we're running this method to fetch the configuration updates. + db::AuditEntryCollection audit_entries; + + // If we're fetching updates we need to retrieve audit entries to see + // which objects have to be updated. If we're performing full reconfiguration + // we also need audit entries to set the last_audit_revision_time_ to the + // time of the most recent audit entry. + + /// @todo We need a separate API call for the latter case to only + /// fetch the last audit entry rather than all of them. + + // Save the timestamp indicating last audit revision time. + auto lb_modification_time = last_audit_revision_time_; + // Save the identifier indicating last audit revision id. + auto lb_modification_id = last_audit_revision_id_; + + audit_entries = getMgr().getPool()->getRecentAuditEntries(backend_selector, + server_selector, + lb_modification_time, + lb_modification_id); + // Store the last audit revision time. It should be set to the most recent + // audit entry fetched. If returned audit is empty we don't update. + updateLastAuditRevisionTimeId(audit_entries); + + // If this is full reconfiguration we don't need the audit entries anymore. + // Let's remove them and proceed as if they don't exist. + if (fetch_mode == FetchMode::FETCH_ALL) { + audit_entries.clear(); + } + + // If we fetch the entire config or we're updating the config and there are + // audit entries indicating that there are some pending updates, let's + // execute the server specific function that fetches and merges the data + // into the given configuration. + if ((fetch_mode == FetchMode::FETCH_ALL) || !audit_entries.empty()) { + try { + databaseConfigApply(backend_selector, server_selector, + lb_modification_time, audit_entries); + } catch (...) { + // Revert last audit revision time and id so as we can retry + // from the last successful attempt. + /// @todo Consider reverting to the initial value to reload + /// the entire configuration if the update failed. + last_audit_revision_time_ = lb_modification_time; + last_audit_revision_id_ = lb_modification_id; + throw; + } + } + } + +protected: + + /// @brief Returns audit entries for new or updated configuration + /// elements of specific type to be fetched from the database. + /// + /// This is convenience method invoked from the implementations of the + /// @c databaseConfigApply function. This method is invoked when the + /// server should fetch the updates to the existing configuration. + /// The collection of audit entries contains audit entries indicating + /// the updates to be fetched. This method returns audit entries for + /// updated configuration elements of the specific type the + /// @c databaseConfigApply should fetch. The returned collection od + /// audit entries contains CREATE or UPDATE entries of the specific type. + /// + /// @param audit_entries collection od audit entries to filter. + /// @param object_type object type to filter with. + /// @return audit entries collection with CREATE or UPDATE entries + /// of the specific type be fetched from the database. + db::AuditEntryCollection + fetchConfigElement(const db::AuditEntryCollection& audit_entries, + const std::string& object_type) const { + db::AuditEntryCollection result; + const auto& index = audit_entries.get<db::AuditEntryObjectTypeTag>(); + auto range = index.equal_range(object_type); + for (auto it = range.first; it != range.second; ++it) { + if ((*it)->getModificationType() != db::AuditEntry::ModificationType::DELETE) { + result.insert(*it); + } + } + + return (result); + } + + /// @brief Server specific method to fetch and apply back end + /// configuration into the local configuration. + /// + /// This pure virtual method must be implemented in the derived classes + /// to provide server specific implementation of fetching and applying + /// the configuration. The implementations should perform the following + /// sequence of operations: + /// - Check if any audit entries exist. If none exist, assume that this + /// is the case of full server (re)configuration, otherwise assume + /// that configuration update is being performed. + /// - Select audit entries which indicate deletion of any configuration + /// elements. For each such audit entry delete the given object from + /// the local configuration. + /// - If the server is performing full reconfiguration, fetch the entire + /// configuration for the server. If the server is merely updating + /// the server configuration, fetch only those objects for which + /// (create/update) audit entries exist. + /// - Merge the fetched configuration into the local server's + /// configuration. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param lb_modification_time Lower bound modification time for the + /// configuration elements to be fetched. + /// @param audit_entries Audit entries fetched from the database since + /// the last configuration update. This collection is empty if there + /// were no updates. + virtual void databaseConfigApply(const db::BackendSelector& backend_selector, + const db::ServerSelector& server_selector, + const boost::posix_time::ptime& lb_modification_time, + const db::AuditEntryCollection& audit_entries) = 0; + + /// @brief Returns the instance of the Config Backend Manager used by + /// this object. + /// + /// @return Reference to the CB Manager instance. + ConfigBackendMgrType& getMgr() const { + return (ConfigBackendMgrType::instance()); + } + + /// @brief Convenience method returning initial timestamp to set the + /// @c last_audit_revision_time_ to. + /// + /// @return Returns 2000-Jan-01 00:00:00 in local time. + static boost::posix_time::ptime getInitialAuditRevisionTime() { + static boost::posix_time::ptime + initial_time(boost::gregorian::date(2000, boost::gregorian::Jan, 1)); + return (initial_time); + } + + /// @brief Updates timestamp of the most recent audit entry fetched from + /// the database. + /// + /// If the collection of audit entries is empty, this method simply + /// returns without updating the timestamp. + /// + /// @param audit_entries reference to the collection of the fetched audit entries. + void updateLastAuditRevisionTimeId(const db::AuditEntryCollection& audit_entries) { + // Do nothing if there are no audit entries. It is the case if + // there were no updates to the configuration. + if (audit_entries.empty()) { + return; + } + + // Get the audit entries sorted by modification time and id, + // and pick the latest entry. + const auto& index = audit_entries.get<db::AuditEntryModificationTimeIdTag>(); + last_audit_revision_time_ = (*index.rbegin())->getModificationTime(); + last_audit_revision_id_ = (*index.rbegin())->getRevisionId(); + } + + /// @brief Stores the most recent audit revision timestamp. + boost::posix_time::ptime last_audit_revision_time_; + + /// @brief Stores the most recent audit revision identifier. + /// + /// The identifier is used when two (or more) audit entries have + /// the same timestamp. It is not used by itself because timestamps + /// are more user friendly. Unfortunately old versions of MySQL do not + /// support millisecond timestamps. + uint64_t last_audit_revision_id_; +}; + +/// @brief Checks if an object is in a collection od audit entries. +/// +/// @param audit_entries collection od audit entries to search for. +/// @param object_id object identifier. +inline bool +hasObjectId(const db::AuditEntryCollection& audit_entries, + const uint64_t& object_id) { + const auto& object_id_idx = audit_entries.get<db::AuditEntryObjectIdTag>(); + return (object_id_idx.count(object_id) > 0); +} + +} // end of namespace isc::process +} // end of namespace isc + +#endif /* CB_CTL_BASE_H */ diff --git a/src/lib/process/config_base.cc b/src/lib/process/config_base.cc new file mode 100644 index 0000000..fa9a8d8 --- /dev/null +++ b/src/lib/process/config_base.cc @@ -0,0 +1,137 @@ +// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <process/config_base.h> +#include <log/logger_manager.h> +#include <log/logger_specification.h> +#include <list> + +using namespace isc::log; +using namespace isc::data; + +namespace isc { +namespace process { + +void +ConfigBase::applyLoggingCfg() const { + + std::list<LoggerSpecification> specs; + for (LoggingInfoStorage::const_iterator it = logging_info_.begin(); + it != logging_info_.end(); ++it) { + specs.push_back(it->toSpec()); + } + LoggerManager manager; + manager.process(specs.begin(), specs.end()); +} + +bool +ConfigBase::equals(const ConfigBase& other) const { + // If number of loggers is different, then configurations aren't equal. + if (logging_info_.size() != other.logging_info_.size()) { + return (false); + } + // Pass through all loggers and try to find the match for each of them + // with the loggers from the other configuration. The order doesn't + // matter so we can't simply compare the vectors. + for (LoggingInfoStorage::const_iterator this_it = + logging_info_.begin(); this_it != logging_info_.end(); + ++this_it) { + bool match = false; + for (LoggingInfoStorage::const_iterator other_it = + other.logging_info_.begin(); + other_it != other.logging_info_.end(); ++other_it) { + if (this_it->equals(*other_it)) { + match = true; + break; + } + } + // No match found for the particular logger so return false. + if (!match) { + return (false); + } + } + + // Check config control info for equality. + if ((config_ctl_info_ && !other.config_ctl_info_) || + (!config_ctl_info_ && other.config_ctl_info_) || + ((config_ctl_info_ && other.config_ctl_info_) && + (!config_ctl_info_->equals(*(other.config_ctl_info_))))) { + return (false); + } + + return (true); +} + +void +ConfigBase::copy(ConfigBase& other) const { + // We will entirely replace loggers in the new configuration. + other.logging_info_.clear(); + for (LoggingInfoStorage::const_iterator it = logging_info_.begin(); + it != logging_info_.end(); ++it) { + other.addLoggingInfo(*it); + } + + // Clone the config control info + if (config_ctl_info_) { + other.config_ctl_info_.reset(new ConfigControlInfo(*config_ctl_info_)); + } else { + other.config_ctl_info_.reset(); + } + + // Clone server tag. + other.server_tag_ = server_tag_; +} + +void +ConfigBase::merge(ConfigBase& other) { + // Merge logging info. + if (!other.logging_info_.empty()) { + logging_info_ = other.logging_info_; + } + + // Merge the config control info + if (other.config_ctl_info_) { + if (config_ctl_info_) { + config_ctl_info_->merge(*other.config_ctl_info_); + } else { + config_ctl_info_ = other.config_ctl_info_; + } + } + + // Merge server tag. + if (!other.server_tag_.unspecified()) { + server_tag_ = other.server_tag_.get(); + } +} + +ElementPtr +ConfigBase::toElement() const { + ElementPtr result = Element::createMap(); + + // Was in the Logging global map. + if (!logging_info_.empty()) { + // Set loggers list + ElementPtr loggers = Element::createList(); + for (LoggingInfoStorage::const_iterator logger = + logging_info_.cbegin(); + logger != logging_info_.cend(); ++logger) { + loggers->add(logger->toElement()); + } + result->set("loggers", loggers); + } + + // server-tag + if (!server_tag_.unspecified()) { + result->set("server-tag", Element::create(server_tag_.get())); + } + + return (result); +} + +}; +}; diff --git a/src/lib/process/config_base.h b/src/lib/process/config_base.h new file mode 100644 index 0000000..1a99d4c --- /dev/null +++ b/src/lib/process/config_base.h @@ -0,0 +1,181 @@ +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CONFIG_BASE_H +#define CONFIG_BASE_H + +#include <cc/cfg_to_element.h> +#include <cc/user_context.h> +#include <process/config_ctl_info.h> +#include <process/logging_info.h> +#include <util/optional.h> +#include <boost/date_time/posix_time/posix_time.hpp> + +namespace isc { +namespace process { + +/// @brief Base class for all configurations +/// +/// This is a common base class that represents configurations. +/// SrvConfig, D2CfgContext, CtrlAgentCfgContext and possibly other +/// classes holding configurations are derived from this. +/// +/// It should contain only those elements that are applicable to really +/// every daemon we may have. Before adding anything here, please consider +/// whether it would be usable by all of the following: DHCP servers, +/// DDNS update daemon, Control Agent, Netconf daemon, DHCP relay, +/// DHCP client. +/// +/// This class currently holds information about common server configuration. +class ConfigBase : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @name Modifiers and accesors for the configuration objects. + /// + /// @warning References to the objects returned by accessors are only + /// valid during the lifetime of the @c ConfigBase object which + /// returned them. + /// + //@{ + /// @brief Returns logging specific configuration. + const process::LoggingInfoStorage& getLoggingInfo() const { + return (logging_info_); + } + + /// @brief Sets logging specific configuration. + /// + /// @param logging_info New logging configuration. + void addLoggingInfo(const process::LoggingInfo& logging_info) { + logging_info_.push_back(logging_info); + } + + /// @brief Apply logging configuration to log4cplus. + void applyLoggingCfg() const; + + /// @brief Compares two configuration. + /// + /// @param other the other configuration to compare to + bool equals(const ConfigBase& other) const; + + /// @brief Merges specified configuration into this configuration. + /// + /// This method merges logging and config control configuration into + /// this configuration. The new logging configuration replaces the + /// existing configuration if the new logging configuration is + /// non-empty. The new config control configuration replaces the + /// existing configuration if the new logging configuration is + /// non-null and non-empty. + /// + /// @warning The call to @c merge may modify the data in the @c other + /// object. Therefore, the caller must not rely on the data held + /// in the @c other object after the call to @c merge. Also, the + /// data held in @c other must not be modified after the call to + /// @c merge because it may affect the merged configuration. + /// + /// If a derivation of this class implements the @c merge method + /// it should call @c ConfigBase::merge. + /// + /// @param other the other configuration to be merged into this + /// configuration. + virtual void merge(ConfigBase& other); + + /// @brief Converts to Element representation + /// + /// This creates a Map element with the following content (expressed + /// as JSON): + /// {{{ + /// { + /// "Server": { + /// : + /// } + /// } + /// }}} + /// + /// Note that it will not contain the configuration control information + /// (i.e. "config-control"), as this is not a top-level element, rather + /// it belongs within the configured process element. + /// + /// @return Element representation. + virtual isc::data::ElementPtr toElement() const; + + /// @brief Fetches a read-only copy of the configuration control + /// information + /// @return pointer to the const ConfigControlInfo + process::ConstConfigControlInfoPtr getConfigControlInfo() const { + return (config_ctl_info_); + } + + /// @brief Set the configuration control information + /// + /// Updates the internal pointer to the configuration control + /// information with the given pointer value. If the given pointer + /// is empty, the internal pointer will be reset. + /// + /// @param config_ctl_info pointer to the configuration value + /// to store. + void setConfigControlInfo(const process::ConfigControlInfoPtr& + config_ctl_info) { + config_ctl_info_ = config_ctl_info; + } + + /// @brief Sets the server's logical name + /// + /// @param server_tag a unique string name which identifies this server + /// from any other configured servers + void setServerTag(const util::Optional<std::string>& server_tag) { + server_tag_ = server_tag; + } + + /// @brief Returns the server's logical name + /// + /// @return string containing the server's tag + util::Optional<std::string> getServerTag() const { + return (server_tag_); + } + + /// @brief Returns the last commit timestamp. + /// @return the last commit timestamp. + boost::posix_time::ptime getLastCommitTime() const { + return (last_commit_time_); + } + + /// @brief Sets the last commit timestamp. + /// @param last_commit_time last commit timestamp. + void setLastCommitTime(const boost::posix_time::ptime& last_commit_time) { + last_commit_time_ = last_commit_time; + } + +protected: + /// @brief Copies the current configuration to a new configuration. + /// + /// This method copies only the parameters defined in this class. + /// Since derived classes are expected to provide their own + /// copy methods, this one is protected and can be used only + /// by descendant classes. + /// + /// @param new_config this configuration will be copied to new_config + void copy(ConfigBase& new_config) const; + +private: + /// @brief Logging specific information. + process::LoggingInfoStorage logging_info_; + + /// @brief Configuration control information. + process::ConfigControlInfoPtr config_ctl_info_; + + /// @brief Logical name of the server + util::Optional<std::string> server_tag_; + + /// @brief Stores the last commit timestamp. + boost::posix_time::ptime last_commit_time_; +}; + +/// @brief Non-const pointer to the @c ConfigBase. +typedef boost::shared_ptr<ConfigBase> ConfigPtr; + +}; +}; + +#endif /* CONFIG_BASE_H */ diff --git a/src/lib/process/config_ctl_info.cc b/src/lib/process/config_ctl_info.cc new file mode 100644 index 0000000..4b1d9fa --- /dev/null +++ b/src/lib/process/config_ctl_info.cc @@ -0,0 +1,130 @@ +// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <process/config_ctl_info.h> + +using namespace isc::data; +using namespace isc::util; + +namespace isc { +namespace process { + +void +ConfigDbInfo::setAccessString(const std::string& access_str, bool test_mode) { + access_str_ = access_str; + access_params_.clear(); + if (!test_mode) { + access_params_ = db::DatabaseConnection::parse(access_str_); + } +} + +bool +ConfigDbInfo::equals(const ConfigDbInfo& other) const { + return (access_params_ == other.access_params_); +} + +isc::data::ElementPtr +ConfigDbInfo::toElement() const { + return (isc::db::DatabaseConnection::toElementDbAccessString(access_str_)); +} + +bool +ConfigDbInfo::getParameterValue(const std::string& name, std::string& value) const { + auto param = access_params_.find(name); + if (param == access_params_.end()) { + return(false); + } + + value = param->second; + return(true); +} + +//******** ConfigControlInfo ********// + +ConfigControlInfo::ConfigControlInfo(const ConfigControlInfo& other) + : config_fetch_wait_time_(other.config_fetch_wait_time_) { + for (auto db : other.db_infos_) { + addConfigDatabase(db.getAccessString()); + } +} + +void +ConfigControlInfo::addConfigDatabase(const std::string& access_str) { + ConfigDbInfo new_db; + new_db.setAccessString(access_str); + + for (auto db : db_infos_) { + if (new_db == db) { + // we have a duplicate! + isc_throw(BadValue, "database with access parameters: " + << access_str << " already exists"); + } + } + + db_infos_.push_back(new_db); +} + +const ConfigDbInfo& +ConfigControlInfo::findConfigDb(const std::string& param_name, + const std::string& param_value) { + for (ConfigDbInfoList::iterator db = db_infos_.begin(); + db != db_infos_.end(); ++db) { + std::string db_value; + if (db->getParameterValue(param_name, db_value) && + (param_value == db_value)) { + return (*db); + } + } + + return (EMPTY_DB()); +} + +const ConfigDbInfo& +ConfigControlInfo::EMPTY_DB() { + static ConfigDbInfo empty; + return (empty); +} + +void +ConfigControlInfo::clear() { + db_infos_.clear(); + config_fetch_wait_time_ = Optional<uint16_t>(30, true); +} + +void +ConfigControlInfo::merge(const ConfigControlInfo& other) { + if (!other.db_infos_.empty()) { + db_infos_ = other.db_infos_; + } +} + +ElementPtr +ConfigControlInfo::toElement() const { + ElementPtr result = Element::createMap(); + ElementPtr db_list = Element::createList(); + for (auto db_info : db_infos_) { + db_list->add(db_info.toElement()); + } + + result->set("config-databases", db_list); + + if (!config_fetch_wait_time_.unspecified()) { + result->set("config-fetch-wait-time", + Element::create(static_cast<int>(config_fetch_wait_time_))); + } + + return(result); +} + +bool +ConfigControlInfo::equals(const ConfigControlInfo& other) const { + return ((db_infos_ == other.db_infos_) && + (config_fetch_wait_time_ == other.config_fetch_wait_time_)); +} + +} // end of namespace isc::process +} // end of namespace isc diff --git a/src/lib/process/config_ctl_info.h b/src/lib/process/config_ctl_info.h new file mode 100644 index 0000000..2c53d59 --- /dev/null +++ b/src/lib/process/config_ctl_info.h @@ -0,0 +1,247 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef PROCESS_CONFIG_CTL_INFO_H +#define PROCESS_CONFIG_CTL_INFO_H + +#include <cc/cfg_to_element.h> +#include <database/database_connection.h> +#include <util/optional.h> + +#include <boost/shared_ptr.hpp> +#include <stdint.h> +#include <vector> + +namespace isc { +namespace process { + +/// @brief Provides configuration information used during a server's +/// configuration process. +/// +class ConfigDbInfo : public isc::data::CfgToElement { +public: + /// @brief Constructor + ConfigDbInfo() {}; + + /// @brief Set the access string. + /// + /// Sets the db's access string to the given value and then parses it + /// into name-value pairs and storing them internally as a + /// DatabaseConnection::ParameterMap. It discards the existing content + /// of the map first. It does not validate the parameter names of values, + /// ensuring the validity of the string content is placed upon the caller. + /// + /// @param access_str string of name=value pairs separated by spaces. + /// @param test_mode flag used in unittests only to skip parsing the access + /// string and storing the parameters. + void setAccessString(const std::string& access_str, bool test_mode = false); + + /// @brief Retrieves the database access string. + /// + /// @return database access string. + std::string getAccessString() const { + return (access_str_); + } + + /// @brief Retrieves the database access string with password redacted. + /// + /// @return database access string with password redacted. + std::string redactedAccessString() const { + return(db::DatabaseConnection::redactedAccessString(access_params_)); + } + + /// @brief Retrieve the map of parameter values. + /// + /// @return Constant reference to the database's parameter map. + const db::DatabaseConnection::ParameterMap& getParameters() const { + return (access_params_); + } + + /// @brief Fetch the value of a given parameter. + /// + /// @param name name of the parameter value to fetch. + /// @param[out] value string which will contain the value of the + /// parameter (if found). + /// + /// @return Boolean true if the parameter named is found in the map, + /// false otherwise. + bool getParameterValue(const std::string& name, + std::string& value) const; + + /// @brief Unparse a configuration object. + /// + /// @return a pointer to unparsed configuration. + virtual isc::data::ElementPtr toElement() const; + + /// @brief Compares two objects for equality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are equal, false otherwise. + bool equals(const ConfigDbInfo& other) const; + + /// @brief Compares two objects for equality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are equal, false otherwise. + bool operator==(const ConfigDbInfo& other) const { + return (equals(other)); + } + + /// @brief Compares two objects for inequality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are not equal, false otherwise. + bool operator!=(const ConfigDbInfo& other) const { + return (!equals(other)); + } + +private: + /// @brief Access string of parameters used to access this database. + std::string access_str_; + + /// @brief Map of the access parameters and their values. + db::DatabaseConnection::ParameterMap access_params_; +}; + +typedef std::vector<ConfigDbInfo> ConfigDbInfoList; + +/// @brief Embodies configuration information used during a server's +/// configuration process. +/// +/// This is class conveys the configuration control information +/// described by the following JSON text: +/// +/// @code +/// "config-control" : +/// { +/// "config-databases": +/// [ +/// { +/// # first config db +/// # common database access parameters +/// "type": <"mysql"|"postgresql">, +/// "name": <"db name">, +/// "host": <"db host name">, +/// : +/// }, +/// { +/// # next config db +/// } +/// ] +/// +/// } +/// @endcode +/// +class ConfigControlInfo : public isc::data::CfgToElement { +public: + + /// @brief Constructor. + ConfigControlInfo() + : config_fetch_wait_time_(30, true) {}; + + /// @brief Copy Constructor. + ConfigControlInfo(const ConfigControlInfo& other); + + /// @brief Sets new value of the config-fetch-wait-time. + /// + /// @param config_fetch_wait_time New value of the parameter which + /// specifies a time period in seconds between the attempts to + /// fetch the server configuration updates. The value of 0 disables + /// the periodic attempts to fetch the updates. + void setConfigFetchWaitTime(const util::Optional<uint16_t>& config_fetch_wait_time) { + config_fetch_wait_time_ = config_fetch_wait_time; + } + + /// @brief Returns configured config-fetch-wait-time value. + /// + /// This value specifies the time period in seconds between the + /// attempts to fetch the server configuration updates via the + /// configuration backends. The value of 0 means that the + /// mechanism to periodically fetch the configuration updates + /// is disabled. + /// + /// @return Time period between the subsequent attempts to + /// fetch server configuration updates in seconds. + const util::Optional<uint16_t>& getConfigFetchWaitTime() const { + return (config_fetch_wait_time_); + } + + /// @brief Sets configuration database access string. + /// + /// @param access_str database access string. + /// + /// @throw BadValue if an entry exists that matches the parameters + /// in the given access string, or if the access string is invalid. + void addConfigDatabase(const std::string& access_str); + + /// @brief Retrieves the list of databases. + /// + /// The entries in the list are stored in the order they were + /// added to it (FIFO). + /// + /// @return a reference to a const list of databases. + const ConfigDbInfoList& getConfigDatabases() const { + return (db_infos_); + } + + /// @brief Retrieves the database with the given access parameter value. + /// + /// @return A reference to the matching database or the not-found value + /// available via @c EMPTY_DB(). + const ConfigDbInfo& findConfigDb(const std::string& param_name, + const std::string& param_value); + + /// @brief Empties the contents of the class, including the database list. + void clear(); + + /// @brief Merges specified configuration into this configuration. + /// + /// If the other configuration is non-empty it completely replaces + /// this configuration. + /// + /// @param other the other configuration to be merged into this + /// configuration. + void merge(const ConfigControlInfo& other); + + /// @brief Unparse a configuration object. + /// + /// @return a pointer to unparsed configuration. + virtual isc::data::ElementPtr toElement() const; + + /// @brief Fetches the not-found value returned by database list searches. + /// + /// @return a reference to the empty ConfigDBInfo. + static const ConfigDbInfo& EMPTY_DB(); + + /// @brief Compares two objects for equality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are equal, false otherwise. + bool equals(const ConfigControlInfo& other) const; + +private: + + /// @brief Configured value of the config-fetch-wait-time. + util::Optional<uint16_t> config_fetch_wait_time_; + + /// @brief List of configuration databases. + ConfigDbInfoList db_infos_; +}; + +/// @brief Defines a pointer to a ConfigControlInfo. +typedef boost::shared_ptr<ConfigControlInfo> ConfigControlInfoPtr; + +/// @brief Defines a pointer to a const ConfigControlInfo. +typedef boost::shared_ptr<const ConfigControlInfo> ConstConfigControlInfoPtr; + +} // namespace process +} // end namespace isc + +#endif // PROCESS_CONFIG_CTL_INFO_H diff --git a/src/lib/process/config_ctl_parser.cc b/src/lib/process/config_ctl_parser.cc new file mode 100644 index 0000000..38f7ac0 --- /dev/null +++ b/src/lib/process/config_ctl_parser.cc @@ -0,0 +1,66 @@ +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/dhcp_config_error.h> +#include <process/config_ctl_parser.h> +#include <database/dbaccess_parser.h> +#include <cstdint> +#include <string> + +using namespace isc; +using namespace isc::data; + +namespace isc { +namespace process { + +ConfigControlInfoPtr +ConfigControlParser::parse(const data::ConstElementPtr& config_control) { + ConfigControlInfoPtr ctl_info(new ConfigControlInfo()); + + try { + if (config_control->contains("config-databases")) { + + auto elem = config_control->get("config-databases"); + if (elem->getType() != Element::list) { + isc_throw (ConfigError, "config-databases must be a list (" + << elem->getPosition() << ")"); + } + + const std::vector<data::ElementPtr>& db_list = elem->listValue(); + for (auto db = db_list.cbegin(); db != db_list.end(); ++db) { + db::DbAccessParser parser; + std::string access_string; + parser.parse(access_string, *db); + /// @todo do we still need access_string for this at all? + /// can't we just use params directly and get rid of the + /// string now that DatabaseConnection::toElement(map) exists? + ctl_info->addConfigDatabase(access_string); + } + } + + if (config_control->contains("config-fetch-wait-time")) { + auto config_fetch_wait_time = getInteger(config_control, + "config-fetch-wait-time", + 0, 65535); + ctl_info->setConfigFetchWaitTime(static_cast<uint16_t>(config_fetch_wait_time)); + } + + } catch (const isc::ConfigError&) { + // Position was already added + throw; + } catch (const std::exception& ex) { + isc_throw(ConfigError, ex.what() << " (" + << config_control->getPosition() << ")"); + } + + return (ctl_info); +} + +} // end of namespace isc::process +} // end of namespace isc + diff --git a/src/lib/process/config_ctl_parser.h b/src/lib/process/config_ctl_parser.h new file mode 100644 index 0000000..4182a19 --- /dev/null +++ b/src/lib/process/config_ctl_parser.h @@ -0,0 +1,35 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef PROCESS_CONFIG_CONTROL_PARSER_H +#define PROCESS_CONFIG_CONTROL_PARSER_H + +#include <cc/data.h> +#include <cc/simple_parser.h> +#include <process/config_ctl_info.h> + +namespace isc { +namespace process { + +/// @brief Implements parser for config control information, "config-control" +class ConfigControlParser : isc::data::SimpleParser { +public: + + /// @brief Parses a configuration control Element + /// + /// @param config_control Element holding the config control content + /// to be parsed. + /// + /// @return Pointer to newly created ConfigControlInfo instance + /// @throw DhcpConfigError when config control content is invalid. + ConfigControlInfoPtr + parse(const data::ConstElementPtr& config_control); +}; + +} // enf of namespace isc::process +} // end of namespace isc + +#endif // PROCESS_CONFIG_CONTROL_PARSER_H diff --git a/src/lib/process/d_cfg_mgr.cc b/src/lib/process/d_cfg_mgr.cc new file mode 100644 index 0000000..4a67183 --- /dev/null +++ b/src/lib/process/d_cfg_mgr.cc @@ -0,0 +1,163 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/command_interpreter.h> +#include <dhcp/libdhcp++.h> +#include <process/d_log.h> +#include <process/d_cfg_mgr.h> +#include <process/daemon.h> +#include <process/redact_config.h> +#include <util/encode/hex.h> +#include <util/strutil.h> + +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string.hpp> + +#include <limits> +#include <iostream> +#include <vector> +#include <map> + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::data; +using namespace isc::asiolink; + +namespace isc { +namespace process { + +// *********************** DCfgMgrBase ************************* + +DCfgMgrBase::DCfgMgrBase(ConfigPtr context) { + setContext(context); +} + +DCfgMgrBase::~DCfgMgrBase() { +} + +void +DCfgMgrBase::resetContext() { + ConfigPtr context = createNewContext(); + setContext(context); +} + +void +DCfgMgrBase::setContext(ConfigPtr& context) { + if (!context) { + isc_throw(DCfgMgrBaseError, "DCfgMgrBase: context cannot be NULL"); + } + + context_ = context; +} + +ConstElementPtr +DCfgMgrBase::redactConfig(ConstElementPtr const& config) const { + ConstElementPtr result(config); + for (std::list<std::string>& json_path : jsonPathsToRedact()) { + result = isc::process::redactConfig(result, json_path); + } + return result; +} + +list<list<string>> DCfgMgrBase::jsonPathsToRedact() const { + static list<list<string>> const list; + return list; +} + +isc::data::ConstElementPtr +DCfgMgrBase::simpleParseConfig(isc::data::ConstElementPtr config_set, + bool check_only, + const std::function<void()>& post_config_cb) { + if (!config_set) { + return (isc::config::createAnswer(1, + std::string("Can't parse NULL config"))); + } + LOG_DEBUG(dctl_logger, isc::log::DBGLVL_COMMAND, DCTL_CONFIG_START) + .arg(redactConfig(config_set)->str()); + + // The parsers implement data inheritance by directly accessing + // configuration context. For this reason the data parsers must store + // the parsed data into context immediately. This may cause data + // inconsistency if the parsing operation fails after the context has been + // modified. We need to preserve the original context here + // so as we can rollback changes when an error occurs. + ConfigPtr original_context = context_; + resetContext(); + bool rollback = false; + + // Answer will hold the result returned to the caller. + ConstElementPtr answer; + + try { + // Logging is common so factor it. + Daemon::configureLogger(config_set, context_); + + // Let's call the actual implementation + answer = parse(config_set, check_only); + + // and check the response returned. + int code = 0; + isc::config::parseAnswer(code, answer); + + // Everything was fine. Configuration set processed successfully. + if (!check_only) { + if (code == 0) { + // Call the callback only when parsing was successful. + if (post_config_cb) { + post_config_cb(); + } + LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0)); + // Set the last commit timestamp. + auto now = boost::posix_time::second_clock::universal_time(); + context_->setLastCommitTime(now); + } else { + rollback = true; + } + + // Use the answer provided. + //answer = isc::config::createAnswer(0, "Configuration committed."); + } else { + LOG_INFO(dctl_logger, DCTL_CONFIG_CHECK_COMPLETE) + .arg(getConfigSummary(0)) + .arg(config::answerToText(answer)); + } + + } catch (const std::exception& ex) { + LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what()); + answer = isc::config::createAnswer(1, ex.what()); + rollback = true; + } + + if (check_only) { + // If this is a configuration check only, then don't actually apply + // the configuration and reverse to the previous one. + context_ = original_context; + } + + if (rollback) { + // An error occurred, so make sure that we restore original context. + context_ = original_context; + } + + return (answer); +} + + +void +DCfgMgrBase::setCfgDefaults(isc::data::ElementPtr) { +} + +isc::data::ConstElementPtr +DCfgMgrBase::parse(isc::data::ConstElementPtr, bool) { + isc_throw(DCfgMgrBaseError, "This class does not implement simple parser paradigm yet"); +} + +} // end of isc::dhcp namespace +} // end of isc namespace diff --git a/src/lib/process/d_cfg_mgr.h b/src/lib/process/d_cfg_mgr.h new file mode 100644 index 0000000..204f7b2 --- /dev/null +++ b/src/lib/process/d_cfg_mgr.h @@ -0,0 +1,252 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D_CFG_MGR_H +#define D_CFG_MGR_H + +#include <cc/data.h> +#include <cc/cfg_to_element.h> +#include <cc/user_context.h> +#include <process/config_base.h> +#include <exceptions/exceptions.h> + +#include <stdint.h> + +#include <functional> +#include <list> +#include <string> + +namespace isc { +namespace process { + +/// @brief Defines a map of ConstElementPtrs keyed by name +typedef std::map<std::string, isc::data::ConstElementPtr> ElementMap; + +/// @brief Exception thrown if the configuration manager encounters an error. +class DCfgMgrBaseError : public isc::Exception { +public: + DCfgMgrBaseError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Configuration Manager +/// +/// DCfgMgrBase is an abstract class that provides the mechanisms for managing +/// an application's configuration. This includes services for parsing sets of +/// configuration values, storing the parsed information in its converted form, +/// and retrieving the information on demand. It is intended to be the worker +/// class which is handed a set of configuration values to process by upper +/// application management layers. +/// +/// This class allows two configuration methods: +/// +/// 1. classic method +/// +/// The class presents a public method for receiving new configurations, +/// parseConfig. This method coordinates the parsing effort as follows: +/// +/// @code +/// make backup copy of configuration context +/// Split top-level configuration elements into to sets: +/// 1. Set of scalar elements (strings, booleans, ints, etc..) +/// 2. Set of object elements (maps, lists, etc...) +/// For each entry in the scalar set: +/// get derivation-specific parser for element +/// run parser +/// update context with parsed results +/// break on error +/// +/// For each entry in the object set; +/// get derivation-specific parser for element +/// run parser +/// update context with parsed results +/// break on error +/// +/// if an error occurred or this is only a check +/// restore configuration context from backup +/// @endcode +/// +/// The above structuring ensures that global parameters are parsed first +/// making them available during subsequent object element parsing. The order +/// in which the object elements are processed is either: +/// +/// 1. Natural order presented by the configuration set +/// 2. Specific order determined by a list of element ids +/// +/// This allows a derivation to specify the order in which its elements are +/// parsed if there are dependencies between elements. +/// +/// To parse a given element, its id along with the element itself, +/// is passed into the virtual method, @c parseElement. Derivations are +/// expected to converts the element into application specific object(s), +/// thereby isolating the CPL from application details. +/// +/// In the event that an error occurs, parsing is halted and the +/// configuration context is restored from backup. +/// +/// See @ref isc::d2::D2CfgMgr and @ref isc::d2::D2Process for example use of +/// this approach. +/// +/// 2. simple configuration method +/// +/// This approach assumes usage of @ref isc::data::SimpleParser paradigm. It +/// does not use any intermediate storage, does not use parser pointers, does +/// not enforce parsing order. +/// +/// Here's the expected control flow order: +/// 1. implementation calls simpleParseConfig from its configure method. +/// 2. simpleParseConfig makes a configuration context +/// 3. parse method from the derived class is called +/// 4. if the configuration was unsuccessful or this is only a check, the +/// old context is reinstantiated. If not, the configuration is kept. +/// +/// See @ref isc::agent::CtrlAgentCfgMgr and @ref isc::agent::CtrlAgentProcess +/// for example use of this approach. +class DCfgMgrBase { +public: + /// @brief Constructor + /// + /// @param context is a pointer to the configuration context the manager + /// will use for storing parsed results. + /// + /// @throw throws DCfgMgrBaseError if context is null + DCfgMgrBase(ConfigPtr context); + + /// @brief Destructor + virtual ~DCfgMgrBase(); + + /// @brief Acts as the receiver of new configurations. + /// + /// This method is similar to what parseConfig did, execept it employs + /// the simple parser paradigm: no intermediate storage, no parser pointers + /// no distinction between params_map and objects_map, parse order (if needed) + /// can be enforced in the actual implementation by calling specific + /// parsers first. See @ref isc::agent::CtrlAgentCfgMgr::parse for example. + /// + /// If check_only is true, the actual parsing is done to check if the configuration + /// is sane, but is then reverted. + /// + /// @param config set of configuration elements to be parsed + /// @param check_only true if the config is to be checked only, but not applied + /// @param post_config_cb Callback to be executed after the usual parsing stage. + /// This can be specified as a C++ lambda which configures other parts of the + /// system based on the parsed configuration information. The callback should + /// throw an exception to signal an error. This method will catch this + /// exception and place an exception string within the result returned. + /// + /// @return an Element that contains the results of configuration composed + /// of an integer status value (0 means successful, non-zero means failure), + /// and a string explanation of the outcome. + isc::data::ConstElementPtr + simpleParseConfig(isc::data::ConstElementPtr config, + bool check_only = false, + const std::function<void()>& post_config_cb = nullptr); + + /// @brief Fetches the configuration context. + /// + /// @return returns a pointer reference to the configuration context. + ConfigPtr& getContext() { + return (context_); + } + + /// @brief Returns configuration summary in the textual format. + /// + /// This method returns the brief text describing the current configuration. + /// It may be used for logging purposes, e.g. whn the new configuration is + /// committed to notify a user about the changes in configuration. + /// + /// @param selection Bitfield which describes the parts of the configuration + /// to be returned. + /// + /// @return Summary of the configuration in the textual format. + virtual std::string getConfigSummary(const uint32_t selection) = 0; + + /// @brief Redact the configuration. + /// + /// This method replaces passwords and secrets by asterisks. By + /// default it follows all subtrees at the exception of user + /// contexts. Please derive the method to allow a reasonable + /// performance by following only subtrees where the syntax allows + /// the presence of passwords and secrets. + /// + /// @param config the Element tree structure that describes the configuration. + /// @return unmodified config or a copy of the config where passwords were + /// replaced by asterisks so can be safely logged to an unprivileged place. + isc::data::ConstElementPtr + redactConfig(isc::data::ConstElementPtr const& config) const; + +protected: + /// @brief Adds default values to the given config + /// + /// Provides derivations a means to add defaults to a configuration + /// Element map prior to parsing it. + /// + /// @param mutable_config - configuration to which defaults should be added + virtual void setCfgDefaults(isc::data::ElementPtr mutable_config); + + /// @brief Abstract factory which creates a context instance. + /// + /// This method is used at the beginning of configuration process to + /// create a fresh, empty copy of the derivation-specific context. This + /// new context will be populated during the configuration process + /// and will replace the existing context provided the configuration + /// process completes without error. + /// + /// @return Returns a ConfigPtr to the new context instance. + virtual ConfigPtr createNewContext() = 0; + + /// @brief Replaces existing context with a new, empty context. + void resetContext(); + + /// @brief Update the current context. + /// + /// Replaces the existing context with the given context. + /// @param context Pointer to the new context. + /// @throw DCfgMgrBaseError if context is NULL. + void setContext(ConfigPtr& context); + + /// @brief Parses actual configuration. + /// + /// This method is expected to be implemented in derived classes that employ + /// SimpleParser paradigm (i.e. they call simpleParseConfig rather than + /// parseConfig from their configure method). + /// + /// Implementations that do not employ this method may provide dummy + /// implementation. + /// + /// @param config the Element tree structure that describes the configuration. + /// @param check_only false for normal configuration, true when verifying only + /// + /// @return an Element that contains the results of configuration composed + /// of an integer status value (0 means successful, non-zero means failure), + /// and a string explanation of the outcome. + virtual isc::data::ConstElementPtr parse(isc::data::ConstElementPtr config, + bool check_only); + + /// @brief Return a list of all paths that contain passwords or secrets. + /// + /// Used in @ref isc::process::Daemon::redactConfig to only make copies and + /// only redact configuration subtrees that contain passwords or secrets. + /// + /// This method needs to be overridden in each process that has a distinct + /// configuration structure. + /// + /// @return the list of lists of sequential JSON map keys needed to reach + /// the passwords and secrets. + virtual std::list<std::list<std::string>> jsonPathsToRedact() const; + +private: + /// @brief Pointer to the configuration context instance. + ConfigPtr context_; +}; + +/// @brief Defines a shared pointer to DCfgMgrBase. +typedef boost::shared_ptr<DCfgMgrBase> DCfgMgrBasePtr; + +} // end of isc::process namespace +} // end of isc namespace + +#endif // D_CFG_MGR_H diff --git a/src/lib/process/d_controller.cc b/src/lib/process/d_controller.cc new file mode 100644 index 0000000..4a2062c --- /dev/null +++ b/src/lib/process/d_controller.cc @@ -0,0 +1,845 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <cc/command_interpreter.h> +#include <cfgrpt/config_report.h> +#include <exceptions/exceptions.h> +#include <hooks/hooks_manager.h> +#include <log/logger.h> +#include <log/logger_support.h> +#include <process/daemon.h> +#include <process/d_log.h> +#include <process/d_controller.h> +#include <process/config_base.h> +#include <kea_version.h> +#include <functional> +#include <sstream> +#include <unistd.h> +#include <signal.h> + +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::config; +using namespace isc::hooks; +namespace ph = std::placeholders; + +namespace isc { +namespace process { + +DControllerBasePtr DControllerBase::controller_; + +// Note that the constructor instantiates the controller's primary IOService. +DControllerBase::DControllerBase(const char* app_name, const char* bin_name) + : app_name_(app_name), bin_name_(bin_name), + verbose_(false), check_only_(false), + io_service_(new isc::asiolink::IOService()), + io_signal_set_() { +} + +void +DControllerBase::setController(const DControllerBasePtr& controller) { + if (controller_) { + // This shouldn't happen, but let's make sure it can't be done. + // It represents a programmatic error. + isc_throw (DControllerBaseError, + "Multiple controller instances attempted."); + } + + controller_ = controller; +} + +ConstElementPtr +DControllerBase::parseFile(const std::string&) { + ConstElementPtr elements; + return (elements); +} + +int +DControllerBase::launch(int argc, char* argv[], const bool test_mode) { + + // Step 1 is to parse the command line arguments. + try { + parseArgs(argc, argv); + } catch (const InvalidUsage& ex) { + usage(ex.what()); + // rethrow it with an empty message + isc_throw(InvalidUsage, ""); + } + + setProcName(bin_name_); + + if (isCheckOnly()) { + checkConfigOnly(); + return (EXIT_SUCCESS); + } + + // It is important that we set a default logger name because this name + // will be used when the user doesn't provide the logging configuration + // in the Kea configuration file. + Daemon::setDefaultLoggerName(bin_name_); + + // Logger's default configuration depends on whether we are in the + // verbose mode or not. CfgMgr manages the logger configuration so + // the verbose mode is set for CfgMgr. + Daemon::setVerbose(verbose_); + + // Do not initialize logger here if we are running unit tests. It would + // replace an instance of unit test specific logger. + if (!test_mode) { + // Now that we know what the mode flags are, we can init logging. + Daemon::loggerInit(bin_name_.c_str(), verbose_); + } + + try { + checkConfigFile(); + } catch (const std::exception& ex) { + LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL) + .arg(app_name_).arg(ex.what()); + isc_throw (LaunchError, "Launch Failed: " << ex.what()); + } + + try { + createPIDFile(); + } catch (const DaemonPIDExists& ex) { + LOG_FATAL(dctl_logger, DCTL_ALREADY_RUNNING) + .arg(bin_name_).arg(ex.what()); + isc_throw (LaunchError, "Launch Failed: " << ex.what()); + } catch (const std::exception& ex) { + LOG_FATAL(dctl_logger, DCTL_PID_FILE_ERROR) + .arg(app_name_).arg(ex.what()); + isc_throw (LaunchError, "Launch failed: " << ex.what()); + } + + // Log the starting of the service. + LOG_INFO(dctl_logger, DCTL_STARTING) + .arg(app_name_) + .arg(getpid()) + .arg(VERSION) + .arg(PACKAGE_VERSION_TYPE); + // When it is not a stable version dissuade use in production. + if (std::string(PACKAGE_VERSION_TYPE) == "development") { + LOG_WARN(dctl_logger, DCTL_DEVELOPMENT_VERSION); + } + try { + // Step 2 is to create and initialize the application process object. + initProcess(); + } catch (const std::exception& ex) { + LOG_FATAL(dctl_logger, DCTL_INIT_PROCESS_FAIL) + .arg(app_name_).arg(ex.what()); + isc_throw (ProcessInitError, + "Application Process initialization failed: " << ex.what()); + } + + LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_STANDALONE) + .arg(app_name_); + + // Step 3 is to load configuration from file. + int rcode; + ConstElementPtr comment = parseAnswer(rcode, configFromFile()); + if (rcode != 0) { + LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL) + .arg(app_name_).arg(comment->stringValue()); + isc_throw (ProcessInitError, "Could Not load configuration file: " + << comment->stringValue()); + } + + // Note that the controller was started. + start_ = boost::posix_time::second_clock::universal_time(); + + // Everything is clear for launch, so start the application's + // event loop. + try { + // Now that we have a process, we can set up signal handling. + initSignalHandling(); + runProcess(); + } catch (const std::exception& ex) { + LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED) + .arg(app_name_).arg(ex.what()); + isc_throw (ProcessRunError, + "Application process event loop failed: " << ex.what()); + } + + // All done, so bail out. + LOG_INFO(dctl_logger, DCTL_SHUTDOWN) + .arg(app_name_).arg(getpid()).arg(VERSION); + + return (getExitValue()); +} + +void +DControllerBase::checkConfigOnly() { + try { + // We need to initialize logging, in case any error + // messages are to be printed. + // This is just a test, so we don't care about lockfile. + setenv("KEA_LOCKFILE_DIR", "none", 0); + Daemon::setDefaultLoggerName(bin_name_); + Daemon::setVerbose(verbose_); + Daemon::loggerInit(bin_name_.c_str(), verbose_); + + // Check the syntax first. + std::string config_file = getConfigFile(); + if (config_file.empty()) { + // Basic sanity check: file name must not be empty. + isc_throw(InvalidUsage, "JSON configuration file not specified"); + } + ConstElementPtr whole_config = parseFile(config_file); + if (!whole_config) { + // No fallback to fromJSONFile + isc_throw(InvalidUsage, "No configuration found"); + } + if (verbose_) { + std::cerr << "Syntax check OK" << std::endl; + } + + // Check the logic next. + ConstElementPtr module_config; + module_config = whole_config->get(getAppName()); + if (!module_config) { + isc_throw(InvalidUsage, "Config file " << config_file << + " does not include '" << getAppName() << "' entry"); + } + if (module_config->getType() != Element::map) { + isc_throw(InvalidUsage, "Config file " << config_file << + " includes not map '" << getAppName() << "' entry"); + } + + // Handle other (i.e. not application name) objects. + std::string errmsg = handleOtherObjects(whole_config); + if (!errmsg.empty()) { + isc_throw(InvalidUsage, "Config file " << config_file << errmsg); + } + + // Get an application process object. + initProcess(); + + ConstElementPtr answer = checkConfig(module_config); + int rcode = 0; + answer = parseAnswer(rcode, answer); + if (rcode != 0) { + isc_throw(InvalidUsage, "Error encountered: " + << answer->stringValue()); + } + } catch (const VersionMessage&) { + throw; + } catch (const InvalidUsage&) { + throw; + } catch (const std::exception& ex) { + isc_throw(InvalidUsage, "Syntax check failed with: " << ex.what()); + } + return; +} + +void +DControllerBase::parseArgs(int argc, char* argv[]) { + + if (argc == 1) { + isc_throw(InvalidUsage, ""); + } + + // Iterate over the given command line options. If its a stock option + // ("c" or "d") handle it here. If its a valid custom option, then + // invoke customOption. + int ch; + opterr = 0; + optind = 1; + std::string opts("dvVWc:t:" + getCustomOpts()); + while ((ch = getopt(argc, argv, opts.c_str())) != -1) { + switch (ch) { + case 'd': + // Enables verbose logging. + verbose_ = true; + break; + + case 'v': + // gather Kea version and throw so main() can catch and return + // rather than calling exit() here which disrupts gtest. + isc_throw(VersionMessage, getVersion(false)); + break; + + case 'V': + // gather Kea version and throw so main() can catch and return + // rather than calling exit() here which disrupts gtest. + isc_throw(VersionMessage, getVersion(true)); + break; + + case 'W': + // gather Kea config report and throw so main() can catch and + // return rather than calling exit() here which disrupts gtest. + isc_throw(VersionMessage, isc::detail::getConfigReport()); + break; + + case 'c': + case 't': + // config file name + if (optarg == NULL) { + isc_throw(InvalidUsage, "configuration file name missing"); + } + + setConfigFile(optarg); + + if (ch == 't') { + check_only_ = true; + } + break; + + case '?': { + // We hit an invalid option. + isc_throw(InvalidUsage, "unsupported option: [" + << static_cast<char>(optopt) << "] " + << (!optarg ? "" : optarg)); + + break; + } + + default: + // We hit a valid custom option + if (!customOption(ch, optarg)) { + // This would be a programmatic error. + isc_throw(InvalidUsage, " Option listed but implemented?: [" + << static_cast<char>(ch) << "] " + << (!optarg ? "" : optarg)); + } + break; + } + } + + // There was too much information on the command line. + if (argc > optind) { + isc_throw(InvalidUsage, "extraneous command line information"); + } +} + +bool +DControllerBase::customOption(int /* option */, char* /*optarg*/) { + // Default implementation returns false. + return (false); +} + +void +DControllerBase::initProcess() { + LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_INIT_PROCESS) + .arg(app_name_); + + // Invoke virtual method to instantiate the application process. + try { + process_.reset(createProcess()); + } catch (const std::exception& ex) { + isc_throw(DControllerBaseError, std::string("createProcess failed: ") + + ex.what()); + } + + // This is pretty unlikely, but will test for it just to be safe.. + if (!process_) { + isc_throw(DControllerBaseError, "createProcess returned NULL"); + } + + // Invoke application's init method (Note this call should throw + // DProcessBaseError if it fails). + process_->init(); +} + +ConstElementPtr +DControllerBase::configFromFile() { + // Rollback any previous staging configuration. For D2, only a + // logger configuration is used here. + // We're not using cfgmgr to store logging configuration anymore. + // isc::dhcp::CfgMgr::instance().rollback(); + + // Will hold configuration. + ConstElementPtr module_config; + // Will receive configuration result. + ConstElementPtr answer; + try { + std::string config_file = getConfigFile(); + if (config_file.empty()) { + // Basic sanity check: file name must not be empty. + isc_throw(BadValue, "JSON configuration file not specified. Please " + "use -c command line option."); + } + + // If parseFile returns an empty pointer, then pass the file onto the + // original JSON parser. + ConstElementPtr whole_config = parseFile(config_file); + if (!whole_config) { + // Read contents of the file and parse it as JSON + whole_config = Element::fromJSONFile(config_file, true); + } + + // Extract derivation-specific portion of the configuration. + module_config = whole_config->get(getAppName()); + if (!module_config) { + isc_throw(BadValue, "Config file " << config_file << + " does not include '" << + getAppName() << "' entry."); + } + if (module_config->getType() != Element::map) { + isc_throw(InvalidUsage, "Config file " << config_file << + " includes not map '" << getAppName() << "' entry"); + } + + // Handle other (i.e. not application name) objects. + std::string errmsg = handleOtherObjects(whole_config); + if (!errmsg.empty()) { + isc_throw(InvalidUsage, "Config file " << config_file << errmsg); + } + + // Let's configure logging before applying the configuration, + // so we can log things during configuration process. + + // Temporary storage for logging configuration + ConfigPtr storage(new ConfigBase()); + + // Configure logging to the temporary storage. + Daemon::configureLogger(module_config, storage); + + // Let's apply the new logging. We do it early, so we'll be able + // to print out what exactly is wrong with the new config in + // case of problems. + storage->applyLoggingCfg(); + + answer = updateConfig(module_config); + // In all cases the right logging configuration is in the context. + process_->getCfgMgr()->getContext()->applyLoggingCfg(); + } catch (const std::exception& ex) { + // Rollback logging configuration. + process_->getCfgMgr()->getContext()->applyLoggingCfg(); + + // build an error result + ConstElementPtr error = createAnswer(COMMAND_ERROR, + std::string("Configuration parsing failed: ") + ex.what()); + return (error); + } + + return (answer); +} + +void +DControllerBase::runProcess() { + LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_RUN_PROCESS) + .arg(app_name_); + if (!process_) { + // This should not be possible. + isc_throw(DControllerBaseError, "Process not initialized"); + } + + // Invoke the application process's run method. This may throw + // DProcessBaseError + process_->run(); +} + +// Instance method for handling new config +ConstElementPtr +DControllerBase::updateConfig(ConstElementPtr new_config) { + return (process_->configure(new_config, false)); +} + +// Instance method for checking new config +ConstElementPtr +DControllerBase::checkConfig(ConstElementPtr new_config) { + return (process_->configure(new_config, true)); +} + +ConstElementPtr +DControllerBase::configGetHandler(const std::string&, + ConstElementPtr /*args*/) { + ConstElementPtr config = process_->getCfgMgr()->getContext()->toElement(); + + return (createAnswer(COMMAND_SUCCESS, config)); +} + +ConstElementPtr +DControllerBase::configWriteHandler(const std::string&, + ConstElementPtr args) { + std::string filename; + + if (args) { + if (args->getType() != Element::map) { + return (createAnswer(COMMAND_ERROR, "Argument must be a map")); + } + ConstElementPtr filename_param = args->get("filename"); + if (filename_param) { + if (filename_param->getType() != Element::string) { + return (createAnswer(COMMAND_ERROR, + "passed parameter 'filename' " + "is not a string")); + } + filename = filename_param->stringValue(); + } + } + + if (filename.empty()) { + // filename parameter was not specified, so let's use + // whatever we remember + filename = getConfigFile(); + if (filename.empty()) { + return (createAnswer(COMMAND_ERROR, + "Unable to determine filename." + "Please specify filename explicitly.")); + } + } + + + // Ok, it's time to write the file. + size_t size = 0; + ElementPtr cfg = process_->getCfgMgr()->getContext()->toElement(); + + try { + size = writeConfigFile(filename, cfg); + } catch (const isc::Exception& ex) { + return (createAnswer(COMMAND_ERROR, + std::string("Error during write-config:") + + ex.what())); + } + if (size == 0) { + return (createAnswer(COMMAND_ERROR, + "Error writing configuration to " + filename)); + } + + // Ok, it's time to return the successful response. + ElementPtr params = Element::createMap(); + params->set("size", Element::create(static_cast<long long>(size))); + params->set("filename", Element::create(filename)); + + return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to " + + filename + " successful", params)); +} + +std::string +DControllerBase::handleOtherObjects(ConstElementPtr args) { + // Check obsolete or unknown (aka unsupported) objects. + const std::string& app_name = getAppName(); + std::string errmsg; + for (auto obj : args->mapValue()) { + const std::string& obj_name = obj.first; + if (obj_name == app_name) { + continue; + } + LOG_ERROR(dctl_logger, DCTL_CONFIG_DEPRECATED) + .arg("'" + obj_name + "', defining anything in global level besides '" + + app_name + "' is no longer supported."); + if (errmsg.empty()) { + errmsg = " contains unsupported '" + obj_name + "' parameter"; + } else { + errmsg += " (and '" + obj_name + "')"; + } + } + return (errmsg); +} + +ConstElementPtr +DControllerBase::configTestHandler(const std::string&, ConstElementPtr args) { + const int status_code = COMMAND_ERROR; // 1 indicates an error + ConstElementPtr module_config; + std::string app_name = getAppName(); + std::string message; + + // Command arguments are expected to be: + // { "Module": { ... } } + if (!args) { + message = "Missing mandatory 'arguments' parameter."; + } else { + module_config = args->get(app_name); + if (!module_config) { + message = "Missing mandatory '" + app_name + "' parameter."; + } else if (module_config->getType() != Element::map) { + message = "'" + app_name + "' parameter expected to be a map."; + } + } + + if (message.empty()) { + // Handle other (i.e. not application name) objects. + std::string errmsg = handleOtherObjects(args); + if (!errmsg.empty()) { + message = "'arguments' parameter" + errmsg; + } + } + + if (!message.empty()) { + // Something is amiss with arguments, return a failure response. + ConstElementPtr result = isc::config::createAnswer(status_code, + message); + return (result); + } + + + // We are starting the configuration process so we should remove any + // staging configuration that has been created during previous + // configuration attempts. + // We're not using cfgmgr to store logging information anymore. + // isc::dhcp::CfgMgr::instance().rollback(); + + // Now we check the server proper. + return (checkConfig(module_config)); +} + +ConstElementPtr +DControllerBase::configReloadHandler(const std::string&, ConstElementPtr) { + // Add reload in message? + return (configFromFile()); +} + +ConstElementPtr +DControllerBase::configSetHandler(const std::string&, ConstElementPtr args) { + const int status_code = COMMAND_ERROR; // 1 indicates an error + ConstElementPtr module_config; + std::string app_name = getAppName(); + std::string message; + + // Command arguments are expected to be: + // { "Module": { ... } } + if (!args) { + message = "Missing mandatory 'arguments' parameter."; + } else { + module_config = args->get(app_name); + if (!module_config) { + message = "Missing mandatory '" + app_name + "' parameter."; + } else if (module_config->getType() != Element::map) { + message = "'" + app_name + "' parameter expected to be a map."; + } + } + + if (!message.empty()) { + // Something is amiss with arguments, return a failure response. + ConstElementPtr result = isc::config::createAnswer(status_code, + message); + return (result); + } + + try { + + // Handle other (i.e. not application name) objects. + handleOtherObjects(args); + + // We are starting the configuration process so we should remove any + // staging configuration that has been created during previous + // configuration attempts. + // We're not using cfgmgr to store logging information anymore. + // isc::dhcp::CfgMgr::instance().rollback(); + + // Temporary storage for logging configuration + ConfigPtr storage(new ConfigBase()); + + // Configure logging to the temporary storage. + Daemon::configureLogger(module_config, storage); + + // Let's apply the new logging. We do it early, so we'll be able + // to print out what exactly is wrong with the new config in + // case of problems. + storage->applyLoggingCfg(); + + ConstElementPtr answer = updateConfig(module_config); + int rcode = 0; + parseAnswer(rcode, answer); + // In all cases the right logging configuration is in the context. + process_->getCfgMgr()->getContext()->applyLoggingCfg(); + return (answer); + } catch (const std::exception& ex) { + // Rollback logging configuration. + process_->getCfgMgr()->getContext()->applyLoggingCfg(); + + // build an error result + ConstElementPtr error = createAnswer(COMMAND_ERROR, + std::string("Configuration parsing failed: ") + ex.what()); + return (error); + } +} + +ConstElementPtr +DControllerBase::serverTagGetHandler(const std::string&, ConstElementPtr) { + const std::string& tag = process_->getCfgMgr()->getContext()->getServerTag(); + ElementPtr response = Element::createMap(); + response->set("server-tag", Element::create(tag)); + + return (createAnswer(COMMAND_SUCCESS, response)); +} + +ConstElementPtr +DControllerBase::statusGetHandler(const std::string&, ConstElementPtr) { + ElementPtr status = Element::createMap(); + status->set("pid", Element::create(static_cast<int>(getpid()))); + + auto now = boost::posix_time::second_clock::universal_time(); + if (!start_.is_not_a_date_time()) { + auto uptime = now - start_; + status->set("uptime", Element::create(uptime.total_seconds())); + } + + auto last_commit = process_->getCfgMgr()->getContext()->getLastCommitTime(); + if (!last_commit.is_not_a_date_time()) { + auto reload = now - last_commit; + status->set("reload", Element::create(reload.total_seconds())); + } + + return (createAnswer(COMMAND_SUCCESS, status)); +} + +ConstElementPtr +DControllerBase::versionGetHandler(const std::string&, ConstElementPtr) { + ConstElementPtr answer; + + // For version-get put the extended version in arguments + ElementPtr extended = Element::create(getVersion(true)); + ElementPtr arguments = Element::createMap(); + arguments->set("extended", extended); + answer = createAnswer(COMMAND_SUCCESS, getVersion(false), arguments); + return (answer); +} + +ConstElementPtr +DControllerBase::buildReportHandler(const std::string&, ConstElementPtr) { + return (createAnswer(COMMAND_SUCCESS, isc::detail::getConfigReport())); +} + +ConstElementPtr +DControllerBase::shutdownHandler(const std::string&, ConstElementPtr args) { + // Shutdown is universal. If its not that, then try it as + // a custom command supported by the derivation. If that + // doesn't pan out either, than send to it the application + // as it may be supported there. + + int exit_value = EXIT_SUCCESS; + if (args) { + // @todo Should we go ahead and shutdown even if the args are invalid? + if (args->getType() != Element::map) { + return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map")); + } + + ConstElementPtr param = args->get("exit-value"); + if (param) { + if (param->getType() != Element::integer) { + return (createAnswer(CONTROL_RESULT_ERROR, + "parameter 'exit-value' is not an integer")); + } + + exit_value = param->intValue(); + } + } + + setExitValue(exit_value); + return (shutdownProcess(args)); +} + +ConstElementPtr +DControllerBase::shutdownProcess(ConstElementPtr args) { + if (process_) { + return (process_->shutdown(args)); + } + + // Not really a failure, but this condition is worth noting. In reality + // it should be pretty hard to cause this. + LOG_WARN(dctl_logger, DCTL_NOT_RUNNING).arg(app_name_); + return (createAnswer(COMMAND_SUCCESS, "Process has not been initialized")); +} + +void +DControllerBase::initSignalHandling() { + /// @todo block everything we don't handle + + // Create our signal set. + io_signal_set_.reset(new IOSignalSet(io_service_, + std::bind(&DControllerBase:: + processSignal, + this, ph::_1))); + // Register for the signals we wish to handle. + io_signal_set_->add(SIGHUP); + io_signal_set_->add(SIGINT); + io_signal_set_->add(SIGTERM); +} + +void +DControllerBase::processSignal(int signum) { + switch (signum) { + case SIGHUP: + { + LOG_INFO(dctl_logger, DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD) + .arg(signum).arg(getConfigFile()); + int rcode; + ConstElementPtr comment = parseAnswer(rcode, configFromFile()); + if (rcode != 0) { + LOG_ERROR(dctl_logger, DCTL_CFG_FILE_RELOAD_ERROR) + .arg(comment->stringValue()); + } + + break; + } + + case SIGINT: + case SIGTERM: + { + LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, + DCTL_SHUTDOWN_SIGNAL_RECVD).arg(signum); + ElementPtr arg_set; + shutdownHandler(SHUT_DOWN_COMMAND, arg_set); + break; + } + + default: + LOG_WARN(dctl_logger, DCTL_UNSUPPORTED_SIGNAL).arg(signum); + break; + } +} + +void +DControllerBase::usage(const std::string & text) { + if (text != "") { + std::cerr << "Usage error: " << text << std::endl; + } + + std::cerr << "Usage: " << bin_name_ << std::endl + << " -v: print version number and exit" << std::endl + << " -V: print extended version information and exit" + << std::endl + << " -W: display the configuration report and exit" + << std::endl + << " -d: optional, verbose output " << std::endl + << " -c <config file name> : mandatory," + << " specify name of configuration file" << std::endl + << " -t <config file name> : check the" + << " configuration file and exit" << std::endl; + + // add any derivation specific usage + std::cerr << getUsageText() << std::endl; +} + +DControllerBase::~DControllerBase() { + // Explicitly unload hooks + HooksManager::prepareUnloadLibraries(); + if (!HooksManager::unloadLibraries()) { + auto names = HooksManager::getLibraryNames(); + std::string msg; + if (!names.empty()) { + msg = names[0]; + for (size_t i = 1; i < names.size(); ++i) { + msg += std::string(", ") + names[i]; + } + } + LOG_ERROR(dctl_logger, DCTL_UNLOAD_LIBRARIES_ERROR).arg(msg); + } +} + +// Refer to config_report so it will be embedded in the binary +const char* const* d_config_report = isc::detail::config_report; + +std::string +DControllerBase::getVersion(bool extended) { + std::stringstream tmp; + + tmp << VERSION; + if (extended) { + tmp << std::endl << EXTENDED_VERSION << std::endl; + tmp << "linked with:" << std::endl; + tmp << isc::log::Logger::getVersion() << std::endl; + tmp << getVersionAddendum(); + } + + return (tmp.str()); +} + +} // end of namespace isc::process + +} // end of namespace isc diff --git a/src/lib/process/d_controller.h b/src/lib/process/d_controller.h new file mode 100644 index 0000000..1a1c6a6 --- /dev/null +++ b/src/lib/process/d_controller.h @@ -0,0 +1,653 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D_CONTROLLER_H +#define D_CONTROLLER_H + +#include <asiolink/io_service.h> +#include <asiolink/io_service_signal.h> +#include <cc/data.h> +#include <exceptions/exceptions.h> +#include <log/logger_support.h> +#include <process/daemon.h> +#include <process/d_log.h> +#include <process/d_process.h> + +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +#include <string> +#include <set> + +namespace isc { +namespace process { + +/// @brief Exception thrown when the command line is invalid. +/// Can be used to transmit negative messages too. +class InvalidUsage : public isc::Exception { +public: + InvalidUsage(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Exception used to convey version info upwards. +/// Since command line argument parsing is done as part of +/// DControllerBase::launch(), it uses this exception to propagate +/// version information up to main(), when command line argument +/// -v, -V or -W is given. Can be used to transmit positive messages too. +class VersionMessage : public isc::Exception { +public: + VersionMessage(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Exception thrown when the controller launch fails. +class LaunchError: public isc::Exception { +public: + LaunchError (const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Exception thrown when the application process fails. +class ProcessInitError: public isc::Exception { +public: + ProcessInitError (const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Exception thrown when the application process encounters an +/// operation in its event loop (i.e. run method). +class ProcessRunError: public isc::Exception { +public: + ProcessRunError (const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Exception thrown when the controller encounters an operational error. +class DControllerBaseError : public isc::Exception { +public: + DControllerBaseError (const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + + +/// @brief Defines a shared pointer to DControllerBase. +class DControllerBase; +typedef boost::shared_ptr<DControllerBase> DControllerBasePtr; + +/// @brief Application Controller +/// +/// DControllerBase is an abstract singleton which provides the framework and +/// services for managing an application process that implements the +/// DProcessBase interface. It runs the process like a stand-alone, command +/// line driven executable, which must be supplied a configuration file at +/// startup. It coordinates command line argument parsing, process +/// instantiation and initialization, and runtime control through external +/// command and configuration event handling. +/// It creates the IOService instance which is used for runtime control +/// events and passes the IOService into the application process at process +/// creation. +/// It provides the callback handlers for command and configuration events +/// which could be triggered by an external source. Such sources are intended +/// to be registered with and monitored by the controller's IOService such that +/// the appropriate handler can be invoked. +/// +/// DControllerBase provides dynamic configuration file reloading upon receipt +/// of SIGHUP, and graceful shutdown upon receipt of either SIGINT or SIGTERM. +/// +/// NOTE: Derivations must supply their own static singleton instance method(s) +/// for creating and fetching the instance. The base class declares the instance +/// member in order for it to be available for static callback functions. +class DControllerBase : public Daemon { +public: + /// @brief Constructor + /// + /// @param app_name is display name of the application under control. This + /// name appears in log statements. + /// @param bin_name is the name of the application executable. + DControllerBase(const char* app_name, const char* bin_name); + + /// @brief Destructor + virtual ~DControllerBase(); + + /// @brief returns Kea version on stdout and exit. + /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion() + std::string getVersion(bool extended); + + /// @brief Acts as the primary entry point into the controller execution + /// and provides the outermost application control logic: + /// + /// 1. parse command line arguments + /// 2. instantiate and initialize the application process + /// 3. load the configuration file + /// 4. record the start timestamp + /// 5. initialize signal handling + /// 6. start and wait on the application process event loop + /// 7. exit to the caller + /// + /// It is intended to be called from main() and be given the command line + /// arguments. + /// + /// This function can be run in "test mode". It prevents initialization + /// of module logger. This is used in unit tests which initialize logger + /// in their main function. Such a logger uses environmental variables to + /// control severity, verbosity etc. + /// + /// @param argc is the number of command line arguments supplied + /// @param argv is the array of string (char *) command line arguments + /// @param test_mode is a bool value which indicates if + /// @c DControllerBase::launch should be run in the test mode (if true). + /// This parameter doesn't have default value to force test implementers to + /// enable test mode explicitly. + /// + /// @throw throws one of the following exceptions: + /// InvalidUsage - Indicates invalid command line. + /// ProcessInitError - Failed to create and initialize application + /// process object. + /// ProcessRunError - A fatal error occurred while in the application + /// process event loop. + /// @return The value from @c Daemon::getExitValue(). + virtual int launch(int argc, char* argv[], const bool test_mode); + + /// @brief Instance method invoked by the configuration event handler and + /// which processes the actual configuration update. Provides behavioral + /// path for both integrated and stand-alone modes. The current + /// implementation will merge the configuration update into the existing + /// configuration and then invoke the application process' configure method. + /// + /// @param new_config is the new configuration + /// + /// @return returns an Element that contains the results of configuration + /// update composed of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr + new_config); + + /// @brief Instance method invoked by the configuration event handler and + /// which processes the actual configuration check. Provides behavioral + /// path for both integrated and stand-alone modes. The current + /// implementation will merge the configuration update into the existing + /// configuration and then invoke the application process' configure method + /// with a final rollback. + /// + /// @param new_config is the new configuration + /// + /// @return returns an Element that contains the results of configuration + /// update composed of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + virtual isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr + new_config); + + /// @brief Reconfigures the process from a configuration file + /// + /// By default the file is assumed to be a JSON text file whose contents + /// include at least: + /// + /// @code + /// { "<module-name>": {<module-config>} + /// } + /// + /// where: + /// module-name : is a label which uniquely identifies the + /// configuration data for this controller's application + /// + /// module-config: a set of zero or more JSON elements which comprise + /// the application's configuration values + /// @endcode + /// + /// To translate the JSON content into Elements, @c parseFile() is called + /// first. This virtual method provides derivations a means to parse the + /// file content using an alternate parser. If it returns an empty pointer + /// than the JSON parsing providing by Element::fromJSONFile() is called. + /// + /// Once parsed, the method extracts the set of configuration + /// elements for the module-name that matches the controller's app_name_, + /// looks for the loggers entry and, if present uses it to configure + /// logging. It then passes that set into @c updateConfig() (or + /// @c checkConfig()). + /// + /// The file may contain an arbitrary number of other modules. + /// + /// @return returns an Element that contains the results of configuration + /// update composed of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + virtual isc::data::ConstElementPtr configFromFile(); + + /// @brief Fetches the name of the application under control. + /// + /// @return returns the controller service name string + std::string getAppName() const { + return (app_name_); + } + + /// @brief Fetches the name of the application executable. + /// + /// @return returns the controller logger name string + std::string getBinName() const { + return (bin_name_); + } + + /// @brief handler for version-get command + /// + /// This method handles the version-get command. It returns the basic and + /// extended version. + /// + /// @param command (ignored) + /// @param args (ignored) + /// @return answer with version details. + isc::data::ConstElementPtr + versionGetHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for 'build-report' command + /// + /// This method handles build-report command. It returns the output printed + /// by configure script which contains most compilation parameters. + /// + /// @param command (ignored) + /// @param args (ignored) + /// @return answer with build report + isc::data::ConstElementPtr + buildReportHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for config-get command + /// + /// This method handles the config-get command, which retrieves + /// the current configuration and returns it in response. + /// + /// @param command (ignored) + /// @param args (ignored) + /// @return current configuration wrapped in a response + isc::data::ConstElementPtr + configGetHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for config-write command + /// + /// This handle processes write-config command, which writes the + /// current configuration to disk. This command takes one optional + /// parameter called filename. If specified, the current configuration + /// will be written to that file. If not specified, the file used during + /// Kea start-up will be used. To avoid any exploits, the path is + /// always relative and .. is not allowed in the filename. This is + /// a security measure against exploiting file writes remotely. + /// + /// @param command (ignored) + /// @param args may contain optional string argument filename + /// @return status of the configuration file write + isc::data::ConstElementPtr + configWriteHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for config-test command + /// + /// This method handles the config-test command, which checks + /// configuration specified in args parameter. + /// + /// @param command (ignored) + /// @param args configuration to be checked. + /// @return status of the command + isc::data::ConstElementPtr + configTestHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for config-reload command + /// + /// This method handles the config-reload command, which reloads + /// the configuration file. + /// + /// @param command (ignored) + /// @param args (ignored) + /// @return status of the command + isc::data::ConstElementPtr + configReloadHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for config-set command + /// + /// This method handles the config-set command, which loads + /// configuration specified in args parameter. + /// + /// @param command (ignored) + /// @param args configuration to be checked. + /// @return status of the command + isc::data::ConstElementPtr + configSetHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for 'shutdown' command + /// + /// This method handles shutdown command. It initiates the shutdown procedure + /// using CPL methods. + /// @param command (ignored) + /// @param args (ignored) + /// @return answer confirming that the shutdown procedure is started + isc::data::ConstElementPtr + shutdownHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for server-tag-get command + /// + /// This method handles the server-tag-get command, which retrieves + /// the current server tag and returns it in response. + /// + /// @param command (ignored) + /// @param args (ignored) + /// @return current configuration wrapped in a response + isc::data::ConstElementPtr + serverTagGetHandler(const std::string& command, + isc::data::ConstElementPtr args); + + /// @brief handler for status-get command + /// + /// This method handles the status-get command, which retrieves + /// the server process information i.e. the pid and returns it in + /// response. + /// + /// @param command (ignored) + /// @param args (ignored) + /// @return process information wrapped in a response + isc::data::ConstElementPtr + statusGetHandler(const std::string& command, + isc::data::ConstElementPtr args); + +protected: + /// @brief Virtual method that provides derivations the opportunity to + /// support additional command line options. It is invoked during command + /// line argument parsing (see parseArgs method) if the option is not + /// recognized as a stock DControllerBase option. + /// + /// @param option is the option "character" from the command line, without + /// any prefixing hyphen(s) + /// @param optarg is the argument value (if one) associated with the option + /// + /// @return must return true if the option was valid, false if it is + /// invalid. (Note the default implementation always returns false.) + virtual bool customOption(int option, char *optarg); + + /// @brief Abstract method that is responsible for instantiating the + /// application process object. It is invoked by the controller after + /// command line argument parsing as part of the process initialization + /// (see initProcess method). + /// + /// @return returns a pointer to the new process object (DProcessBase*) + /// or NULL if the create fails. + /// Note this value is subsequently wrapped in a smart pointer. + virtual DProcessBase* createProcess() = 0; + + /// @brief Virtual method which can be used to contribute derivation + /// specific usage text. It is invoked by the usage() method under + /// invalid usage conditions. + /// + /// @return returns the desired text. + virtual const std::string getUsageText() const { + return (""); + } + + /// @brief Virtual method which returns a string containing the option + /// letters for any custom command line options supported by the derivation. + /// These are added to the stock options of "c", "d", ..., during command + /// line interpretation. + /// + /// @return returns a string containing the custom option letters. + virtual const std::string getCustomOpts() const { + return (""); + } + + /// @brief Check the configuration + /// + /// Called by @c launch() when @c check_only_ mode is enabled + /// @throw VersionMessage when successful but a message should be displayed + /// @throw InvalidUsage when an error was detected + void checkConfigOnly(); + + /// @brief Application-level signal processing method. + /// + /// This method is the last step in processing a OS signal occurrence. + /// It currently supports the following signals as follows: + /// -# SIGHUP - instigates reloading the configuration file + /// -# SIGINT - instigates a graceful shutdown + /// -# SIGTERM - instigates a graceful shutdown + /// If it receives any other signal, it will issue a debug statement and + /// discard it. + /// Derivations wishing to support additional signals could override this + /// method with one that: processes the signal if it is one of additional + /// signals, otherwise invoke this method (DControllerBase::processSignal()) + /// with the signal value. + /// @todo Provide a convenient way for derivations to register additional + /// signals. + virtual void processSignal(int signum); + + /// @brief Supplies whether or not verbose logging is enabled. + /// + /// @return returns true if verbose logging is enabled. + bool isVerbose() const { + return (verbose_); + } + + /// @brief Method for enabling or disabling verbose logging. + /// + /// @param value is the new value to assign the flag. + void setVerbose(bool value) { + verbose_ = value; + } + + /// @brief Supplies whether or not check only mode is enabled. + /// + /// @return returns true if check only is enabled. + bool isCheckOnly() const { + return (check_only_); + } + + /// @brief Method for enabling or disabling check only mode. + /// + /// @todo this method and @c setVerbose are currently not used. + /// + /// @param value is the new value to assign the flag. + void setCheckOnly(bool value) { + check_only_ = value; + } + + /// @brief Getter for fetching the controller's IOService + /// + /// @return returns a pointer reference to the IOService. + asiolink::IOServicePtr& getIOService() { + return (io_service_); + } + + /// @brief Static getter which returns the singleton instance. + /// + /// @return returns a pointer reference to the private singleton instance + /// member. + static DControllerBasePtr& getController() { + return (controller_); + } + + /// @brief Static setter which sets the singleton instance. + /// + /// @param controller is a pointer to the singleton instance. + /// + /// @throw throws DControllerBase error if an attempt is made to set the + /// instance a second time. + static void setController(const DControllerBasePtr& controller); + + /// @brief Processes the command line arguments. It is the first step + /// taken after the controller has been launched. It combines the stock + /// list of options with those returned by getCustomOpts(), and uses + /// cstdlib's getopt to loop through the command line. + /// It handles stock options directly, and passes any custom options into + /// the customOption method. Currently there are only some stock options + /// -c/t for specifying the configuration file, -d for verbose logging, + /// and -v/V/W for version reports. + /// + /// @param argc is the number of command line arguments supplied + /// @param argv is the array of string (char *) command line arguments + /// + /// @throw InvalidUsage when there are usage errors. + /// @throw VersionMessage if the -v, -V or -W arguments is given. + void parseArgs(int argc, char* argv[]); + + + ///@brief Parse a given file into Elements + /// + /// This method provides a means for deriving classes to use alternate + /// parsing mechanisms to parse configuration files into the corresponding + /// isc::data::Elements. The elements produced must be equivalent to those + /// which would be produced by the original JSON parsing. Implementations + /// should throw when encountering errors. + /// + /// The default implementation returns an empty pointer, signifying to + /// callers that they should submit the file to the original parser. + /// + /// @param file_name pathname of the file to parse + /// + /// @return pointer to the elements created + /// + virtual isc::data::ConstElementPtr parseFile(const std::string& file_name); + + ///@brief Parse text into Elements + /// + /// This method provides a means for deriving classes to use alternate + /// parsing mechanisms to parse configuration text into the corresponding + /// isc::data::Elements. The elements produced must be equivalent to those + /// which would be produced by the original JSON parsing. Implementations + /// should throw when encountering errors. + /// + /// The default implementation returns an empty pointer, signifying to + /// callers that they should submit the text to the original parser. + /// + /// @param input text to parse + /// + /// @return pointer to the elements created + /// + virtual isc::data::ConstElementPtr parseText(const std::string& input) { + static_cast<void>(input); // just tu shut up the unused parameter warning + isc::data::ConstElementPtr elements; + return (elements); + } + + /// @brief Instantiates the application process and then initializes it. + /// This is the second step taken during launch, following successful + /// command line parsing. It is used to invoke the derivation-specific + /// implementation of createProcess, following by an invoking of the + /// newly instantiated process's init method. + /// + /// @throw throws DControllerBaseError or indirectly DProcessBaseError + /// if there is a failure creating or initializing the application process. + void initProcess(); + + /// @brief Invokes the application process's event loop,(DBaseProcess::run). + /// It is called during launch only after successfully completing the + /// requested setup: command line parsing, application initialization, + /// and session establishment (if not stand-alone). + /// The process event loop is expected to only return upon application + /// shutdown either in response to the shutdown command or due to an + /// unrecoverable error. + /// + // @throw throws DControllerBaseError or indirectly DProcessBaseError + void runProcess(); + + /// @brief Initiates shutdown procedure. This method is invoked + /// by executeCommand in response to the shutdown command. It will invoke + /// the application process's shutdown method which causes the process to + /// to begin its shutdown process. + /// + /// Note, it is assumed that the process of shutting down is neither + /// instantaneous nor synchronous. This method does not "block" waiting + /// until the process has halted. Rather it is used to convey the + /// need to shutdown. A successful return indicates that the shutdown + /// has successfully commenced, but does not indicate that the process + /// has actually exited. + /// + /// @return returns an Element that contains the results of shutdown + /// command composed of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + /// + /// @param args is a set of derivation-specific arguments (if any) + /// for the shutdown command. + isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args); + + /// @brief Initializes signal handling + /// + /// This method configures the controller to catch and handle signals. + /// It instantiates a IOSignalSet which listens for SIGHUP, SIGINT, and + /// SIGTERM. + void initSignalHandling(); + + /// @brief Fetches the current process + /// + /// @return a pointer to the current process instance. + DProcessBasePtr getProcess() { + return (process_); + } + + /// @brief Prints the program usage text to std error. + /// + /// @param text is a string message which will preceded the usage text. + /// This is intended to be used for specific usage violation messages. + void usage(const std::string& text); + + /// @brief Fetches text containing additional version specifics + /// + /// This method is provided so derivations can append any additional + /// desired information such as library dependencies to the extended + /// version text returned when DControllerBase::getVersion(true) is + /// invoked. + /// @return a string containing additional version info + virtual std::string getVersionAddendum() { return (""); } + + /// @brief Deals with other (i.e. not application name) global objects. + /// + /// Code shared between configuration handlers: + /// - check obsolete or unknown (aka unsupported) objects. + /// + /// @param args Command arguments. + /// @return Error message or empty string. + std::string handleOtherObjects(isc::data::ConstElementPtr args); + +private: + /// @brief Name of the service under control. + /// This name is used as the configuration module name and appears in log + /// statements. + std::string app_name_; + + /// @brief Name of the service executable. + /// By convention this matches the executable name. It is also used to + /// establish the logger name. + std::string bin_name_; + + /// @brief Indicates if the verbose logging mode is enabled. + bool verbose_; + + /// @brief Indicates if the check only mode for the configuration + /// is enabled (usually specified by the command line -t argument). + bool check_only_; + + /// @brief Pointer to the instance of the process. + /// + /// This is required for config and command handlers to gain access to + /// the process + DProcessBasePtr process_; + + /// @brief Shared pointer to an IOService object, used for ASIO operations. + isc::asiolink::IOServicePtr io_service_; + + /// @brief ASIO signal set. + isc::asiolink::IOSignalSetPtr io_signal_set_; + + /// @brief Singleton instance value. + static DControllerBasePtr controller_; + +// DControllerTest is named a friend class to facilitate unit testing while +// leaving the intended member scopes intact. +friend class DControllerTest; +}; + +} // namespace isc::process +} // namespace isc + +#endif diff --git a/src/lib/process/d_log.cc b/src/lib/process/d_log.cc new file mode 100644 index 0000000..a449996 --- /dev/null +++ b/src/lib/process/d_log.cc @@ -0,0 +1,21 @@ +// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// Defines the logger used by the top-level component of kea-dhcp-ddns. + +#include <config.h> + +#include <process/d_log.h> + +namespace isc { +namespace process { + +/// @brief Defines the logger used within libkea-process library. +isc::log::Logger dctl_logger("dctl"); + +} // namespace process +} // namespace isc + diff --git a/src/lib/process/d_log.h b/src/lib/process/d_log.h new file mode 100644 index 0000000..5a20cf4 --- /dev/null +++ b/src/lib/process/d_log.h @@ -0,0 +1,23 @@ +// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D_LOG_H +#define D_LOG_H + +#include <log/logger_support.h> +#include <log/macros.h> +#include <process/process_messages.h> + +namespace isc { +namespace process { + +/// Define the loggers used within libkea-process library. +extern isc::log::Logger dctl_logger; + +} // namespace process +} // namespace isc + +#endif // D_LOG_H diff --git a/src/lib/process/d_process.h b/src/lib/process/d_process.h new file mode 100644 index 0000000..e7e6546 --- /dev/null +++ b/src/lib/process/d_process.h @@ -0,0 +1,220 @@ +// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D_PROCESS_H +#define D_PROCESS_H + +#include <asiolink/io_service.h> +#include <cc/data.h> +#include <process/d_cfg_mgr.h> + +#include <boost/shared_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <atomic> + +namespace isc { +namespace process { + +/// @brief Exception thrown if the process encountered an operational error. +class DProcessBaseError : public isc::Exception { +public: + DProcessBaseError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief String value for the version-get command. +static const std::string VERSION_GET_COMMAND("version-get"); + +/// @brief String value for the build-report command. +static const std::string BUILD_REPORT_COMMAND("build-report"); + +/// @brief String value for the config-get command. +static const std::string CONFIG_GET_COMMAND("config-get"); + +/// @brief String value for the config-write command. +static const std::string CONFIG_WRITE_COMMAND("config-write"); + +/// @brief String value for the config-test command. +static const std::string CONFIG_TEST_COMMAND("config-test"); + +/// @brief String value for the config-reload command. +static const std::string CONFIG_RELOAD_COMMAND("config-reload"); + +/// @brief String value for the config-set command. +static const std::string CONFIG_SET_COMMAND("config-set"); + +/// @brief String value for the server-tag-get command. +static const std::string SERVER_TAG_GET_COMMAND("server-tag-get"); + +/// @brief String value for the shutdown command. +static const std::string SHUT_DOWN_COMMAND("shutdown"); + +/// @brief String value for the status-get command. +static const std::string STATUS_GET_COMMAND("status-get"); + +/// @brief Returned by the process to indicate a command was successful. +static const int COMMAND_SUCCESS = 0; + +/// @brief Returned by the process to indicates a command failed. +static const int COMMAND_ERROR = 1; + +/// @brief Returned by the process to indicates a command is not valid. +static const int COMMAND_INVALID = 2; + +/// @brief Application Process Interface +/// +/// DProcessBase is an abstract class represents the primary "application" +/// level object in a "managed" asynchronous application. It provides a uniform +/// interface such that a managing layer can construct, initialize, and start +/// the application's event loop. The event processing is centered around the +/// use of isc::asiolink::io_service. The io_service is shared between the +/// managing layer and the DProcessBase. This allows management layer IO such +/// as directives to be sensed and handled, as well as processing IO activity +/// specific to the application. In terms of management layer IO, there are +/// methods shutdown, configuration updates, and commands unique to the +/// application. +class DProcessBase { +public: + /// @brief Constructor + /// + /// @param app_name is a text label for the process. Generally used + /// in log statements, but otherwise arbitrary. + /// @param io_service is the io_service used by the caller for + /// asynchronous event handling. + /// @param cfg_mgr the configuration manager instance that handles + /// configuration parsing. + /// + /// @throw DProcessBaseError is io_service is NULL. + DProcessBase(const char* app_name, asiolink::IOServicePtr io_service, + DCfgMgrBasePtr cfg_mgr) + : app_name_(app_name), io_service_(io_service), shut_down_flag_(false), + cfg_mgr_(cfg_mgr) { + if (!io_service_) { + isc_throw (DProcessBaseError, "IO Service cannot be null"); + } + + if (!cfg_mgr_) { + isc_throw (DProcessBaseError, "CfgMgr cannot be null"); + } + }; + + /// @brief May be used after instantiation to perform initialization unique + /// to application. It must be invoked prior to invoking run. This would + /// likely include the creation of additional IO sources and their + /// integration into the io_service. + /// @throw DProcessBaseError if the initialization fails. + virtual void init() = 0; + + /// @brief Implements the process's event loop. In its simplest form it + /// would an invocation io_service_->run(). This method should not exit + /// until the process itself is exiting due to a request to shutdown or + /// some anomaly is forcing an exit. + /// @throw DProcessBaseError if an operational error is encountered. + virtual void run() = 0; + + /// @brief Initiates the process's shutdown process. + /// + /// This is last step in the shutdown event callback chain, that is + /// intended to notify the process it is to begin its shutdown process. + /// + /// @param args an Element set of shutdown arguments (if any) that are + /// supported by the process derivation. + /// + /// @return an Element that contains the results of argument processing, + /// consisting of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + /// + /// @throw DProcessBaseError if an operational error is encountered. + virtual isc::data::ConstElementPtr + shutdown(isc::data::ConstElementPtr args) = 0; + + /// @brief Processes the given configuration. + /// + /// This method may be called multiple times during the process lifetime. + /// Certainly once during process startup, and possibly later if the user + /// alters configuration. This method must not throw, it should catch any + /// processing errors and return a success or failure answer as described + /// below. On success the last commit timestamp must be updated. + /// + /// @param config_set a new configuration (JSON) for the process + /// @param check_only true if configuration is to be verified only, not applied + /// @return an Element that contains the results of configuration composed + /// of an integer status value (0 means successful, non-zero means failure), + /// and a string explanation of the outcome. + virtual isc::data::ConstElementPtr + configure(isc::data::ConstElementPtr config_set, + bool check_only = false) = 0; + + /// @brief Destructor + virtual ~DProcessBase(){}; + + /// @brief Checks if the process has been instructed to shut down. + /// + /// @return true if process shutdown flag is true. + bool shouldShutdown() const { + return (shut_down_flag_); + } + + /// @brief Sets the process shut down flag to the given value. + /// + /// @param value is the new value to assign the flag. + void setShutdownFlag(bool value) { + shut_down_flag_ = value; + } + + /// @brief Fetches the application name. + /// + /// @return application name string. + const std::string getAppName() const { + return (app_name_); + } + + /// @brief Fetches the controller's IOService. + /// + /// @return a reference to the controller's IOService. + asiolink::IOServicePtr& getIoService() { + return (io_service_); + } + + /// @brief Convenience method for stopping IOservice processing. + /// Invoking this will cause the process to exit any blocking + /// IOService method such as run(). No further IO events will be + /// processed. + void stopIOService() { + io_service_->stop(); + } + + /// @brief Fetches the process's configuration manager. + /// + /// @return a reference to the configuration manager. + DCfgMgrBasePtr& getCfgMgr() { + return (cfg_mgr_); + } + +private: + /// @brief Text label for the process. Generally used in log statements, + /// but otherwise can be arbitrary. + std::string app_name_; + + /// @brief The IOService to be used for asynchronous event handling. + asiolink::IOServicePtr io_service_; + + /// @brief Boolean flag set when shutdown has been requested. + std::atomic<bool> shut_down_flag_; + + /// @brief Pointer to the configuration manager. + DCfgMgrBasePtr cfg_mgr_; +}; + +/// @brief Defines a shared pointer to DProcessBase. +typedef boost::shared_ptr<DProcessBase> DProcessBasePtr; + +}; // namespace isc::process +}; // namespace isc + +#endif diff --git a/src/lib/process/daemon.cc b/src/lib/process/daemon.cc new file mode 100644 index 0000000..c2589c9 --- /dev/null +++ b/src/lib/process/daemon.cc @@ -0,0 +1,266 @@ +// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/data.h> +#include <process/daemon.h> +#include <process/log_parser.h> +#include <exceptions/exceptions.h> +#include <log/logger_name.h> +#include <log/logger_support.h> +#include <process/config_base.h> +#include <process/redact_config.h> +#include <util/filename.h> + +#include <functional> +#include <sstream> +#include <fstream> + +#include <errno.h> + +using namespace isc::data; +namespace ph = std::placeholders; + +/// @brief provides default implementation for basic daemon operations +/// +/// This file provides stub implementations that are expected to be redefined +/// in derived classes (e.g. ControlledDhcpv6Srv) +namespace isc { +namespace process { + +bool Daemon::verbose_ = false; + +std::string Daemon::proc_name_(""); + +std::string Daemon::default_logger_name_("kea"); + +Daemon::Daemon() + : signal_set_(), config_file_(""), + pid_file_dir_(DATA_DIR), pid_file_(), am_file_author_(false), + exit_value_(EXIT_SUCCESS) { + + // The pid_file_dir can be overridden via environment variable + // This is primarily intended to simplify testing + const char* const env = getenv("KEA_PIDFILE_DIR"); + if (env) { + pid_file_dir_ = env; + } +} + +Daemon::~Daemon() { + if (pid_file_ && am_file_author_) { + pid_file_->deleteFile(); + } +} + +void Daemon::cleanup() { +} + +void Daemon::shutdown() { +} + +void Daemon::configureLogger(const ConstElementPtr& log_config, + const ConfigPtr& storage) { + + if (log_config) { + ConstElementPtr loggers = log_config->get("loggers"); + if (loggers) { + LogConfigParser parser(storage); + parser.parseConfiguration(loggers, verbose_); + } + } +} + +void +Daemon::setVerbose(bool verbose) { + verbose_ = verbose; +} + +bool +Daemon::getVerbose() { + return (verbose_); +} + +void Daemon::loggerInit(const char* name, bool verbose) { + + setenv("KEA_LOGGER_DESTINATION", "stdout", 0); + + // Initialize logger system + isc::log::initLogger(name, isc::log::DEBUG, isc::log::MAX_DEBUG_LEVEL, + NULL); + + // Apply default configuration (log INFO or DEBUG to stdout) + isc::log::setDefaultLoggingOutput(verbose); +} + +std::string Daemon::getVersion(bool /*extended*/) { + isc_throw(isc::NotImplemented, "Daemon::getVersion() called"); +} + +std::string +Daemon::getConfigFile() const { + return (config_file_); +} + +void +Daemon::setConfigFile(const std::string& config_file) { + config_file_ = config_file; +} + +void +Daemon::checkConfigFile() const { + if (config_file_.empty()) { + isc_throw(isc::BadValue, "config file name is not set"); + } + + // Create Filename instance from the config_file_ pathname, and + // check the file name component. + isc::util::Filename file(config_file_); + if (file.name().empty()) { + isc_throw(isc::BadValue, "config file:" << config_file_ + << " is missing file name"); + } +} + +std::string +Daemon::getProcName() { + return (proc_name_); +}; + +void +Daemon::setProcName(const std::string& proc_name) { + proc_name_ = proc_name; +} + +std::string +Daemon::getPIDFileDir() const { + return(pid_file_dir_); +} + +void +Daemon::setPIDFileDir(const std::string& pid_file_dir) { + pid_file_dir_ = pid_file_dir; +} + +std::string +Daemon::getPIDFileName() const { + if (pid_file_) { + return (pid_file_->getFilename()); + } + + return (""); +}; + +void +Daemon::setPIDFileName(const std::string& pid_file_name) { + if (pid_file_) { + isc_throw(isc::InvalidOperation, "Daemon::setConfigFile" + " file name already set to:" << pid_file_->getFilename()); + } + + if (pid_file_name.empty()) { + isc_throw(isc::BadValue, "Daemon::setPIDFileName" + " file name may not be empty"); + } + + pid_file_.reset(new util::PIDFile(pid_file_name)); +}; + +std::string +Daemon::makePIDFileName() const { + if (config_file_.empty()) { + isc_throw(isc::InvalidOperation, + "Daemon::makePIDFileName config file name is not set"); + } + + // Create Filename instance from the config_file_ pathname, so we can + // extract the fname component. + isc::util::Filename file(config_file_); + if (file.name().empty()) { + isc_throw(isc::BadValue, "Daemon::makePIDFileName config file:" + << config_file_ << " is missing file name"); + } + + if (proc_name_.empty()) { + isc_throw(isc::InvalidOperation, + "Daemon::makePIDFileName process name is not set"); + } + + // Make the pathname for the PID file from the runtime directory, + // configuration name and process name. + std::ostringstream stream; + stream << pid_file_dir_ << "/" << file.name() + << "." << proc_name_ << ".pid"; + + return(stream.str()); +}; + +void +Daemon::createPIDFile(int pid) { + // If pid_file_ hasn't been instantiated explicitly, then do so + // using the default name. + if (!pid_file_) { + setPIDFileName(makePIDFileName()); + } + + // If we find a pre-existing file containing a live PID we bail. + int chk_pid = pid_file_->check(); + if (chk_pid > 0) { + isc_throw(DaemonPIDExists, "Daemon::createPIDFile: PID: " << chk_pid + << " exists, PID file: " << getPIDFileName()); + } + + if (pid == 0) { + // Write the PID of the current process + pid_file_->write(); + } else { + // Write the PID we were given + pid_file_->write(pid); + } + + am_file_author_ = true; +} + +size_t +Daemon::writeConfigFile(const std::string& config_file, + ConstElementPtr cfg) const { + if (!cfg) { + isc_throw(Unexpected, "Can't write configuration: conversion to JSON failed"); + } + + std::ofstream out(config_file, std::ios::trunc); + if (!out.good()) { + isc_throw(Unexpected, "Unable to open file " + config_file + " for writing"); + } + + // Write the actual content using pretty printing. + prettyPrint(cfg, out); + + size_t bytes = static_cast<size_t>(out.tellp()); + + out.close(); + + return (bytes); +} + +std::list<std::list<std::string>> +Daemon::jsonPathsToRedact() const { + static std::list<std::list<std::string>> const list; + return list; +} + +isc::data::ConstElementPtr +Daemon::redactConfig(isc::data::ConstElementPtr const& config) { + isc::data::ConstElementPtr result(config); + for (std::list<std::string>& json_path : jsonPathsToRedact()) { + result = isc::process::redactConfig(result, json_path); + } + return result; +} + +} // namespace process +} // namespace isc diff --git a/src/lib/process/daemon.h b/src/lib/process/daemon.h new file mode 100644 index 0000000..d64445a --- /dev/null +++ b/src/lib/process/daemon.h @@ -0,0 +1,296 @@ +// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DAEMON_H +#define DAEMON_H + +#include <cc/data.h> +#include <process/config_base.h> +#include <util/pid_file.h> +#include <asiolink/io_service_signal.h> + +#include <boost/noncopyable.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <list> +#include <string> + +namespace isc { +namespace process { + +/// @brief Exception thrown when the PID file points to a live PID +class DaemonPIDExists : public Exception { +public: + DaemonPIDExists(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Base class for all services +/// +/// This is the base class that all daemons (DHCPv4, DHCPv6, D2 and possibly +/// others) are derived from. It provides a standard interface for starting up, +/// reconfiguring, shutting down and several other operations. It also covers +/// some common operations. +/// +/// This class is not expected to be instantiated directly, but rather daemon +/// implementations should derive from it. +/// +/// Methods are not pure virtual, as we need to instantiate basic daemons (e.g. +/// Dhcpv4Srv and Dhcpv6Srv) in tests, without going through the hassles of +/// implementing stub methods. +/// +/// @note Only one instance of this class is instantiated as it encompasses +/// the whole operation of the server. Nothing, however, enforces the +/// singleton status of the object. +class Daemon : public boost::noncopyable { + +public: + /// @brief Default constructor + /// + /// Initializes the object installing custom signal handlers for the + /// process to NULL. + Daemon(); + + /// @brief Destructor + /// + /// Having virtual destructor ensures that all derived classes will have + /// virtual destructor as well. + virtual ~Daemon(); + + /// @brief Performs final deconfiguration. + /// + /// Performs configuration backend specific final clean-up. This is called + /// shortly before the daemon terminates. Depending on backend, it may + /// terminate existing msgq session, close LDAP connection or similar. + /// + /// The daemon is not expected to receive any further commands or + /// configuration updates as it is in final stages of shutdown. + virtual void cleanup(); + + /// @brief Initiates shutdown procedure for the whole server. + virtual void shutdown(); + + /// @brief Initializes logger + /// + /// This method initializes logging system. It also sets the default + /// output to stdout. This is used in early stages of the startup + /// phase before config file and parsed and proper logging details + /// are known. + /// + /// @param log_name name used in logger initialization + /// @param verbose verbose mode (true usually enables DEBUG messages) + static void loggerInit(const char* log_name, bool verbose); + + /// @brief Configures logger + /// + /// Applies configuration stored in a top-level structure in the + /// configuration file. This structure has a "loggers" array that + /// contains 0 or more entries, each configuring one logging source + /// (name, severity, debuglevel), each with zero or more outputs (file, + /// maxsize, maximum number of files). + /// + /// @param log_config JSON structures that describe logging + /// @param storage configuration will be stored here + static void configureLogger(const isc::data::ConstElementPtr& log_config, + const isc::process::ConfigPtr& storage); + + /// @brief Sets or clears verbose mode + /// + /// Verbose mode (-v in command-line) triggers loggers to log everything + /// (sets severity to DEBUG and debuglevel to 99). Values specified in the + /// config file are ignored. + /// + /// @param verbose specifies if verbose should be set or not + static void setVerbose(const bool verbose); + + /// @brief Returns if running in verbose mode + /// + /// @return verbose mode + static bool getVerbose(); + + /// @brief returns Kea version on stdout and exits. + /// + /// With extended == false, this method returns a simple string + /// containing version number. With extended == true, it returns + /// also additional information about sources. It is expected to + /// return extra information about dependencies and used DB backends. + /// + /// As there is no static virtual methods in C++ this class method + /// has to be redefined in derived classes and called with the + /// derived class name or a child name. + /// + /// @param extended print additional information? + /// @return text string + static std::string getVersion(bool extended); + + /// @brief Returns config file name. + /// @return text string + std::string getConfigFile() const; + + /// @brief Sets the configuration file name + /// + /// @param config_file pathname of the configuration file + void setConfigFile(const std::string& config_file); + + /// @brief Checks the configuration file name. + /// @throw BadValue when the configuration file name is bad. + void checkConfigFile() const; + + /// @brief Writes current configuration to specified file + /// + /// This method writes the current configuration to specified file. + /// @todo: this logically more belongs to CPL process file. Once + /// Daemon is merged with CPL architecture, it will be a better + /// fit. + /// + /// If cfg is not specified, the current config (as returned by + /// CfgMgr::instance().getCurrentCfg() will be returned. + /// + /// @param config_file name of the file to write the configuration to + /// @param cfg configuration to write (optional) + /// @return number of files written + /// @throw Unexpected if CfgMgr can't retrieve configuration or file cannot + /// be written + virtual size_t + writeConfigFile(const std::string& config_file, + isc::data::ConstElementPtr cfg = isc::data::ConstElementPtr()) const; + + /// @brief returns the process name + /// This value is used as when forming the default PID file name + /// @return text string + static std::string getProcName(); + + /// @brief Sets the process name + /// @param proc_name name the process by which the process is recognized + static void setProcName(const std::string& proc_name); + + /// @brief Returns the directory used when forming default PID file name + /// @return text string + std::string getPIDFileDir() const; + + /// @brief Sets the PID file directory + /// @param pid_file_dir path into which the PID file should be written + /// Note the value should not include a trailing slash, '/' + void setPIDFileDir(const std::string& pid_file_dir); + + /// @brief Returns the current PID file name + /// @return text string + std::string getPIDFileName() const; + + /// @brief Sets PID file name + /// + /// If this method is called prior to calling createPIDFile, + /// the value passed in will be treated as the full file name + /// for the PID file. This provides a means to override the + /// default file name with an explicit value. + /// + /// @param pid_file_name file name to be used as the PID file + void setPIDFileName(const std::string& pid_file_name); + + /// @brief Creates the PID file + /// + /// If the PID file name has not been previously set, the method + /// uses manufacturePIDFileName() to set it. If the PID file + /// name refers to an existing file whose contents are a PID whose + /// process is still alive, the method will throw a DaemonPIDExists + /// exception. Otherwise, the file created (or truncated) and + /// the given pid (if not zero) is written to the file. + /// + /// @param pid PID to write to the file if not zero, otherwise the + /// PID of the current process is used. + void createPIDFile(int pid = 0); + + /// @brief Returns default logger name. + static std::string getDefaultLoggerName() { + return (default_logger_name_); + } + + /// @brief Sets the default logger name. + /// + /// This name is used in cases when a user doesn't provide a configuration + /// for logger in the Kea configuration file. + static void setDefaultLoggerName(const std::string& logger) { + default_logger_name_ = logger; + } + + /// @brief Fetches the exit value. + int getExitValue() { + return (exit_value_); + } + + /// @brief Sets the exit value. + /// + /// @param value new exit value. + void setExitValue(int value) { + exit_value_ = value; + } + + /// @brief Return a list of all paths that contain passwords or secrets. + /// + /// Used in @ref isc::process::Daemon::redactConfig to only make copies and + /// only redact configuration subtrees that contain passwords or secrets. To + /// be overridden by derived classes. + /// + /// @return the list of lists of sequential JSON map keys needed to reach + /// the passwords and secrets. + virtual std::list<std::list<std::string>> jsonPathsToRedact() const; + + /// @brief Redact a configuration. + /// + /// Calls @ref isc::process::redactConfig + /// + /// @param config the Element tree structure that describes the configuration. + /// + /// @return the redacted configuration + isc::data::ConstElementPtr redactConfig(isc::data::ConstElementPtr const& config); + +protected: + + /// @brief A pointer to the object installing custom signal handlers. + /// + /// This pointer needs to be initialized to point to the @c IOSignalSet + /// object in the derived classes which need to handle signals received + /// by the process. + isc::asiolink::IOSignalSetPtr signal_set_; + + /// @brief Manufacture the pid file name + std::string makePIDFileName() const; + + /// @brief Timestamp of the start of the daemon. + boost::posix_time::ptime start_; + +private: + /// @brief Config file name or empty if config file not used. + std::string config_file_; + + /// @brief Name of this process, used when creating its pid file + static std::string proc_name_; + + /// @brief Pointer to the directory where PID file(s) are written + /// It defaults to --localstatedir / run + std::string pid_file_dir_; + + /// @brief Pointer to the PID file for this process + isc::util::PIDFilePtr pid_file_; + + /// @brief Indicates whether verbose mode is turned on or not. + static bool verbose_; + + /// @brief Stores default logger name + static std::string default_logger_name_; + + /// @brief Flag indicating if this instance created the file + bool am_file_author_; + + /// @brief Exit value the process should use. Typically set + /// by the application during a controlled shutdown. + int exit_value_; +}; + +} // namespace process +} // namespace isc + +#endif diff --git a/src/lib/process/libprocess.dox b/src/lib/process/libprocess.dox new file mode 100644 index 0000000..2ea2e5c --- /dev/null +++ b/src/lib/process/libprocess.dox @@ -0,0 +1,195 @@ +// Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/** + @page libprocess libkea-process - Controllable Process Layer (CPL) + +@section cpl Controllable Process Layer (CPL) +During the design and development of D2 (Kea's DHCP-DDNS process), an abstract +layer for process control, called the Controllable Process Layer or CPL, was +created. Kea's DHCP servers were initially developed prior to D2 and thus +before CPL existed. + +Out of short term convenience and the fact that only D2 was using it, the CPL +was initially developed as part of D2 in src/bin/d2. In order to use CPL for +new Kea processes, it has since been moved into its own library, libkea-process. +The following sections describe the architecture of CPL and how it can be used to implement new daemons in Kea. + +The CPL provides the essentials for a controllable, configurable, +asynchronous process. They are the result of an effort to distill the +common facets of process control currently duplicated in Kea's +DHCP servers into a reusable construct. The classes which form this abstract +base are shown in the following class diagram: + +@image html abstract_app_classes.svg "Controllable Process Layer Classes" + +- isc::process::DControllerBase - provides all of the services necessary to manage +an application process class derived from isc::d2::DProcess. These services include: + - Command line argument handling + - Process instantiation and initialization + - Support for stand-alone execution + - Process event loop invocation and shutdown + + It creates and manages an instance of isc::process::DProcessBase. The CPL is + designed for asynchronous event processing applications. It is constructed + to use ASIO library for IO processing. @c DControllerBase owns an + isc::asiolink::IOService instance and it passes this into the @c + DProcessBase constructor. It is this @c IOService that is used to drive the + process's event loop. The controller is designed to provide any interfaces + between the process it controls and the outside world. + + @c DControllerBase provides configuration for its process via a JSON file + specified as a mandatory command line argument. The file structure is + expected be as follows: + + { "<module-name>": {<module-config>} } + + where: + - module-name : is a label which uniquely identifies the + configuration data for the (i.e. the controlled process.) + It is the value returned by @ref + isc::process::DControllerBase::getAppName() + + - module-config: a set of zero or more JSON elements which comprise + application's configuration values. Element syntax is governed + by those elements supported in isc::cc. + + The file may contain an arbitrary number of other modules. + + @todo Eventually, some sort of secure socket interface which supports remote + control operations such as configuration changes or status reporting will + likely be implemented. + +- isc::process::DProcessBase - defines an asynchronous-event processor (i.e. +application) which provides a uniform interface to: + - Instantiate and initialize a process instance + - "Run" the application by starting its event loop + - Inject events to control the process +It owns an instance of @c DCfgMgrBase. + +- isc::process::DCfgMgrBase - provides the mechanisms for managing an application's +configuration. This includes services for parsing sets of configuration +values, storing the parsed information in its converted form, and retrieving +the information on demand. It owns an instance of @c DCfgContextBase, which +provides a "global" context for information that is accessible before, during, +and after parsing. + +- isc::process::DCfgContextBase - implements a container for configuration +information or "context". It provides a single enclosure for the storage of +configuration parameters or any other information that needs to accessible +within a given context. + +The following sequence diagram shows how a configuration from file moves +through the CPL layer: + +@image html config_from_file_sequence.svg "CPL Configuration From File Sequence" + +The CPL classes will likely move into a common library. + +@section cplSignals CPL Signal Handling + +CPL supports interaction with the outside world via OS signals. The default +implementation supports the following signal driven behavior: +- SIGHUP receipt of this signal will cause a reloading of the configuration +file. +- SIGINT/SIGTERM receipt of either of these signals will initiate an +orderly shutdown. + +CPL applications wait for for process asynchronous IO events through +isc::asiolink::IOService::run() or its variants. These calls are not +interrupted upon signal receipt as is the select() function and while +boost::asio provides a signal mechanism it requires linking in additional +libraries. Therefore, CPL provides its own signal handling mechanism to +propagate an OS signal such as SIGHUP to an IOService as a ready event with a +callback. + +isc::process::DControllerBase uses two mechanisms to carry out signal handling. It +uses isc::util::SignalSet to catch OS signals, and isc::process::IOSignalQueue to +propagate them to its isc::asiolink::IOService as instances of +isc::process::IOSignal. + +This CPL signaling class hierarchy is illustrated in the following diagram: + +@image html cpl_signal_classes.svg "CPL Signal Classes" + +The mechanics of isc::process::IOSignal are straight forward. Upon construction it +is given the target isc::asiolink::IOService, the value of the OS signal to +send (e.g. SIGINT, SIGHUP...), and an isc::process::IOSignalHandler. This handler +should contain the logic the caller would normally execute in its OS signal +handler. Each isc::process::IOSignal instance has a unique identifier called its +sequence_id. + +Internally, IOSignal creates a 1 ms, one-shot timer, on the given +IOService. When the timer expires its event handler invokes the caller's +IOSignalHandler passing it the sequence_id of the IOSignal. + +Sending IOSignals is done through an isc::process::IOSignalQueue. This class is +used to create the signals, house them until they are delivered, and dequeue +them so they can be been handled. To generate an IOSignal when an OS signal +arrives, the process's OS signal handler need only call +isc::process::IOSignalQueue::pushSignal() with the appropriate values. + +To dequeue the IOSignal inside the caller's IOSignalHandler, one simply +invokes isc::process::IOSignalQueue::popSignal() passing it the sequence_id +parameter passed to the handler. This method returns a pointer to +instigating IOSignal from which the value of OS signal (i.e. SIGINT, +SIGUSR1...) can be obtained. Note that calling popSignal() removes the +IOSignalPtr from the queue, which should reduce its reference count to +zero upon exiting the handler (unless a deliberate copy of it is made). + +A typical isc::process::IOSignalHandler might be structured as follows: +@code + + void processSignal(IOSignalId sequence_id) { + // Pop the signal instance off the queue. + IOSignalPtr signal = io_signal_queue_->popSignal(sequence_id); + + int os_signal_value = signal->getSignum(); + : + // logic based on the signal value + : + } + +@endcode + +IOSignal's handler invocation code will catch, log ,and then swallow any +exceptions thrown by an IOSignalHandler. This is done to protect the integrity +IOService context. + +The following sequence diagram depicts the initialization of signal handling +during startup and the subsequent receipt of a SIGHUP: + +@image html cpl_signal_sequence.svg "CPL Signal Handling Sequence" + +@section redact Redact Passwords + +There are two tools to remove sensitive data as passwords or secrets from logs: + - redactedAccessString for database access strings + - redactConfig for full configurations + +The jsonPathsToRedact method must be defined in derived classes following this +procedure: + - Get all possible JSON paths from the root of the configuration to leaves that + fulfill the role of map keys and which contain "password" or "secret". + - For each of these paths, remove the root node and the leaf node. + - Include all the paths in the method. Duplicate subpaths are expected in the + case of common subpaths to different leaves. + +There are two special syntaxes: + - "[]" suggests that the searched element is a list. This is required for all + lists and is for performance gain. + - "*" as a last element in a JSON path tells the redacter to look in all + elements that follow for elements that contain "password" and "secret". This is + when the particular configuration that is targeted by the "*" does not have a + well defined structure, such as is the case for "parameters" in the + "hooks-libraries" map in "Dhcp4" and "Dhcp6". + +@section cplMTConsiderations Multi-Threading Consideration for Controllable Process Layer + +By default this library is not thread safe and currently there is no known +exception. + +*/ diff --git a/src/lib/process/log_parser.cc b/src/lib/process/log_parser.cc new file mode 100644 index 0000000..6c05058 --- /dev/null +++ b/src/lib/process/log_parser.cc @@ -0,0 +1,163 @@ +// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <cc/data.h> +#include <process/log_parser.h> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <log/logger_specification.h> +#include <log/logger_support.h> +#include <log/logger_manager.h> +#include <log/logger_name.h> + +using namespace isc::data; +using namespace isc::log; + +namespace isc { +namespace process { + +LogConfigParser::LogConfigParser(const ConfigPtr& storage) + :config_(storage), verbose_(false) { + if (!storage) { + isc_throw(BadValue, "LogConfigParser needs a pointer to the " + "configuration, so parsed data can be stored there"); + } +} + +void LogConfigParser::parseConfiguration(const isc::data::ConstElementPtr& loggers, + bool verbose) { + verbose_ = verbose; + + // Iterate over all entries in "Server/loggers" list + BOOST_FOREACH(ConstElementPtr logger, loggers->listValue()) { + parseConfigEntry(logger); + } +} + +void LogConfigParser::parseConfigEntry(isc::data::ConstElementPtr entry) { + if (!entry) { + // This should not happen, but let's be on the safe side and check + return; + } + + if (!config_) { + isc_throw(BadValue, "configuration storage not set, can't parse logger config."); + } + + LoggingInfo info; + // Remove default destinations as we are going to replace them. + info.clearDestinations(); + + // Get user context + isc::data::ConstElementPtr user_context = entry->get("user-context"); + if (user_context) { + info.setContext(user_context); + } + + // Get a name + isc::data::ConstElementPtr name_ptr = entry->get("name"); + if (!name_ptr) { + isc_throw(BadValue, "loggers entry does not have a mandatory 'name' " + "element (" << entry->getPosition() << ")"); + } + info.name_ = name_ptr->stringValue(); + + // Get severity + isc::data::ConstElementPtr severity_ptr = entry->get("severity"); + if (!severity_ptr) { + isc_throw(BadValue, "loggers entry does not have a mandatory " + "'severity' element (" << entry->getPosition() << ")"); + } + try { + info.severity_ = isc::log::getSeverity(severity_ptr->stringValue().c_str()); + } catch (const std::exception&) { + isc_throw(BadValue, "Unsupported severity value '" + << severity_ptr->stringValue() << "' (" + << severity_ptr->getPosition() << ")"); + } + + // Get debug logging level + info.debuglevel_ = 0; + isc::data::ConstElementPtr debuglevel_ptr = entry->get("debuglevel"); + + // It's ok to not have debuglevel, we'll just assume its least verbose + // (0) level. + if (debuglevel_ptr) { + try { + info.debuglevel_ = boost::lexical_cast<int>(debuglevel_ptr->str()); + if ( (info.debuglevel_ < 0) || (info.debuglevel_ > 99) ) { + // Comment doesn't matter, it is caught several lines below + isc_throw(BadValue, ""); + } + } catch (...) { + isc_throw(BadValue, "Unsupported debuglevel value '" + << debuglevel_ptr->stringValue() + << "', expected 0-99 (" + << debuglevel_ptr->getPosition() << ")"); + } + } + + // We want to follow the normal path, so it could catch parsing errors even + // when verbose mode is enabled. If it is, just override whatever was parsed + // in the config file. + if (verbose_) { + info.severity_ = isc::log::DEBUG; + info.debuglevel_ = 99; + } + + isc::data::ConstElementPtr output_options = entry->get("output_options"); + + if (output_options) { + parseOutputOptions(info.destinations_, output_options); + } + + config_->addLoggingInfo(info); +} + +void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination, + isc::data::ConstElementPtr output_options) { + if (!output_options) { + isc_throw(BadValue, "Missing 'output_options' structure in 'loggers'"); + } + + BOOST_FOREACH(ConstElementPtr output_option, output_options->listValue()) { + + LoggingDestination dest; + + isc::data::ConstElementPtr output = output_option->get("output"); + if (!output) { + isc_throw(BadValue, "output_options entry does not have a mandatory 'output' " + "element (" << output_option->getPosition() << ")"); + } + dest.output_ = output->stringValue(); + + isc::data::ConstElementPtr maxver_ptr = output_option->get("maxver"); + if (maxver_ptr) { + dest.maxver_ = boost::lexical_cast<int>(maxver_ptr->str()); + } + + isc::data::ConstElementPtr maxsize_ptr = output_option->get("maxsize"); + if (maxsize_ptr) { + dest.maxsize_ = boost::lexical_cast<uint64_t>(maxsize_ptr->str()); + } + + isc::data::ConstElementPtr flush_ptr = output_option->get("flush"); + if (flush_ptr) { + dest.flush_ = flush_ptr->boolValue(); + } + + isc::data::ConstElementPtr pattern = output_option->get("pattern"); + if (pattern) { + dest.pattern_ = pattern->stringValue(); + } + + destination.push_back(dest); + } +} + +} // namespace isc::dhcp +} // namespace isc diff --git a/src/lib/process/log_parser.h b/src/lib/process/log_parser.h new file mode 100644 index 0000000..92209bd --- /dev/null +++ b/src/lib/process/log_parser.h @@ -0,0 +1,93 @@ +// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DHCPSRV_LOGGING_H +#define DHCPSRV_LOGGING_H + +#include <cc/data.h> +#include <process/logging_info.h> +#include <process/config_base.h> +#include <vector> + +namespace isc { +namespace process { + +/// @brief Configures log4cplus by translating Kea configuration structures +/// +/// This parser iterates over provided data elements and translates them +/// into values applicable to log4cplus. +/// +/// The data structures converted to JSON format have the following syntax: +/// { +/// "name": "kea", +/// "output_options": [ +/// { +/// "output": "/home/thomson/kea-inst/kea-warn.log", +/// "maxver": 8, +/// "maxsize": 204800, +/// "flush": true +/// } +/// ], +/// "severity": "WARN" +/// } +/// +/// This is only an example and actual values may be different. +/// +/// The data structures don't have to originate from JSON. JSON is just a +/// convenient presentation syntax. +/// +/// This class uses @c ConfigBase object to store logging configuration. +class LogConfigParser { +public: + + /// @brief Constructor + /// + /// @param storage parsed logging configuration will be stored here + LogConfigParser(const ConfigPtr& storage); + + /// @brief Parses specified configuration + /// + /// Walks over specified logging configuration JSON structures and store + /// parsed information in config_->logging_info_. + /// + /// @param log_config JSON structures to be parsed (loggers list) + /// @param verbose specifies verbose mode (true forces DEBUG, debuglevel = 99) + void parseConfiguration(const isc::data::ConstElementPtr& log_config, + bool verbose = false); + +private: + + /// @brief Parses one JSON structure in Server/loggers" array + /// + /// @param entry JSON structure to be parsed + /// @brief parses one structure in Server/loggers. + void parseConfigEntry(isc::data::ConstElementPtr entry); + + /// @brief Parses output_options structure + /// + /// @ref @c LogConfigParser for an example in JSON format. + /// + /// @param destination parsed parameters will be stored here + /// @param output_options element to be parsed + void parseOutputOptions(std::vector<LoggingDestination>& destination, + isc::data::ConstElementPtr output_options); + + /// @brief Configuration is stored here + /// + /// LogConfigParser class uses only config_->logging_info_ field. + ConfigPtr config_; + + /// @brief Verbose mode + /// + /// When verbose mode is enabled, logging severity is overridden to DEBUG, + /// and debuglevel is always 99. + bool verbose_; +}; + +} // namespace isc::dhcp +} // namespace isc + +#endif // DHCPSRV_LOGGING_H diff --git a/src/lib/process/logging_info.cc b/src/lib/process/logging_info.cc new file mode 100644 index 0000000..a32119d --- /dev/null +++ b/src/lib/process/logging_info.cc @@ -0,0 +1,212 @@ +// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <process/logging_info.h> +#include <process/daemon.h> +#include <log/logger_name.h> + +using namespace isc::log; +using namespace isc::data; + +namespace isc { +namespace process { + +static const std::string STDOUT = "stdout"; +static const std::string STDERR = "stderr"; +static const std::string SYSLOG = "syslog"; +static const std::string SYSLOG_COLON = "syslog:"; + +bool +LoggingDestination::equals(const LoggingDestination& other) const { + return (output_ == other.output_ && + maxver_ == other.maxver_ && + maxsize_ == other.maxsize_ && + flush_ == other.flush_ && + pattern_ == other.pattern_); +} + +ElementPtr +LoggingDestination::toElement() const { + ElementPtr result = Element::createMap(); + + // Set output + result->set("output", Element::create(output_)); + + // Set flush + result->set("flush", Element::create(flush_)); + + // Set pattern + result->set("pattern", Element::create(pattern_)); + + if ((output_ != STDOUT) && (output_ != STDERR) && (output_ != SYSLOG) && + (output_.find(SYSLOG_COLON) == std::string::npos)) { + // Set maxver + result->set("maxver", Element::create(maxver_)); + + // Set maxsize + result->set("maxsize", Element::create(static_cast<long long>(maxsize_))); + } + + return (result); +} + +LoggingInfo::LoggingInfo() + : name_("kea"), severity_(isc::log::INFO), debuglevel_(0) { + // If configuration Manager is in the verbose mode, we need to modify the + // default settings. + if (Daemon::getVerbose()) { + severity_ = isc::log::DEBUG; + debuglevel_ = 99; + } + + // If the process has set the non-empty name for the default logger, + // let's use this name. + std::string default_logger = Daemon::getDefaultLoggerName(); + if (!default_logger.empty()) { + name_ = default_logger; + } + + // Add a default logging destination in case use hasn't provided a + // logger specification. + LoggingDestination dest; + dest.output_ = "stdout"; + destinations_.push_back(dest); +} + +bool +LoggingInfo::equals(const LoggingInfo& other) const { + // If number of destinations aren't equal, the objects are not equal. + if (destinations_.size() != other.destinations_.size()) { + return (false); + } + // If there is the same number of logging destinations verify that the + // destinations are equal. The order doesn't matter to we don't expect + // that they are at the same index of the vectors. + for (auto const& dest : destinations_) { + bool match = false; + for (auto const &dest_other : other.destinations_) { + if (dest.equals(dest_other)) { + match = true; + break; + } + } + if (!match) { + return (false); + } + } + + // Logging destinations are equal. Check the rest of the parameters for + // equality. + return (name_ == other.name_ && + severity_ == other.severity_ && + debuglevel_ == other.debuglevel_); +} + +LoggerSpecification +LoggingInfo::toSpec() const { + LoggerSpecification spec(name_, severity_, debuglevel_); + + // Go over logger destinations and create output options accordingly. + for (auto const& dest : destinations_) { + OutputOption option; + // Set up output option according to destination specification + if (dest.output_ == STDOUT) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDOUT; + + } else if (dest.output_ == STDERR) { + option.destination = OutputOption::DEST_CONSOLE; + option.stream = OutputOption::STR_STDERR; + + } else if (dest.output_ == SYSLOG) { + option.destination = OutputOption::DEST_SYSLOG; + // Use default specified in OutputOption constructor for the + // syslog destination + + } else if (dest.output_.find(SYSLOG_COLON) == 0) { + option.destination = OutputOption::DEST_SYSLOG; + // Must take account of the string actually being "syslog:" + if (dest.output_ == SYSLOG_COLON) { + // The expected syntax is syslog:facility. User skipped + // the logging name, so we'll just use the default ("kea") + option.facility = isc::log::getDefaultRootLoggerName(); + + } else { + // Everything else in the string is the facility name + option.facility = dest.output_.substr(SYSLOG_COLON.size()); + } + + } else { + // Not a recognized destination, assume a file. + option.destination = OutputOption::DEST_FILE; + option.filename = dest.output_; + option.maxsize = dest.maxsize_; + option.maxver = dest.maxver_; + } + + // Copy the immediate flush flag + option.flush = dest.flush_; + + // Copy the pattern + option.pattern = dest.pattern_; + + // ... and set the destination + spec.addOutputOption(option); + } + + return (spec); +} + +ElementPtr +LoggingInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set user context + contextToElement(result); + // Set name + result->set("name", Element::create(name_)); + // Set output_options if not empty + if (!destinations_.empty()) { + ElementPtr options = Element::createList(); + for (auto const& dest : destinations_) { + options->add(dest.toElement()); + } + result->set("output_options", options); + } + // Set severity + std::string severity; + switch (severity_) { + case isc::log::DEBUG: + severity = "DEBUG"; + break; + case isc::log::INFO: + severity = "INFO"; + break; + case isc::log::WARN: + severity = "WARN"; + break; + case isc::log::ERROR: + severity = "ERROR"; + break; + case isc::log::FATAL: + severity = "FATAL"; + break; + case isc::log::NONE: + severity = "NONE"; + break; + default: + isc_throw(ToElementError, "illegal severity: " << severity_); + break; + } + result->set("severity", Element::create(severity)); + // Set debug level + result->set("debuglevel", Element::create(debuglevel_)); + + return (result); +} + +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/lib/process/logging_info.h b/src/lib/process/logging_info.h new file mode 100644 index 0000000..7660fe0 --- /dev/null +++ b/src/lib/process/logging_info.h @@ -0,0 +1,146 @@ +// Copyright (C) 2014-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DHCPSRV_LOGGING_INFO_H +#define DHCPSRV_LOGGING_INFO_H + +#include <log/logger_level.h> +#include <log/logger_specification.h> +#include <cc/cfg_to_element.h> +#include <cc/user_context.h> +#include <stdint.h> +#include <vector> + +namespace isc { +namespace process { + +/// @brief Defines single logging destination +/// +/// This structure is used to keep log4cplus configuration parameters. +struct LoggingDestination : public isc::data::CfgToElement { +public: + + /// @brief defines logging destination output + /// + /// Values accepted are: stdout, stderr, syslog, syslog:name. + /// Any other destination will be considered a file name. + std::string output_; + + /// @brief Maximum number of log files in rotation + int maxver_; + + /// @brief Maximum log file size + uint64_t maxsize_; + + /// @brief Immediate flush + bool flush_; + + /// @brief defines the log format pattern + /// It dictates what additional elements are output + std::string pattern_; + + /// @brief Compares two objects for equality. + /// + /// @param other Object to be compared with this object. + /// + /// @return true if objects are equal, false otherwise. + bool equals(const LoggingDestination& other) const; + + /// @brief Default constructor. + LoggingDestination() + : output_("stdout"), maxver_(1), maxsize_(10240000), flush_(true), pattern_("") { + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; +}; + +/// @brief structure that describes one logging entry +/// +/// This is a structure that conveys one logger entry configuration. +/// The structure in JSON form has the following syntax: +/// { +/// "name": "*", +/// "output_options": [ +/// { +/// "output": "/path/to/the/logfile.log", +/// "maxver": 8, +/// "maxsize": 204800, +/// "flush": true +/// "pattern": "%-5p [%c] %m\n" +/// } +/// ], +/// "severity": "WARN", +/// "debuglevel": 99 +/// }, +class LoggingInfo : public isc::data::UserContext, public isc::data::CfgToElement { +public: + + /// @brief logging name + std::string name_; + + /// @brief describes logging severity + isc::log::Severity severity_; + + /// @brief debuglevel (used when severity_ == DEBUG) + /// + /// We use range 0(least verbose)..99(most verbose) + int debuglevel_; + + /// @brief specific logging destinations + std::vector<LoggingDestination> destinations_; + + /// @brief Default constructor. + LoggingInfo(); + + /// @brief Removes logging destinations. + void clearDestinations() { + destinations_.clear(); + } + + /// @brief Compares two objects for equality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are equal, false otherwise. + bool equals(const LoggingInfo& other) const; + + /// @brief Compares two objects for equality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are equal, false otherwise. + bool operator==(const LoggingInfo& other) const { + return (equals(other)); + } + + /// @brief Compares two objects for inequality. + /// + /// @param other An object to be compared with this object. + /// + /// @return true if objects are not equal, false otherwise. + bool operator!=(const LoggingInfo& other) const { + return (!equals(other)); + } + + /// @brief Converts logger configuration to a spec. + isc::log::LoggerSpecification toSpec() const; + + /// @brief Unparse a configuration object + /// + /// @return a pointer to unparsed configuration + virtual isc::data::ElementPtr toElement() const; +}; + +/// @brief storage for logging information in log4cplus format +typedef std::vector<isc::process::LoggingInfo> LoggingInfoStorage; + +} +} + +#endif // DHCPSRV_LOGGING_INFO_H diff --git a/src/lib/process/process_messages.cc b/src/lib/process/process_messages.cc new file mode 100644 index 0000000..a4ee304 --- /dev/null +++ b/src/lib/process/process_messages.cc @@ -0,0 +1,83 @@ +// File created from ../../../src/lib/process/process_messages.mes + +#include <cstddef> +#include <log/message_types.h> +#include <log/message_initializer.h> + +namespace isc { +namespace process { + +extern const isc::log::MessageID DCTL_ALREADY_RUNNING = "DCTL_ALREADY_RUNNING"; +extern const isc::log::MessageID DCTL_CCSESSION_ENDING = "DCTL_CCSESSION_ENDING"; +extern const isc::log::MessageID DCTL_CFG_FILE_RELOAD_ERROR = "DCTL_CFG_FILE_RELOAD_ERROR"; +extern const isc::log::MessageID DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD = "DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD"; +extern const isc::log::MessageID DCTL_COMMAND_RECEIVED = "DCTL_COMMAND_RECEIVED"; +extern const isc::log::MessageID DCTL_CONFIG_CHECK_COMPLETE = "DCTL_CONFIG_CHECK_COMPLETE"; +extern const isc::log::MessageID DCTL_CONFIG_COMPLETE = "DCTL_CONFIG_COMPLETE"; +extern const isc::log::MessageID DCTL_CONFIG_DEPRECATED = "DCTL_CONFIG_DEPRECATED"; +extern const isc::log::MessageID DCTL_CONFIG_FETCH = "DCTL_CONFIG_FETCH"; +extern const isc::log::MessageID DCTL_CONFIG_FILE_LOAD_FAIL = "DCTL_CONFIG_FILE_LOAD_FAIL"; +extern const isc::log::MessageID DCTL_CONFIG_LOAD_FAIL = "DCTL_CONFIG_LOAD_FAIL"; +extern const isc::log::MessageID DCTL_CONFIG_START = "DCTL_CONFIG_START"; +extern const isc::log::MessageID DCTL_CONFIG_STUB = "DCTL_CONFIG_STUB"; +extern const isc::log::MessageID DCTL_CONFIG_UPDATE = "DCTL_CONFIG_UPDATE"; +extern const isc::log::MessageID DCTL_DEVELOPMENT_VERSION = "DCTL_DEVELOPMENT_VERSION"; +extern const isc::log::MessageID DCTL_INIT_PROCESS = "DCTL_INIT_PROCESS"; +extern const isc::log::MessageID DCTL_INIT_PROCESS_FAIL = "DCTL_INIT_PROCESS_FAIL"; +extern const isc::log::MessageID DCTL_NOT_RUNNING = "DCTL_NOT_RUNNING"; +extern const isc::log::MessageID DCTL_OPEN_CONFIG_DB = "DCTL_OPEN_CONFIG_DB"; +extern const isc::log::MessageID DCTL_PARSER_FAIL = "DCTL_PARSER_FAIL"; +extern const isc::log::MessageID DCTL_PID_FILE_ERROR = "DCTL_PID_FILE_ERROR"; +extern const isc::log::MessageID DCTL_PROCESS_FAILED = "DCTL_PROCESS_FAILED"; +extern const isc::log::MessageID DCTL_RUN_PROCESS = "DCTL_RUN_PROCESS"; +extern const isc::log::MessageID DCTL_SESSION_FAIL = "DCTL_SESSION_FAIL"; +extern const isc::log::MessageID DCTL_SHUTDOWN = "DCTL_SHUTDOWN"; +extern const isc::log::MessageID DCTL_SHUTDOWN_SIGNAL_RECVD = "DCTL_SHUTDOWN_SIGNAL_RECVD"; +extern const isc::log::MessageID DCTL_STANDALONE = "DCTL_STANDALONE"; +extern const isc::log::MessageID DCTL_STARTING = "DCTL_STARTING"; +extern const isc::log::MessageID DCTL_UNLOAD_LIBRARIES_ERROR = "DCTL_UNLOAD_LIBRARIES_ERROR"; +extern const isc::log::MessageID DCTL_UNSUPPORTED_SIGNAL = "DCTL_UNSUPPORTED_SIGNAL"; + +} // namespace process +} // namespace isc + +namespace { + +const char* values[] = { + "DCTL_ALREADY_RUNNING", "%1 already running? %2", + "DCTL_CCSESSION_ENDING", "%1 ending control channel session", + "DCTL_CFG_FILE_RELOAD_ERROR", "configuration reload failed: %1, reverting to current configuration.", + "DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD", "OS signal %1 received, reloading configuration from file: %2", + "DCTL_COMMAND_RECEIVED", "%1 received command: %2, arguments: %3", + "DCTL_CONFIG_CHECK_COMPLETE", "server has completed configuration check: %1, result: %2", + "DCTL_CONFIG_COMPLETE", "server has completed configuration: %1", + "DCTL_CONFIG_DEPRECATED", "server configuration includes a deprecated object: %1", + "DCTL_CONFIG_FETCH", "Fetching configuration data from config backends.", + "DCTL_CONFIG_FILE_LOAD_FAIL", "%1 reason: %2", + "DCTL_CONFIG_LOAD_FAIL", "%1 configuration failed to load: %2", + "DCTL_CONFIG_START", "parsing new configuration: %1", + "DCTL_CONFIG_STUB", "%1 configuration stub handler called", + "DCTL_CONFIG_UPDATE", "%1 updated configuration received: %2", + "DCTL_DEVELOPMENT_VERSION", "This software is a development branch of Kea. It is not recommended for production use.", + "DCTL_INIT_PROCESS", "%1 initializing the application", + "DCTL_INIT_PROCESS_FAIL", "%1 application initialization failed: %2", + "DCTL_NOT_RUNNING", "%1 application instance is not running", + "DCTL_OPEN_CONFIG_DB", "Opening configuration database: %1", + "DCTL_PARSER_FAIL", ": %1", + "DCTL_PID_FILE_ERROR", "%1 could not create a PID file: %2", + "DCTL_PROCESS_FAILED", "%1 application execution failed: %2", + "DCTL_RUN_PROCESS", "%1 starting application event loop", + "DCTL_SESSION_FAIL", "%1 controller failed to establish Kea session: %1", + "DCTL_SHUTDOWN", "%1 has shut down, pid: %2, version: %3", + "DCTL_SHUTDOWN_SIGNAL_RECVD", "OS signal %1 received, starting shutdown", + "DCTL_STANDALONE", "%1 skipping message queue, running standalone", + "DCTL_STARTING", "%1 starting, pid: %2, version: %3 (%4)", + "DCTL_UNLOAD_LIBRARIES_ERROR", "error unloading hooks libraries during shutdown: %1", + "DCTL_UNSUPPORTED_SIGNAL", "ignoring reception of unsupported signal: %1", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/lib/process/process_messages.h b/src/lib/process/process_messages.h new file mode 100644 index 0000000..0ae950a --- /dev/null +++ b/src/lib/process/process_messages.h @@ -0,0 +1,45 @@ +// File created from ../../../src/lib/process/process_messages.mes + +#ifndef PROCESS_MESSAGES_H +#define PROCESS_MESSAGES_H + +#include <log/message_types.h> + +namespace isc { +namespace process { + +extern const isc::log::MessageID DCTL_ALREADY_RUNNING; +extern const isc::log::MessageID DCTL_CCSESSION_ENDING; +extern const isc::log::MessageID DCTL_CFG_FILE_RELOAD_ERROR; +extern const isc::log::MessageID DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD; +extern const isc::log::MessageID DCTL_COMMAND_RECEIVED; +extern const isc::log::MessageID DCTL_CONFIG_CHECK_COMPLETE; +extern const isc::log::MessageID DCTL_CONFIG_COMPLETE; +extern const isc::log::MessageID DCTL_CONFIG_DEPRECATED; +extern const isc::log::MessageID DCTL_CONFIG_FETCH; +extern const isc::log::MessageID DCTL_CONFIG_FILE_LOAD_FAIL; +extern const isc::log::MessageID DCTL_CONFIG_LOAD_FAIL; +extern const isc::log::MessageID DCTL_CONFIG_START; +extern const isc::log::MessageID DCTL_CONFIG_STUB; +extern const isc::log::MessageID DCTL_CONFIG_UPDATE; +extern const isc::log::MessageID DCTL_DEVELOPMENT_VERSION; +extern const isc::log::MessageID DCTL_INIT_PROCESS; +extern const isc::log::MessageID DCTL_INIT_PROCESS_FAIL; +extern const isc::log::MessageID DCTL_NOT_RUNNING; +extern const isc::log::MessageID DCTL_OPEN_CONFIG_DB; +extern const isc::log::MessageID DCTL_PARSER_FAIL; +extern const isc::log::MessageID DCTL_PID_FILE_ERROR; +extern const isc::log::MessageID DCTL_PROCESS_FAILED; +extern const isc::log::MessageID DCTL_RUN_PROCESS; +extern const isc::log::MessageID DCTL_SESSION_FAIL; +extern const isc::log::MessageID DCTL_SHUTDOWN; +extern const isc::log::MessageID DCTL_SHUTDOWN_SIGNAL_RECVD; +extern const isc::log::MessageID DCTL_STANDALONE; +extern const isc::log::MessageID DCTL_STARTING; +extern const isc::log::MessageID DCTL_UNLOAD_LIBRARIES_ERROR; +extern const isc::log::MessageID DCTL_UNSUPPORTED_SIGNAL; + +} // namespace process +} // namespace isc + +#endif // PROCESS_MESSAGES_H diff --git a/src/lib/process/process_messages.mes b/src/lib/process/process_messages.mes new file mode 100644 index 0000000..1d601ab --- /dev/null +++ b/src/lib/process/process_messages.mes @@ -0,0 +1,156 @@ +# Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$NAMESPACE isc::process + +% DCTL_ALREADY_RUNNING %1 already running? %2 +This is an error message that occurs when a module encounters a pre-existing +PID file which contains the PID of a running process. This most likely +indicates an attempt to start a second instance of a module using the +same configuration file. It is possible, though unlikely, that the PID file +is a remnant left behind by a server crash or power failure and the PID +it contains refers to a process other than Kea process. In such an event, +it would be necessary to manually remove the PID file. The first argument is +the process name, the second contains the PID and PID file. + +% DCTL_CCSESSION_ENDING %1 ending control channel session +This debug message is issued just before the controller attempts +to disconnect from its session with the Kea control channel. + +% DCTL_CFG_FILE_RELOAD_ERROR configuration reload failed: %1, reverting to current configuration. +This is an error message indicating that the application attempted to reload +its configuration from file and encountered an error. This is likely due to +invalid content in the configuration file. The application should continue +to operate under its current configuration. + +% DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD OS signal %1 received, reloading configuration from file: %2 +This is an informational message indicating the application has received a signal +instructing it to reload its configuration from file. + +% DCTL_COMMAND_RECEIVED %1 received command: %2, arguments: %3 +A debug message listing the command (and possible arguments) received +from the Kea control system by the controller. + +% DCTL_CONFIG_CHECK_COMPLETE server has completed configuration check: %1, result: %2 +This is an informational message announcing the successful processing of a +new configuration check is complete. The result of that check is printed. +This informational message is printed when configuration check is requested. + +% DCTL_CONFIG_COMPLETE server has completed configuration: %1 +This is an informational message announcing the successful processing of a +new configuration. It is output during server startup, and when an updated +configuration is committed by the administrator. Additional information +may be provided. + +% DCTL_CONFIG_DEPRECATED server configuration includes a deprecated object: %1 +This error message is issued when the configuration includes a deprecated +object (i.e. a top level element) which will be ignored. + +% DCTL_CONFIG_FETCH Fetching configuration data from config backends. +This is an informational message emitted when the Kea server is about to begin +retrieving configuration data from one or more configuration backends. + +% DCTL_CONFIG_FILE_LOAD_FAIL %1 reason: %2 +This fatal error message indicates that the application attempted to load its +initial configuration from file and has failed. The service will exit. + +% DCTL_CONFIG_LOAD_FAIL %1 configuration failed to load: %2 +This critical error message indicates that the initial application +configuration has failed. The service will start, but will not +process requests until the configuration has been corrected. + +% DCTL_CONFIG_START parsing new configuration: %1 +A debug message indicating that the application process has received an +updated configuration and has passed it to its configuration manager +for parsing. + +% DCTL_CONFIG_STUB %1 configuration stub handler called +This debug message is issued when the dummy handler for configuration +events is called. This only happens during initial startup. + +% DCTL_CONFIG_UPDATE %1 updated configuration received: %2 +A debug message indicating that the controller has received an +updated configuration from the Kea configuration system. + +% DCTL_DEVELOPMENT_VERSION This software is a development branch of Kea. It is not recommended for production use. +This warning message is displayed when the version is a development +(vs stable) one: the second number of the version is odd. + +% DCTL_INIT_PROCESS %1 initializing the application +This debug message is issued just before the controller attempts +to create and initialize its application instance. + +% DCTL_INIT_PROCESS_FAIL %1 application initialization failed: %2 +This error message is issued if the controller could not initialize the +application and will exit. + +% DCTL_NOT_RUNNING %1 application instance is not running +A warning message is issued when an attempt is made to shut down the +application when it is not running. + +% DCTL_OPEN_CONFIG_DB Opening configuration database: %1 +This message is printed when the Kea server is attempting to open a +configuration database. The database access string with password redacted +is logged. + +% DCTL_PARSER_FAIL : %1 +On receipt of a new configuration, the server failed to create a parser to +decode the contents of the named configuration element, or the creation +succeeded but the parsing actions and committal of changes failed. +The reason for the failure is given in the message. + +% DCTL_PID_FILE_ERROR %1 could not create a PID file: %2 +This is an error message that occurs when the server is unable to create +its PID file. The log message should contain details sufficient to +determine the underlying cause. The most likely culprits are that +some portion of the pathname does not exist or a permissions issue. The +default path is determined by --localstatedir or --runstatedir configure +parameters but may be overridden by setting environment variable, +KEA_PIDFILE_DIR. The first argument is the process name. + +% DCTL_PROCESS_FAILED %1 application execution failed: %2 +The controller has encountered a fatal error while running the +application and is terminating. The reason for the failure is +included in the message. + +% DCTL_RUN_PROCESS %1 starting application event loop +This debug message is issued just before the controller invokes +the application run method. + +% DCTL_SESSION_FAIL %1 controller failed to establish Kea session: %1 +The controller has failed to establish communication with the rest of +Kea and will exit. + +% DCTL_SHUTDOWN %1 has shut down, pid: %2, version: %3 +This is an informational message indicating that the service has shut +down. The argument specifies a name of the service. + +% DCTL_SHUTDOWN_SIGNAL_RECVD OS signal %1 received, starting shutdown +This is a debug message indicating the application has received a signal +instructing it to shutdown. + +% DCTL_STANDALONE %1 skipping message queue, running standalone +This is a debug message indicating that the controller is running in the +application in standalone mode. This means it will not connected to the Kea +message queue. Standalone mode is only useful during program development, +and should not be used in a production environment. + +% DCTL_STARTING %1 starting, pid: %2, version: %3 (%4) +This is an informational message issued when controller for the +service first starts. Version is also reported. + +% DCTL_UNLOAD_LIBRARIES_ERROR error unloading hooks libraries during shutdown: %1 +This error message indicates that during shutdown, unloading hooks +libraries failed to close them. If the list of libraries is empty it is +a programmatic error in the server code. If it is not empty it could be +a programmatic error in one of the hooks libraries which could lead to +a crash during finalization. + +% DCTL_UNSUPPORTED_SIGNAL ignoring reception of unsupported signal: %1 +This is a debug message indicating that the application received an +unsupported signal. This is a programming error indicating that the +application has registered to receive the signal but no associated +processing logic has been added. diff --git a/src/lib/process/redact_config.cc b/src/lib/process/redact_config.cc new file mode 100644 index 0000000..16bba0f --- /dev/null +++ b/src/lib/process/redact_config.cc @@ -0,0 +1,97 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <process/redact_config.h> + +#include <boost/algorithm/string.hpp> + +using namespace isc; +using namespace isc::data; +using namespace std; + +namespace { + +template <typename ElementPtrType> +ElementPtrType +redact(ElementPtrType const& element, list<string> json_path) { + if (!element) { + isc_throw(BadValue, "redact() got a null pointer"); + } + + string const next_key(json_path.empty() ? string() : json_path.front()); + ElementPtr result; + if (element->getType() == Element::list) { + // If we are looking for a list... + if (next_key == "*" || next_key == "[]") { + // But if we are looking specifically for a list... + if (next_key == "[]") { + // Then advance in the path. + json_path.pop_front(); + } + // Then redact all children. + result = Element::createList(); + for (ElementPtr const& child : element->listValue()) { + result->add(redact(child, json_path)); + } + return result; + } + } else if (element->getType() == Element::map) { + // If we are looking for anything or if we have reached the end of a + /// path... + if (next_key == "*" || json_path.empty()) { + // Then iterate through all the children. + result = Element::createMap(); + for (auto kv : element->mapValue()) { + std::string const& key(kv.first); + ConstElementPtr const& value(kv.second); + + if (boost::algorithm::ends_with(key, "password") || + boost::algorithm::ends_with(key, "secret")) { + // Sensitive data + result->set(key, Element::create(string("*****"))); + } else if (key == "user-context") { + // Skip user contexts. + result->set(key, value); + } else { + if (json_path.empty()) { + // End of path means no sensitive data expected in this + // subtree, so we stop here. + result->set(key, value); + } else { + // We are looking for anything '*' so redact further. + result->set(key, redact(value, json_path)); + } + } + } + return result; + } else { + ConstElementPtr child(element->get(next_key)); + if (child) { + result = isc::data::copy(element, 1); + json_path.pop_front(); + result->set(next_key, redact(child, json_path)); + return result; + } + } + } + + return element; +} + +} // namespace + +namespace isc { +namespace process { + +ConstElementPtr +redactConfig(ConstElementPtr const& element, list<string> const& json_path) { + return redact(element, json_path); +} + +} // namespace process +} // namespace isc diff --git a/src/lib/process/redact_config.h b/src/lib/process/redact_config.h new file mode 100644 index 0000000..a0d1d0a --- /dev/null +++ b/src/lib/process/redact_config.h @@ -0,0 +1,37 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef REDACT_CONFIG_H +#define REDACT_CONFIG_H + +#include <cc/data.h> +#include <list> + +namespace isc { +namespace process { + +/// @brief Redact a configuration. +/// +/// This method walks on the configuration tree: +/// - it copies only subtrees where a change was done. +/// - it replaces passwords and secrets by asterisks. +/// - it skips user context. +/// - if a not empty list of keywords is given it follows only them. +/// +/// @param element initially the Element tree structure that describe the +/// configuration and smaller subtrees in recursive calls. +/// @param json_path JSON path to redact +/// +/// @return a copy of the config where passwords and secrets were replaced by +/// asterisks so it can be safely logged to an unprivileged place. +isc::data::ConstElementPtr +redactConfig(isc::data::ConstElementPtr const& element, + std::list<std::string> const& json_path = {"*"}); + +} // namespace process +} // namespace isc + +#endif // REDACT_CONFIG_H diff --git a/src/lib/process/tests/Makefile.am b/src/lib/process/tests/Makefile.am new file mode 100644 index 0000000..4d01645 --- /dev/null +++ b/src/lib/process/tests/Makefile.am @@ -0,0 +1,58 @@ +SUBDIRS = . +dhcp_data_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/process/tests\" +AM_CPPFLAGS += -DDATA_DIR="\"$(dhcp_data_dir)\"" +AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += libprocess_unittests + +libprocess_unittests_SOURCES = d_cfg_mgr_unittests.cc +libprocess_unittests_SOURCES += cb_ctl_base_unittests.cc +libprocess_unittests_SOURCES += config_base_unittests.cc +libprocess_unittests_SOURCES += config_ctl_info_unittests.cc +libprocess_unittests_SOURCES += config_ctl_parser_unittests.cc +libprocess_unittests_SOURCES += d_controller_unittests.cc +libprocess_unittests_SOURCES += daemon_unittest.cc +libprocess_unittests_SOURCES += log_parser_unittests.cc +libprocess_unittests_SOURCES += logging_info_unittests.cc +libprocess_unittests_SOURCES += run_unittests.cc + +libprocess_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) + +libprocess_unittests_CXXFLAGS = $(AM_CXXFLAGS) + +libprocess_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) + +libprocess_unittests_LDADD = $(top_builddir)/src/lib/process/testutils/libprocesstest.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +libprocess_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libprocess_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) +libprocess_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD) +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/process/tests/Makefile.in b/src/lib/process/tests/Makefile.in new file mode 100644 index 0000000..c9baadf --- /dev/null +++ b/src/lib/process/tests/Makefile.in @@ -0,0 +1,1164 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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@ + +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@ +TESTS = $(am__EXEEXT_1) +@HAVE_GTEST_TRUE@am__append_1 = libprocess_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/lib/process/tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = libprocess_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__libprocess_unittests_SOURCES_DIST = d_cfg_mgr_unittests.cc \ + cb_ctl_base_unittests.cc config_base_unittests.cc \ + config_ctl_info_unittests.cc config_ctl_parser_unittests.cc \ + d_controller_unittests.cc daemon_unittest.cc \ + log_parser_unittests.cc logging_info_unittests.cc \ + run_unittests.cc +@HAVE_GTEST_TRUE@am_libprocess_unittests_OBJECTS = libprocess_unittests-d_cfg_mgr_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-cb_ctl_base_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-config_base_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-config_ctl_info_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-config_ctl_parser_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-d_controller_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-daemon_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-log_parser_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-logging_info_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libprocess_unittests-run_unittests.$(OBJEXT) +libprocess_unittests_OBJECTS = $(am_libprocess_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libprocess_unittests_DEPENDENCIES = $(top_builddir)/src/lib/process/testutils/libprocesstest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/testutils/libkea-testutils.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libprocess_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) \ + $(libprocess_unittests_LDFLAGS) $(LDFLAGS) -o $@ +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 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = \ + ./$(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-config_base_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-d_controller_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-daemon_unittest.Po \ + ./$(DEPDIR)/libprocess_unittests-log_parser_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-logging_info_unittests.Po \ + ./$(DEPDIR)/libprocess_unittests-run_unittests.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libprocess_unittests_SOURCES) +DIST_SOURCES = $(am__libprocess_unittests_SOURCES_DIST) +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 +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 distdir-am +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 +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +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@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +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@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +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@ +SUBDIRS = . +dhcp_data_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) \ + -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/process/tests\" \ + -DDATA_DIR="\"$(dhcp_data_dir)\"" \ + -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +@HAVE_GTEST_TRUE@libprocess_unittests_SOURCES = \ +@HAVE_GTEST_TRUE@ d_cfg_mgr_unittests.cc \ +@HAVE_GTEST_TRUE@ cb_ctl_base_unittests.cc \ +@HAVE_GTEST_TRUE@ config_base_unittests.cc \ +@HAVE_GTEST_TRUE@ config_ctl_info_unittests.cc \ +@HAVE_GTEST_TRUE@ config_ctl_parser_unittests.cc \ +@HAVE_GTEST_TRUE@ d_controller_unittests.cc daemon_unittest.cc \ +@HAVE_GTEST_TRUE@ log_parser_unittests.cc \ +@HAVE_GTEST_TRUE@ logging_info_unittests.cc run_unittests.cc +@HAVE_GTEST_TRUE@libprocess_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libprocess_unittests_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libprocess_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@libprocess_unittests_LDADD = $(top_builddir)/src/lib/process/testutils/libprocesstest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/testutils/libkea-testutils.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) \ +@HAVE_GTEST_TRUE@ $(BOOST_LIBS) $(GTEST_LDADD) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(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) --foreign src/lib/process/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/process/tests/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +libprocess_unittests$(EXEEXT): $(libprocess_unittests_OBJECTS) $(libprocess_unittests_DEPENDENCIES) $(EXTRA_libprocess_unittests_DEPENDENCIES) + @rm -f libprocess_unittests$(EXEEXT) + $(AM_V_CXXLD)$(libprocess_unittests_LINK) $(libprocess_unittests_OBJECTS) $(libprocess_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-config_base_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-d_controller_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-daemon_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-log_parser_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-logging_info_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocess_unittests-run_unittests.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libprocess_unittests-d_cfg_mgr_unittests.o: d_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-d_cfg_mgr_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Tpo -c -o libprocess_unittests-d_cfg_mgr_unittests.o `test -f 'd_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`d_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Tpo $(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_cfg_mgr_unittests.cc' object='libprocess_unittests-d_cfg_mgr_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-d_cfg_mgr_unittests.o `test -f 'd_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`d_cfg_mgr_unittests.cc + +libprocess_unittests-d_cfg_mgr_unittests.obj: d_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-d_cfg_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Tpo -c -o libprocess_unittests-d_cfg_mgr_unittests.obj `if test -f 'd_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'd_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d_cfg_mgr_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Tpo $(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_cfg_mgr_unittests.cc' object='libprocess_unittests-d_cfg_mgr_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-d_cfg_mgr_unittests.obj `if test -f 'd_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'd_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d_cfg_mgr_unittests.cc'; fi` + +libprocess_unittests-cb_ctl_base_unittests.o: cb_ctl_base_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-cb_ctl_base_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Tpo -c -o libprocess_unittests-cb_ctl_base_unittests.o `test -f 'cb_ctl_base_unittests.cc' || echo '$(srcdir)/'`cb_ctl_base_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Tpo $(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cb_ctl_base_unittests.cc' object='libprocess_unittests-cb_ctl_base_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-cb_ctl_base_unittests.o `test -f 'cb_ctl_base_unittests.cc' || echo '$(srcdir)/'`cb_ctl_base_unittests.cc + +libprocess_unittests-cb_ctl_base_unittests.obj: cb_ctl_base_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-cb_ctl_base_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Tpo -c -o libprocess_unittests-cb_ctl_base_unittests.obj `if test -f 'cb_ctl_base_unittests.cc'; then $(CYGPATH_W) 'cb_ctl_base_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cb_ctl_base_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Tpo $(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cb_ctl_base_unittests.cc' object='libprocess_unittests-cb_ctl_base_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-cb_ctl_base_unittests.obj `if test -f 'cb_ctl_base_unittests.cc'; then $(CYGPATH_W) 'cb_ctl_base_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/cb_ctl_base_unittests.cc'; fi` + +libprocess_unittests-config_base_unittests.o: config_base_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-config_base_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-config_base_unittests.Tpo -c -o libprocess_unittests-config_base_unittests.o `test -f 'config_base_unittests.cc' || echo '$(srcdir)/'`config_base_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-config_base_unittests.Tpo $(DEPDIR)/libprocess_unittests-config_base_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_base_unittests.cc' object='libprocess_unittests-config_base_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-config_base_unittests.o `test -f 'config_base_unittests.cc' || echo '$(srcdir)/'`config_base_unittests.cc + +libprocess_unittests-config_base_unittests.obj: config_base_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-config_base_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-config_base_unittests.Tpo -c -o libprocess_unittests-config_base_unittests.obj `if test -f 'config_base_unittests.cc'; then $(CYGPATH_W) 'config_base_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/config_base_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-config_base_unittests.Tpo $(DEPDIR)/libprocess_unittests-config_base_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_base_unittests.cc' object='libprocess_unittests-config_base_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-config_base_unittests.obj `if test -f 'config_base_unittests.cc'; then $(CYGPATH_W) 'config_base_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/config_base_unittests.cc'; fi` + +libprocess_unittests-config_ctl_info_unittests.o: config_ctl_info_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-config_ctl_info_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Tpo -c -o libprocess_unittests-config_ctl_info_unittests.o `test -f 'config_ctl_info_unittests.cc' || echo '$(srcdir)/'`config_ctl_info_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Tpo $(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_ctl_info_unittests.cc' object='libprocess_unittests-config_ctl_info_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-config_ctl_info_unittests.o `test -f 'config_ctl_info_unittests.cc' || echo '$(srcdir)/'`config_ctl_info_unittests.cc + +libprocess_unittests-config_ctl_info_unittests.obj: config_ctl_info_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-config_ctl_info_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Tpo -c -o libprocess_unittests-config_ctl_info_unittests.obj `if test -f 'config_ctl_info_unittests.cc'; then $(CYGPATH_W) 'config_ctl_info_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/config_ctl_info_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Tpo $(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_ctl_info_unittests.cc' object='libprocess_unittests-config_ctl_info_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-config_ctl_info_unittests.obj `if test -f 'config_ctl_info_unittests.cc'; then $(CYGPATH_W) 'config_ctl_info_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/config_ctl_info_unittests.cc'; fi` + +libprocess_unittests-config_ctl_parser_unittests.o: config_ctl_parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-config_ctl_parser_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Tpo -c -o libprocess_unittests-config_ctl_parser_unittests.o `test -f 'config_ctl_parser_unittests.cc' || echo '$(srcdir)/'`config_ctl_parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Tpo $(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_ctl_parser_unittests.cc' object='libprocess_unittests-config_ctl_parser_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-config_ctl_parser_unittests.o `test -f 'config_ctl_parser_unittests.cc' || echo '$(srcdir)/'`config_ctl_parser_unittests.cc + +libprocess_unittests-config_ctl_parser_unittests.obj: config_ctl_parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-config_ctl_parser_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Tpo -c -o libprocess_unittests-config_ctl_parser_unittests.obj `if test -f 'config_ctl_parser_unittests.cc'; then $(CYGPATH_W) 'config_ctl_parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/config_ctl_parser_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Tpo $(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='config_ctl_parser_unittests.cc' object='libprocess_unittests-config_ctl_parser_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-config_ctl_parser_unittests.obj `if test -f 'config_ctl_parser_unittests.cc'; then $(CYGPATH_W) 'config_ctl_parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/config_ctl_parser_unittests.cc'; fi` + +libprocess_unittests-d_controller_unittests.o: d_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-d_controller_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-d_controller_unittests.Tpo -c -o libprocess_unittests-d_controller_unittests.o `test -f 'd_controller_unittests.cc' || echo '$(srcdir)/'`d_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-d_controller_unittests.Tpo $(DEPDIR)/libprocess_unittests-d_controller_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_controller_unittests.cc' object='libprocess_unittests-d_controller_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-d_controller_unittests.o `test -f 'd_controller_unittests.cc' || echo '$(srcdir)/'`d_controller_unittests.cc + +libprocess_unittests-d_controller_unittests.obj: d_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-d_controller_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-d_controller_unittests.Tpo -c -o libprocess_unittests-d_controller_unittests.obj `if test -f 'd_controller_unittests.cc'; then $(CYGPATH_W) 'd_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d_controller_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-d_controller_unittests.Tpo $(DEPDIR)/libprocess_unittests-d_controller_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_controller_unittests.cc' object='libprocess_unittests-d_controller_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-d_controller_unittests.obj `if test -f 'd_controller_unittests.cc'; then $(CYGPATH_W) 'd_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/d_controller_unittests.cc'; fi` + +libprocess_unittests-daemon_unittest.o: daemon_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-daemon_unittest.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-daemon_unittest.Tpo -c -o libprocess_unittests-daemon_unittest.o `test -f 'daemon_unittest.cc' || echo '$(srcdir)/'`daemon_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-daemon_unittest.Tpo $(DEPDIR)/libprocess_unittests-daemon_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='daemon_unittest.cc' object='libprocess_unittests-daemon_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-daemon_unittest.o `test -f 'daemon_unittest.cc' || echo '$(srcdir)/'`daemon_unittest.cc + +libprocess_unittests-daemon_unittest.obj: daemon_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-daemon_unittest.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-daemon_unittest.Tpo -c -o libprocess_unittests-daemon_unittest.obj `if test -f 'daemon_unittest.cc'; then $(CYGPATH_W) 'daemon_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/daemon_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-daemon_unittest.Tpo $(DEPDIR)/libprocess_unittests-daemon_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='daemon_unittest.cc' object='libprocess_unittests-daemon_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-daemon_unittest.obj `if test -f 'daemon_unittest.cc'; then $(CYGPATH_W) 'daemon_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/daemon_unittest.cc'; fi` + +libprocess_unittests-log_parser_unittests.o: log_parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-log_parser_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-log_parser_unittests.Tpo -c -o libprocess_unittests-log_parser_unittests.o `test -f 'log_parser_unittests.cc' || echo '$(srcdir)/'`log_parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-log_parser_unittests.Tpo $(DEPDIR)/libprocess_unittests-log_parser_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='log_parser_unittests.cc' object='libprocess_unittests-log_parser_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-log_parser_unittests.o `test -f 'log_parser_unittests.cc' || echo '$(srcdir)/'`log_parser_unittests.cc + +libprocess_unittests-log_parser_unittests.obj: log_parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-log_parser_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-log_parser_unittests.Tpo -c -o libprocess_unittests-log_parser_unittests.obj `if test -f 'log_parser_unittests.cc'; then $(CYGPATH_W) 'log_parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/log_parser_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-log_parser_unittests.Tpo $(DEPDIR)/libprocess_unittests-log_parser_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='log_parser_unittests.cc' object='libprocess_unittests-log_parser_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-log_parser_unittests.obj `if test -f 'log_parser_unittests.cc'; then $(CYGPATH_W) 'log_parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/log_parser_unittests.cc'; fi` + +libprocess_unittests-logging_info_unittests.o: logging_info_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-logging_info_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-logging_info_unittests.Tpo -c -o libprocess_unittests-logging_info_unittests.o `test -f 'logging_info_unittests.cc' || echo '$(srcdir)/'`logging_info_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-logging_info_unittests.Tpo $(DEPDIR)/libprocess_unittests-logging_info_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='logging_info_unittests.cc' object='libprocess_unittests-logging_info_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-logging_info_unittests.o `test -f 'logging_info_unittests.cc' || echo '$(srcdir)/'`logging_info_unittests.cc + +libprocess_unittests-logging_info_unittests.obj: logging_info_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-logging_info_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-logging_info_unittests.Tpo -c -o libprocess_unittests-logging_info_unittests.obj `if test -f 'logging_info_unittests.cc'; then $(CYGPATH_W) 'logging_info_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/logging_info_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-logging_info_unittests.Tpo $(DEPDIR)/libprocess_unittests-logging_info_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='logging_info_unittests.cc' object='libprocess_unittests-logging_info_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-logging_info_unittests.obj `if test -f 'logging_info_unittests.cc'; then $(CYGPATH_W) 'logging_info_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/logging_info_unittests.cc'; fi` + +libprocess_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/libprocess_unittests-run_unittests.Tpo -c -o libprocess_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-run_unittests.Tpo $(DEPDIR)/libprocess_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libprocess_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +libprocess_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -MT libprocess_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/libprocess_unittests-run_unittests.Tpo -c -o libprocess_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocess_unittests-run_unittests.Tpo $(DEPDIR)/libprocess_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libprocess_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocess_unittests_CPPFLAGS) $(CPPFLAGS) $(libprocess_unittests_CXXFLAGS) $(CXXFLAGS) -c -o libprocess_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# 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 + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) +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: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-config_base_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-d_controller_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-daemon_unittest.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-log_parser_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-logging_info_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-run_unittests.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile 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 ./$(DEPDIR)/libprocess_unittests-cb_ctl_base_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-config_base_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-config_ctl_info_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-config_ctl_parser_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-d_cfg_mgr_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-d_controller_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-daemon_unittest.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-log_parser_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-logging_info_unittests.Po + -rm -f ./$(DEPDIR)/libprocess_unittests-run_unittests.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool 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-compile \ + mostlyclean-generic mostlyclean-libtool 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/src/lib/process/tests/cb_ctl_base_unittests.cc b/src/lib/process/tests/cb_ctl_base_unittests.cc new file mode 100644 index 0000000..58d0aa0 --- /dev/null +++ b/src/lib/process/tests/cb_ctl_base_unittests.cc @@ -0,0 +1,718 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <config_backend/base_config_backend_mgr.h> +#include <config_backend/base_config_backend_pool.h> +#include <process/cb_ctl_base.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/gregorian/gregorian.hpp> +#include <boost/shared_ptr.hpp> +#include <gtest/gtest.h> +#include <map> +#include <string> + +using namespace isc; +using namespace isc::cb; +using namespace isc::db; +using namespace isc::process; + +namespace { + +/// @brief Implementation of the config backend for testing the +/// @c CBControlBase template class. +/// +/// This simple class allows for adding, retrieving and clearing audit +/// entries. The @c CBControlBase unit tests use it to control the +/// behavior of the @c CBControlBase class under test. +class CBControlBackend : BaseConfigBackend { +public: + + /// @brief Constructor. + CBControlBackend(const db::DatabaseConnection::ParameterMap&) { + } + + /// @brief Retrieves the audit entries later than specified time. + /// + /// @param modification_time The lower bound time for which audit + /// entries should be returned. + /// @param modification_id The lower bound id for which audit + /// entries should be returned. + /// + /// @return Collection of audit entries later than specified time. + virtual db::AuditEntryCollection + getRecentAuditEntries(const db::ServerSelector&, + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) const { + db::AuditEntryCollection filtered_entries; + + // Use the index which orders the audit entries by timestamps. + const auto& index = audit_entries_.get<AuditEntryModificationTimeIdTag>(); + + // Locate the first audit entry after the last one having the + // specified modification time and id. + auto modification = boost::make_tuple(modification_time, modification_id); + auto first_entry = index.upper_bound(modification); + + // If there are any entries found return them. + if (first_entry != index.end()) { + filtered_entries.insert(first_entry, index.end()); + } + + return (filtered_entries); + } + + /// @brief Add audit entry to the backend. + /// + /// @param object_type Object type to be stored in the audit entry. + /// @param object_id Object id to be stored in the audit entry. + /// @param modification_time Audit entry modification time to be set. + /// @param modification_id Audit entry modification id to be set. + void addAuditEntry(const ServerSelector&, + const std::string& object_type, + const uint64_t object_id, + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) { + // Create new audit entry from the specified parameters. + AuditEntryPtr audit_entry(new AuditEntry(object_type, + object_id, + AuditEntry::ModificationType::CREATE, + modification_time, + modification_id, + "added audit entry")); + + // The audit entries are held in the static variable so as they + // don't disappear when we diconnect from the backend. The + // audit entries are explicitly cleared during the unit tests + // setup. + audit_entries_.insert(audit_entry); + } + + /// @brief Returns backend type in the textual format. + /// + /// @return Name of the storage for configurations, e.g. "mysql", + /// "pgsql" and so forth. + virtual std::string getType() const { + return ("memfile"); + } + + /// @brief Returns backend host + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return host on which the database is located. + virtual std::string getHost() const { + return (""); + } + + /// @brief Returns backend port number. + /// + /// This is used by the @c BaseConfigBackendPool to select backend + /// when @c BackendSelector is specified. + /// + /// @return Port number on which database service is available. + virtual uint16_t getPort() const { + return (0); + } + + /// @brief Removes audit entries. + static void clearAuditEntries() { + audit_entries_.clear(); + } + +private: + + /// @brief Static collection of audit entries. + /// + /// Thanks to storing them in the static member they are preserved + /// when the unit tests "disconnect" from the backend. + static AuditEntryCollection audit_entries_; +}; + +/// @brief Pointer to the @c CBControlBackend object. +typedef boost::shared_ptr<CBControlBackend> CBControlBackendPtr; + +AuditEntryCollection CBControlBackend::audit_entries_; + +/// @brief Implementation of the backends pool used in the +/// @c CBControlBase template class unit tests. +class CBControlBackendPool : public BaseConfigBackendPool<CBControlBackend> { +public: + + /// @brief Add audit entry to the backend. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param object_type Object type to be stored in the audit entry. + /// @param object_id Object id to be stored in the audit entry. + /// @param modification_time Audit entry modification time to be set. + /// @param modification_id Audit entry modification id to be set. + void addAuditEntry(const BackendSelector& backend_selector, + const ServerSelector& server_selector, + const std::string& object_type, + const uint64_t object_id, + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) { + createUpdateDeleteProperty<void, const std::string&, uint64_t, + const boost::posix_time::ptime&, uint64_t> + (&CBControlBackend::addAuditEntry, backend_selector, server_selector, + object_type, object_id, modification_time, modification_id); + } + + /// @brief Retrieves the audit entries later than specified time. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param modification_time The lower bound time for which audit + /// entries should be returned. + /// @param modification_id The lower bound id for which audit + /// entries should be returned. + /// + /// @return Collection of audit entries later than specified time. + virtual db::AuditEntryCollection + getRecentAuditEntries(const BackendSelector& backend_selector, + const ServerSelector& server_selector, + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) const { + AuditEntryCollection audit_entries; + getMultiplePropertiesConst<AuditEntryCollection, const boost::posix_time::ptime&> + (&CBControlBackend::getRecentAuditEntries, backend_selector, + server_selector, audit_entries, modification_time, + modification_id); + return (audit_entries); + } +}; + +/// @brief Implementation of the config backends manager used +/// in the @c CBControlBase template class unit tests. +class CBControlBackendMgr : public BaseConfigBackendMgr<CBControlBackendPool> { +public: + + /// @brief Constructor. + CBControlBackendMgr() + : instance_id_(0) { + } + + /// @brief Returns instance of the @c CBControlBackendMgr. + /// + /// @return Reference to the instance of the @c CBControlBackendMgr. + static CBControlBackendMgr& instance() { + static CBControlBackendMgr mgr; + return (mgr); + } + + /// @brief Returns instance id. + /// + /// This value is used in tests which verify that the @c CBControlBase::getMgr + /// returns the right instance of the CB manager. + /// + /// @return Instance id. + uint32_t getInstanceId() const { + return (instance_id_); + } + + /// @brief Sets new instance id. + /// + /// @param instance_id New instance id. + void setInstanceId(const uint32_t instance_id) { + instance_id_ = instance_id; + } + + /// @brief Instance id. + uint32_t instance_id_; +}; + +/// @brief Implementation of the @c CBControlBase class used in +/// the unit tests. +/// +/// It makes some of the protected methods public. It also provides +/// means to test the behavior of the @c CBControlBase template. +class CBControl : public CBControlBase<CBControlBackendMgr> { +public: + + using CBControlBase<CBControlBackendMgr>::fetchConfigElement; + using CBControlBase<CBControlBackendMgr>::getMgr; + using CBControlBase<CBControlBackendMgr>::getInitialAuditRevisionTime; + + /// @brief Constructor. + CBControl() + : CBControlBase<CBControlBackendMgr>(), + merges_num_(0), + backend_selector_(BackendSelector::Type::MYSQL), + server_selector_(ServerSelector::UNASSIGNED()), + audit_entries_num_(-1), + enable_throw_(false) { + } + + /// @brief Implementation of the method called to fetch and apply + /// configuration from the database into the local configuration. + /// + /// This stub implementation doesn't attempt to merge any configurations + /// but merely records the values of the parameters called. + /// + /// @param backend_selector Backend selector. + /// @param server_selector Server selector. + /// @param audit_entries Collection of audit entries. + virtual void databaseConfigApply(const BackendSelector& backend_selector, + const ServerSelector& server_selector, + const boost::posix_time::ptime&, + const AuditEntryCollection& audit_entries) { + ++merges_num_; + backend_selector_ = backend_selector; + server_selector_ = server_selector; + audit_entries_num_ = static_cast<int>(audit_entries.size()); + + if (enable_throw_) { + isc_throw(Unexpected, "throwing from databaseConfigApply"); + } + } + + /// @brief Returns the number of times the @c databaseConfigApply was called. + size_t getMergesNum() const { + return (merges_num_); + } + + /// @brief Returns backend selector used as an argument in a call to + /// @c databaseConfigApply. + const BackendSelector& getBackendSelector() const { + return (backend_selector_); + } + + /// @brief Returns server selector used as an argument in a call to + /// @c databaseConfigApply. + const ServerSelector& getServerSelector() const { + return (server_selector_); + } + + /// @brief Returns the number of audit entries in the collection passed + /// to @c databaseConfigApply + int getAuditEntriesNum() const { + return (audit_entries_num_); + } + + /// @brief Returns the recorded time of last audit entry. + boost::posix_time::ptime getLastAuditRevisionTime() const { + return (last_audit_revision_time_); + } + + /// @brief Returns the recorded id of last audit entry. + uint64_t getLastAuditRevisionId() const { + return (last_audit_revision_id_); + } + + /// @brief Overwrites the last audit entry time. + /// + /// @param last_audit_revision_time New time to be set. + void setLastAuditRevisionTime(const boost::posix_time::ptime& last_audit_revision_time) { + last_audit_revision_time_ = last_audit_revision_time; + } + + /// @brief Overwrites the last audit revision id. + /// + /// @param last_audit_revision_id New id to be set. + void setLastAuditRevisionId(const uint64_t& last_audit_revision_id) { + last_audit_revision_id_ = last_audit_revision_id; + } + + /// @brief Enables the @c databaseConfigApply function to throw. + /// + /// This is useful to test scenarios when configuration merge fails. + void enableThrow() { + enable_throw_ = true; + } + +private: + + /// @brief Recorded number of calls to @c databaseConfigApply. + size_t merges_num_; + + /// @brief Recorded backend selector value. + BackendSelector backend_selector_; + + /// @brief Recorded server selector value. + ServerSelector server_selector_; + + /// @brief Recorded number of audit entries. + int audit_entries_num_; + + /// @brief Boolean value indicating if the @c databaseConfigApply should throw. + bool enable_throw_; +}; + +/// @brief Out of the blue instance id used in tests. +constexpr uint32_t TEST_INSTANCE_ID = 123; + +/// @brief Test fixture class for @c CBControlBase template class. +class CBControlBaseTest : public ::testing::Test { +public: + + /// @brief Constructor. + CBControlBaseTest() + : cb_ctl_(), mgr_(CBControlBackendMgr::instance()), + timestamps_() { + mgr_.registerBackendFactory("db1", + [](const DatabaseConnection::ParameterMap& params) + -> CBControlBackendPtr { + return (CBControlBackendPtr(new CBControlBackend(params))); + }); + mgr_.setInstanceId(TEST_INSTANCE_ID); + initTimestamps(); + CBControlBackend::clearAuditEntries(); + } + + /// @brief Destructor. + /// + /// Removes audit entries created in the test. + ~CBControlBaseTest() { + CBControlBackend::clearAuditEntries(); + } + + /// @brief Initialize posix time values used in tests. + void initTimestamps() { + // Current time minus 1 hour to make sure it is in the past. + timestamps_["today"] = boost::posix_time::second_clock::local_time() + - boost::posix_time::hours(1); + // Yesterday. + timestamps_["yesterday"] = timestamps_["today"] - boost::posix_time::hours(24); + // Two days ago. + timestamps_["two days ago"] = timestamps_["today"] - boost::posix_time::hours(48); + // Tomorrow. + timestamps_["tomorrow"] = timestamps_["today"] + boost::posix_time::hours(24); + } + + /// @brief Creates an instance of the configuration object. + /// + /// @param db1_access Database access string to be used to connect to + /// the test configuration backend. It doesn't connect if the string + /// is empty. + ConfigPtr makeConfigBase(const std::string& db1_access = "") const { + ConfigControlInfoPtr config_ctl_info(new ConfigControlInfo()); + + if (!db1_access.empty()) { + config_ctl_info->addConfigDatabase(db1_access); + } + + ConfigPtr config_base(new ConfigBase()); + + config_base->setConfigControlInfo(config_ctl_info); + return (config_base); + } + + /// @brief Instance of the @c CBControl used in tests. + CBControl cb_ctl_; + + /// @brief Instance of the Config Backend Manager. + CBControlBackendMgr& mgr_; + + /// @brief Holds timestamp values used in tests. + std::map<std::string, boost::posix_time::ptime> timestamps_; +}; + +// This test verifies that the same instance of the Config +// Backend Manager is returned all the time. +TEST_F(CBControlBaseTest, getMgr) { + auto mgr = cb_ctl_.getMgr(); + EXPECT_EQ(TEST_INSTANCE_ID, mgr.getInstanceId()); +} + +// This test verifies that the initial audit revision time is set to +// local time of 2000-01-01. +TEST_F(CBControlBaseTest, getInitialAuditRevisionTime) { + auto initial_time = cb_ctl_.getInitialAuditRevisionTime(); + ASSERT_FALSE(initial_time.is_not_a_date_time()); + auto tm = boost::posix_time::to_tm(initial_time); + EXPECT_EQ(100, tm.tm_year); + EXPECT_EQ(0, tm.tm_mon); + EXPECT_EQ(0, tm.tm_yday); + EXPECT_EQ(0, tm.tm_hour); + EXPECT_EQ(0, tm.tm_min); + EXPECT_EQ(0, tm.tm_sec); +} + +// This test verifies that last audit entry time is reset upon the +// call to CBControlBase::reset(). +TEST_F(CBControlBaseTest, reset) { + cb_ctl_.setLastAuditRevisionTime(timestamps_["tomorrow"]); + cb_ctl_.reset(); + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditRevisionId()); +} + +// This test verifies that it is correctly determined what entries the +// server should fetch for the particular configuration element. +TEST_F(CBControlBaseTest, fetchConfigElement) { + db::AuditEntryCollection audit_entries; + db::AuditEntryCollection updated; + // When audit entries collection is empty any subset is empty too. + updated = cb_ctl_.fetchConfigElement(audit_entries, "my_object_type"); + EXPECT_TRUE(updated.empty()); + + // Now test the case that there is a DELETE audit entry. In this case + // our function should indicate that the configuration should not be + // fetched for the given object type. Note that when the configuration + // element is deleted, it no longer exists in database so there is + // no reason to fetch the data from the database. + AuditEntryPtr audit_entry(new AuditEntry("dhcp4_subnet", 1234 , + AuditEntry::ModificationType::DELETE, + 2345, "added audit entry")); + audit_entries.insert(audit_entry); + updated = cb_ctl_.fetchConfigElement(audit_entries, "my_object_type"); + EXPECT_TRUE(updated.empty()); + EXPECT_TRUE(hasObjectId(audit_entries, 1234)); + EXPECT_FALSE(hasObjectId(audit_entries, 5678)); + EXPECT_FALSE(hasObjectId(updated, 1234)); + + // Add another audit entry which indicates creation of the configuration element. + // This time we should get it. + audit_entry.reset(new AuditEntry("my_object_type", 5678, + AuditEntry::ModificationType::CREATE, + 6789, "added audit entry")); + audit_entries.insert(audit_entry); + updated = cb_ctl_.fetchConfigElement(audit_entries, "my_object_type"); + ASSERT_EQ(1, updated.size()); + AuditEntryPtr updated_entry = (*updated.begin()); + ASSERT_TRUE(updated_entry); + EXPECT_EQ("my_object_type", updated_entry->getObjectType()); + EXPECT_EQ(5678, updated_entry->getObjectId()); + EXPECT_EQ(AuditEntry::ModificationType::CREATE, updated_entry->getModificationType()); + EXPECT_TRUE(hasObjectId(audit_entries, 5678)); + EXPECT_TRUE(hasObjectId(updated, 5678)); + EXPECT_FALSE(hasObjectId(updated, 1234)); + + // Also we should get 'true' for the UPDATE case. + audit_entry.reset(new AuditEntry("my_object_type", + 5678, AuditEntry::ModificationType::UPDATE, + 6790, "added audit entry")); + audit_entries.insert(audit_entry); + updated = cb_ctl_.fetchConfigElement(audit_entries, "my_object_type"); + EXPECT_EQ(2, updated.size()); + bool saw_create = false; + bool saw_update = false; + for (auto entry : updated) { + EXPECT_EQ("my_object_type", entry->getObjectType()); + EXPECT_EQ(5678, entry->getObjectId()); + if (AuditEntry::ModificationType::CREATE == entry->getModificationType()) { + EXPECT_FALSE(saw_create); + saw_create = true; + } else if (AuditEntry::ModificationType::UPDATE == entry->getModificationType()) { + EXPECT_FALSE(saw_update); + saw_update = true; + } + } + EXPECT_TRUE(saw_create); + EXPECT_TRUE(saw_update); + EXPECT_TRUE(hasObjectId(updated, 5678)); + EXPECT_FALSE(hasObjectId(updated, 1234)); +} + +// This test verifies that true is return when the server successfully +// connects to the backend and false if there are no backends to connect +// to. +TEST_F(CBControlBaseTest, connect) { + EXPECT_TRUE(cb_ctl_.databaseConfigConnect(makeConfigBase("type=db1"))); + EXPECT_FALSE(cb_ctl_.databaseConfigConnect(makeConfigBase())); +} + +// This test verifies the scenario when the server fetches the entire +// configuration from the database upon startup. +TEST_F(CBControlBaseTest, fetchAll) { + auto config_base = makeConfigBase("type=db1"); + + // Add two audit entries to the database. The server should load + // the entire configuration from the database regardless of the + // existing audit entries. However, the last audit entry timestamp + // should be set to the most recent audit entry in the + // @c CBControlBase. + ASSERT_TRUE(cb_ctl_.databaseConfigConnect(config_base)); + + cb_ctl_.getMgr().getPool()->addAuditEntry(BackendSelector::UNSPEC(), + ServerSelector::ALL(), + "sql_table_2", + 1234, + timestamps_["yesterday"], + 2345); + + cb_ctl_.getMgr().getPool()->addAuditEntry(BackendSelector::UNSPEC(), + ServerSelector::ALL(), + "sql_table_1", + 3456, + timestamps_["today"], + 4567); + + // Disconnect from the database in order to check that the + // databaseConfigFetch reconnects. + ASSERT_NO_THROW(cb_ctl_.databaseConfigDisconnect()); + + // Verify that various indicators are set to their initial values. + ASSERT_EQ(0, cb_ctl_.getMergesNum()); + ASSERT_EQ(BackendSelector::Type::MYSQL, cb_ctl_.getBackendSelector().getBackendType()); + ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); + ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditRevisionId()); + + // Connect to the database and fetch the configuration. + ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base)); + + // There should be one invocation of the databaseConfigApply. + ASSERT_EQ(1, cb_ctl_.getMergesNum()); + // Since this is full reconfiguration the audit entry collection + // passed to the databaseConfigApply should be empty. + EXPECT_EQ(0, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(BackendSelector::Type::UNSPEC, cb_ctl_.getBackendSelector().getBackendType()); + EXPECT_EQ(ServerSelector::Type::ALL, cb_ctl_.getServerSelector().getType()); + // Make sure that the internal timestamp is set to the most recent + // audit entry, so as the server will only later fetch config + // updates after this timestamp. + EXPECT_EQ(timestamps_["today"], cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(4567, cb_ctl_.getLastAuditRevisionId()); +} + +// This test verifies that the configuration can be fetched for a +// specified server tag. +TEST_F(CBControlBaseTest, fetchFromServer) { + auto config_base = makeConfigBase("type=db1"); + // Set a server tag. + config_base->setServerTag("a-tag"); + + // Verify that various indicators are set to their initial values. + ASSERT_EQ(0, cb_ctl_.getMergesNum()); + ASSERT_EQ(BackendSelector::Type::MYSQL, cb_ctl_.getBackendSelector().getBackendType()); + ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); + ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditRevisionId()); + + ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base)); + + ASSERT_EQ(1, cb_ctl_.getMergesNum()); + EXPECT_EQ(0, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(BackendSelector::Type::UNSPEC, cb_ctl_.getBackendSelector().getBackendType()); + // An explicit server selector should have been used this time. + ASSERT_EQ(ServerSelector::Type::SUBSET, cb_ctl_.getServerSelector().getType()); + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + + // Make sure that the server selector used in databaseConfigFetch is + // correct. + auto tags = cb_ctl_.getServerSelector().getTags(); + ASSERT_EQ(1, tags.size()); + EXPECT_EQ("a-tag", tags.begin()->get()); +} + +// This test verifies that incremental configuration changes can be +// fetched. +TEST_F(CBControlBaseTest, fetchUpdates) { + auto config_base = makeConfigBase("type=db1"); + + // Connect to the database and store an audit entry. Do not close + // the database connection to simulate the case when the server + // uses existing connection to fetch configuration updates. + ASSERT_TRUE(cb_ctl_.databaseConfigConnect(config_base)); + cb_ctl_.getMgr().getPool()->addAuditEntry(BackendSelector::UNSPEC(), + ServerSelector::ALL(), + "sql_table_1", + 3456, + timestamps_["today"], + 4567); + + // Verify that various indicators are set to their initial values. + ASSERT_EQ(0, cb_ctl_.getMergesNum()); + ASSERT_EQ(BackendSelector::Type::MYSQL, cb_ctl_.getBackendSelector().getBackendType()); + ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); + ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditRevisionId()); + + ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base, + CBControl::FetchMode::FETCH_UPDATE)); + + // There should be one invocation to databaseConfigApply recorded. + ASSERT_EQ(1, cb_ctl_.getMergesNum()); + // The number of audit entries passed to this function should be 1. + EXPECT_EQ(1, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(BackendSelector::Type::UNSPEC, cb_ctl_.getBackendSelector().getBackendType()); + EXPECT_EQ(ServerSelector::Type::ALL, cb_ctl_.getServerSelector().getType()); + // The last audit entry time should be set to the latest audit entry. + EXPECT_EQ(timestamps_["today"], cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(4567, cb_ctl_.getLastAuditRevisionId()); +} + +// Check that the databaseConfigApply function is not called when there +// are no more unprocessed audit entries. +TEST_F(CBControlBaseTest, fetchNoUpdates) { + auto config_base = makeConfigBase("type=db1"); + + // Set last audit entry time to the timestamp of the audit + // entry we are going to add. That means that there will be + // no new audit entries to fetch. + cb_ctl_.setLastAuditRevisionTime(timestamps_["yesterday"]); + cb_ctl_.setLastAuditRevisionId(4567); + + ASSERT_TRUE(cb_ctl_.databaseConfigConnect(config_base)); + + cb_ctl_.getMgr().getPool()->addAuditEntry(BackendSelector::UNSPEC(), + ServerSelector::ALL(), + "sql_table_1", + 3456, + timestamps_["yesterday"], + 4567); + + ASSERT_EQ(0, cb_ctl_.getMergesNum()); + + ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base, + CBControl::FetchMode::FETCH_UPDATE)); + + // The databaseConfigApply should not be called because there are + // no new audit entires to process. + ASSERT_EQ(0, cb_ctl_.getMergesNum()); +} + +// This test verifies that database config fetch failures are handled +// gracefully. +TEST_F(CBControlBaseTest, fetchFailure) { + auto config_base = makeConfigBase("type=db1"); + + // Connect to the database and store an audit entry. Do not close + // the database connection to simulate the case when the server + // uses existing connection to fetch configuration updates. + ASSERT_TRUE(cb_ctl_.databaseConfigConnect(config_base)); + cb_ctl_.getMgr().getPool()->addAuditEntry(BackendSelector::UNSPEC(), + ServerSelector::ALL(), + "sql_table_1", + 3456, + timestamps_["today"], + 4567); + + // Configure the CBControl to always throw simulating the failure + // during configuration merge. + cb_ctl_.enableThrow(); + + // Verify that various indicators are set to their initial values. + ASSERT_EQ(0, cb_ctl_.getMergesNum()); + ASSERT_EQ(BackendSelector::Type::MYSQL, cb_ctl_.getBackendSelector().getBackendType()); + ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); + ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditRevisionId()); + + ASSERT_THROW(cb_ctl_.databaseConfigFetch(config_base, CBControl::FetchMode::FETCH_UPDATE), + isc::Unexpected); + + // There should be one invocation to databaseConfigApply recorded. + ASSERT_EQ(1, cb_ctl_.getMergesNum()); + // The number of audit entries passed to this function should be 1. + EXPECT_EQ(1, cb_ctl_.getAuditEntriesNum()); + EXPECT_EQ(BackendSelector::Type::UNSPEC, cb_ctl_.getBackendSelector().getBackendType()); + EXPECT_EQ(ServerSelector::Type::ALL, cb_ctl_.getServerSelector().getType()); + // The last audit entry time should not be modified because there was a merge + // error. + EXPECT_EQ(cb_ctl_.getInitialAuditRevisionTime(), cb_ctl_.getLastAuditRevisionTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditRevisionId()); +} + +} diff --git a/src/lib/process/tests/config_base_unittests.cc b/src/lib/process/tests/config_base_unittests.cc new file mode 100644 index 0000000..cec4582 --- /dev/null +++ b/src/lib/process/tests/config_base_unittests.cc @@ -0,0 +1,230 @@ +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <process/config_base.h> +#include <util/optional.h> + +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::process; +using namespace isc::util; + +/// @brief Derived ConfigBase class +/// We use this derivation to test the +/// copy and equality functions. +class ConfigBaseImpl : public ConfigBase { +public: + void + copy(ConfigBaseImpl& other) const { + ConfigBase::copy(other); + } +}; + +// Verifies construction, copy, and equality of +// ConfigBase with respect to ConfigControInfo. +TEST(ConfigBase, configControlInfoTests) { + + // Create a control info instance + ConfigControlInfoPtr ctl_info1(new ConfigControlInfo()); + ctl_info1->addConfigDatabase("type=mysql host=example.com"); + ctl_info1->addConfigDatabase("type=mysql host=example2.com"); + + // Create a ConfigBase + ConfigBaseImpl base1; + base1.setConfigControlInfo(ctl_info1); + + // Clone the ConfigBase + ConfigBaseImpl base2; + base1.copy(base2); + + // They should be equal. + EXPECT_TRUE(base1.equals(base2)); + + // Reset control info for one of them. + base1.setConfigControlInfo(ConfigControlInfoPtr()); + + // They should not be equal. + EXPECT_FALSE(base1.equals(base2)); + + // Reset control info for the other one. + base2.setConfigControlInfo(ConfigControlInfoPtr()); + + // They should be equal again. + EXPECT_TRUE(base1.equals(base2)); +} + +// Verifies that logging information can be merged to another. +TEST(ConfigBase, mergeLoggingInfo) { + // Create first logging info. + LoggingInfo log_info1; + log_info1.name_ = "foo"; + + // Create second logging info. + LoggingInfo log_info2; + log_info2.name_ = "bar"; + + // Create first config base instance. + ConfigBaseImpl base1; + base1.addLoggingInfo(log_info1); + + // Copy the first instance to keep it as reference. + ConfigBaseImpl base1_copy; + base1_copy.copy(base1); + + // Create second config base instance. + ConfigBaseImpl base2; + ASSERT_NO_THROW(base1.merge(base2)); + EXPECT_TRUE(base1.equals(base1_copy)); + + // Set some data for the second config. + base2.addLoggingInfo(log_info2); + + // This time the merge should replace the original config. + ASSERT_NO_THROW(base1.merge(base2)); + EXPECT_TRUE(base1.equals(base2)); +} + +// Verifies that config control can be merged to another. +TEST(ConfigBase, mergeConfigControl) { + // Create first config control info. + ConfigControlInfoPtr ctl_info1(new ConfigControlInfo()); + ctl_info1->addConfigDatabase("type=mysql host=example.com"); + ctl_info1->addConfigDatabase("type=mysql host=example2.com"); + + // Create second config control info. + ConfigControlInfoPtr ctl_info2(new ConfigControlInfo()); + ctl_info2->addConfigDatabase("type=pgsql host=example.com"); + ctl_info2->addConfigDatabase("type=pgsql host=example2.com"); + + // Create first config base instance. + ConfigBaseImpl base1; + base1.setConfigControlInfo(ctl_info1); + + // Copy the first instance to keep it as reference. + ConfigBaseImpl base1_copy; + base1_copy.copy(base1); + + // Create second config base instance. + ConfigBaseImpl base2; + + // Merged base is empty, so the original should be preserved. + ASSERT_NO_THROW(base1.merge(base2)); + EXPECT_TRUE(base1.equals(base1_copy)); + + // Set some data for the second config. + base2.setConfigControlInfo(ctl_info2); + + // This time the merge should replace the original config. + ASSERT_NO_THROW(base1.merge(base2)); + EXPECT_TRUE(base1.equals(base2)); +} + +// Verifies that server-tag may be configured. +TEST(ConfigBase, serverTag) { + ConfigBaseImpl conf; + + // Check that the default is an unspecified and empty string. + EXPECT_TRUE(conf.getServerTag().unspecified()); + EXPECT_TRUE(conf.getServerTag().empty()); + + // Check that it can be modified. + conf.setServerTag("boo"); + EXPECT_FALSE(conf.getServerTag().unspecified()); + EXPECT_EQ("boo", conf.getServerTag().get()); +} + +// Verifies that server tag can be merged to another config. +TEST(ConfigBase, mergeServerTag) { + ConfigBaseImpl base1; + ConfigBaseImpl base2; + + // Initially the server tags in both config should be + // unspecified. + EXPECT_TRUE(base1.getServerTag().unspecified()); + EXPECT_TRUE(base2.getServerTag().unspecified()); + + // Merging the config with unspecified server tag should + // not modify the target config. + ASSERT_NO_THROW(base1.merge(base2)); + EXPECT_TRUE(base1.getServerTag().unspecified()); + EXPECT_TRUE(base2.getServerTag().unspecified()); + + // Set server tag for base2 and merge it. + base2.setServerTag(std::string("base2")); + ASSERT_NO_THROW(base1.merge(base2)); + + // The server tag should be copied into the base1. Both + // should now be unspecified. + EXPECT_FALSE(base1.getServerTag().unspecified()); + EXPECT_FALSE(base2.getServerTag().unspecified()); + + // They should also hold the same value. + EXPECT_EQ("base2", base1.getServerTag().get()); + EXPECT_EQ("base2", base2.getServerTag().get()); + + // Reset the server tag to unspecified. + base2.setServerTag(Optional<std::string>()); + EXPECT_FALSE(base1.getServerTag().unspecified()); + EXPECT_TRUE(base2.getServerTag().unspecified()); + + // Merging the config with unspecified server tag should + // result in no change in the target config. + ASSERT_NO_THROW(base1.merge(base2)); + EXPECT_FALSE(base1.getServerTag().unspecified()); + EXPECT_TRUE(base2.getServerTag().unspecified()); + + // The server tag should remain the same. + EXPECT_EQ("base2", base1.getServerTag().get()); + + // Set the explicit server tag in the source config. + base2.setServerTag("new-base2"); + + // Merge again. + ASSERT_NO_THROW(base1.merge(base2)); + + // The new value should be stored in the target config, so + // both should be specified and have the same value. + EXPECT_FALSE(base1.getServerTag().unspecified()); + EXPECT_FALSE(base2.getServerTag().unspecified()); + EXPECT_EQ("new-base2", base1.getServerTag().get()); + EXPECT_EQ("new-base2", base2.getServerTag().get()); +} + +// Verifies that server tag can be copied to another config. +TEST(ConfigBase, copyServerTag) { + ConfigBaseImpl base1; + ConfigBaseImpl base2; + + // Set server tag for the base2. + base2.setServerTag(std::string("base2")); + + // The base1 has server tag unspecified. Copying it to the + // base2 should result in unspecified server tag in base2. + ASSERT_NO_THROW(base1.copy(base2)); + EXPECT_TRUE(base2.getServerTag().unspecified()); + + // Set server tag for base1 and copy it to base2. + base1.setServerTag(std::string("base1")); + ASSERT_NO_THROW(base1.copy(base2)); + + // The base2 should now hold the value from base1. + EXPECT_FALSE(base2.getServerTag().unspecified()); + EXPECT_EQ("base1", base2.getServerTag().get()); + + // Set base1 value to a different value. + base1.setServerTag(std::string("new-base1")); + + // Copy again. + ASSERT_NO_THROW(base1.copy(base2)); + + // It should override the value in the base2. + EXPECT_FALSE(base2.getServerTag().unspecified()); + EXPECT_EQ("new-base1", base2.getServerTag().get()); +} diff --git a/src/lib/process/tests/config_ctl_info_unittests.cc b/src/lib/process/tests/config_ctl_info_unittests.cc new file mode 100644 index 0000000..7c78014 --- /dev/null +++ b/src/lib/process/tests/config_ctl_info_unittests.cc @@ -0,0 +1,180 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <process/config_ctl_info.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <sstream> +#include <iostream> + +using namespace isc::process; +using namespace isc::data; +using namespace isc::util; + +// Verifies initializing via an access string and unparsing into elements +// We just test basic unparsing, as more rigorous testing is done in +// libkea-db testing which ConfigDBInfo uses. +TEST(ConfigDbInfo, basicOperation) { + ConfigDbInfo db; + std::string access = "type=mysql user=tom password=terrific"; + std::string redacted_access = "password=***** type=mysql user=tom"; + std::string access_json = "{\n" + " \"type\":\"mysql\", \n" + " \"user\":\"tom\", \n" + " \"password\":\"terrific\" \n" + "} \n"; + + // Convert the above configuration into Elements for comparison. + ElementPtr exp_elems; + ASSERT_NO_THROW(exp_elems = Element::fromJSON(access_json)) + << "test is broken"; + + // Initialize the db from an the access string + db.setAccessString(access); + EXPECT_EQ(access, db.getAccessString()); + + EXPECT_EQ(redacted_access, db.redactedAccessString()); + + // Convert the db into Elements and make sure they are as expected. + ElementPtr db_elems; + ASSERT_NO_THROW(db_elems = db.toElement()); + EXPECT_TRUE(db_elems->equals(*exp_elems)); +} + +// Verify that db parameter values may be retrieved. +TEST(ConfigDbInfo, getParameterValue) { + ConfigDbInfo db1; + std::string access1 = "type=mysql name=keatest port=33 readonly=false"; + db1.setAccessString(access1); + + std::string value; + bool found = false; + + // Should find "type" + ASSERT_NO_THROW(found = db1.getParameterValue("type", value)); + EXPECT_TRUE(found); + EXPECT_EQ("mysql", value); + + // Should find "name" + ASSERT_NO_THROW(found = db1.getParameterValue("name", value)); + EXPECT_TRUE(found); + EXPECT_EQ("keatest", value); + + // Should find "port" + ASSERT_NO_THROW(found = db1.getParameterValue("port", value)); + EXPECT_TRUE(found); + EXPECT_EQ("33", value); + + // Should find "readonly" + ASSERT_NO_THROW(found = db1.getParameterValue("readonly", value)); + EXPECT_TRUE(found); + EXPECT_EQ("false", value); + + // Should not find "bogus" + ASSERT_NO_THROW(found = db1.getParameterValue("bogus", value)); + EXPECT_FALSE(found); +} + +// Verify that db equality operators work correctly. +TEST(ConfigDbInfo, equalityOperators) { + ConfigDbInfo db1; + std::string access1 = "type=mysql user=tom password=terrific"; + ASSERT_NO_THROW(db1.setAccessString(access1)); + + ConfigDbInfo db2; + std::string access2 = "type=postgresql user=tom password=terrific"; + ASSERT_NO_THROW(db2.setAccessString(access2)); + + // Verify that the two unequal dbs are in fact not equal. + EXPECT_FALSE(db1.equals(db2)); + EXPECT_FALSE(db1 == db2); + EXPECT_TRUE(db1 != db2); + + // Verify that the two equal dbs are in fact equal. + db2.setAccessString(access1); + EXPECT_TRUE(db1.equals(db2)); + EXPECT_TRUE(db1 == db2); + EXPECT_FALSE(db1 != db2); +} + +// Verifies the basic operations of ConfigControlInfo +TEST(ConfigControlInfo, basicOperation) { + + ConfigControlInfo ctl; + // We should have no dbs in the list. + EXPECT_EQ(0, ctl.getConfigDatabases().size()); + // The default fetch time is 30 and it is unspecified. + EXPECT_TRUE(ctl.getConfigFetchWaitTime().unspecified()); + EXPECT_EQ(30, ctl.getConfigFetchWaitTime().get()); + + // Override the default fetch time. + ctl.setConfigFetchWaitTime(Optional<uint16_t>(123)); + EXPECT_EQ(123, ctl.getConfigFetchWaitTime().get()); + + // We should be able to add two distinct, valid dbs + std::string access_str1 = "type=mysql host=machine1.org"; + ASSERT_NO_THROW(ctl.addConfigDatabase(access_str1)); + + std::string access_str2 = "type=postgresql host=machine2.org"; + ASSERT_NO_THROW(ctl.addConfigDatabase(access_str2)); + + // We should fail on a duplicate db. + ASSERT_THROW(ctl.addConfigDatabase(access_str1), isc::BadValue); + + // We should have two dbs in the list. + const ConfigDbInfoList& db_list = ctl.getConfigDatabases(); + EXPECT_EQ(2, db_list.size()); + + // Verify the dbs in the list are as we expect them to be. + EXPECT_EQ (access_str1, db_list[0].getAccessString()); + EXPECT_EQ (access_str2, db_list[1].getAccessString()); + + // Verify we can find dbs based on a property values. + const ConfigDbInfo& db_info = ctl.findConfigDb("type", "mysql"); + EXPECT_FALSE(db_info == ConfigControlInfo::EMPTY_DB()); + EXPECT_EQ (access_str1, db_info.getAccessString()); + + const ConfigDbInfo& db_info2 = ctl.findConfigDb("host", "machine2.org"); + EXPECT_FALSE(db_info2 == ConfigControlInfo::EMPTY_DB()); + EXPECT_EQ (access_str2, db_info2.getAccessString()); + + // Verify not finding a db returns EMPTY_DB(). + const ConfigDbInfo& db_info3 = ctl.findConfigDb("type", "bogus"); + EXPECT_TRUE(db_info3 == ConfigControlInfo::EMPTY_DB()); + + // Verify we can clear the list of dbs and the fetch time. + ctl.clear(); + EXPECT_EQ(0, ctl.getConfigDatabases().size()); + EXPECT_TRUE(ctl.getConfigFetchWaitTime().unspecified()); + EXPECT_EQ(30, ctl.getConfigFetchWaitTime().get()); +} + +// Verifies the copy ctor and equality functions ConfigControlInfo +TEST(ConfigControlInfo, copyAndEquality) { + + // Make an instance with two dbs. + ConfigControlInfo ctl1; + ASSERT_NO_THROW(ctl1.addConfigDatabase("type=mysql host=mach1.org")); + ASSERT_NO_THROW(ctl1.addConfigDatabase("type=postgresql host=mach2.org")); + ctl1.setConfigFetchWaitTime(Optional<uint16_t>(123)); + + // Clone that instance. + ConfigControlInfo ctl2(ctl1); + + // They should be equal. + EXPECT_TRUE(ctl1.equals(ctl2)); + + // Make a third instance with a different db. + ConfigControlInfo ctl3; + ASSERT_NO_THROW(ctl1.addConfigDatabase("type=mysql host=other.org")); + + // They should not equal. + EXPECT_FALSE(ctl3.equals(ctl1)); +} + diff --git a/src/lib/process/tests/config_ctl_parser_unittests.cc b/src/lib/process/tests/config_ctl_parser_unittests.cc new file mode 100644 index 0000000..8e5b328 --- /dev/null +++ b/src/lib/process/tests/config_ctl_parser_unittests.cc @@ -0,0 +1,102 @@ +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <cc/dhcp_config_error.h> +#include <process/config_ctl_parser.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <sstream> +#include <iostream> + +using namespace isc::process; +using namespace isc::data; + +// Verifies valid configurations are parsed correctly. The test +// uses round-trip comparison of configuration Elements to determine +// if parsing was correct. +TEST(ConfigCtlInfoParser, validConfigs) { + std::string configs[] = { + "{}", + + "{ \"config-databases\": [], \n" + " \"config-fetch-wait-time\": 20 \n" + "}", + + "{ \"config-databases\": [ \n" + " { \n" + " \"type\": \"mysql\", \n" + " \"user\":\"tom\", \n" + " \"password\":\"terrific\" \n" + " }, \n" + " { \n" + " \"type\": \"postgresql\",\n" + " \"user\":\"bob\", \n" + " \"password\":\"wonder\" \n" + " } \n" + "] } \n" + }; + + for (auto config : configs) { + ConfigControlParser parser; + ConfigControlInfoPtr ctl_info; + + // Turn the JSON config into Elements. + ElementPtr source_elem; + ASSERT_NO_THROW (source_elem = Element::fromJSON(config)) << + " JSON error, test is broken: " << config; + + // Parse the Elements into a ConfigControlInfo. + ASSERT_NO_THROW(ctl_info = parser.parse(source_elem)); + ASSERT_TRUE(ctl_info); + + // Turn the newly constructed info instance back into elements. + ElementPtr parsed_elem; + ASSERT_NO_THROW(parsed_elem = ctl_info->toElement()); + + // When the config is empty, ControlConfigInfo::toElement still + // generates a map with an empty db list. Replace source for + // element comparison below. + if (source_elem->size() == 0) { + ASSERT_NO_THROW (source_elem = Element::fromJSON( + "{ \"config-databases\": [] }")); + } + + // The parsed element should match the source element. + EXPECT_TRUE(parsed_elem->equals(*source_elem)) << "config: " << config; + } +} + +// Verify that invalid configurations fail to parse gracefully. +TEST(ConfigCtlInfoParser, invalidConfigs) { + // Note that configurations are must be valid JSON, but invalid logically. + std::string configs[] = { + "{ \"config-databases\": \"not_list\" }", + "{ \"config-databases\": [ \n" + " { \n" + " \"bogus\": \"param\" \n" + " } \n" + "] } \n", + "{ \"config-fetch-wait-time\": -1 }", + "{ \"config-fetch-wait-time\": 65537 }", + "{ \"config-fetch-wait-time\": \"a-string\" }", + }; + + for (auto config : configs) { + ConfigControlParser parser; + + // Turn the JSON config into Elements. + ElementPtr source_elem; + ASSERT_NO_THROW (source_elem = Element::fromJSON(config)) << + " JSON error, test is broken: " << config; + + // Parse the Elements into a ConfigControlInfo. + ASSERT_THROW(parser.parse(source_elem), isc::ConfigError) + << "config: " << config; + } +} diff --git a/src/lib/process/tests/d_cfg_mgr_unittests.cc b/src/lib/process/tests/d_cfg_mgr_unittests.cc new file mode 100644 index 0000000..faa3e92 --- /dev/null +++ b/src/lib/process/tests/d_cfg_mgr_unittests.cc @@ -0,0 +1,375 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/command_interpreter.h> +#include <exceptions/exceptions.h> +#include <process/testutils/d_test_stubs.h> +#include <process/d_cfg_mgr.h> +#include <process/redact_config.h> + +#include <boost/foreach.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <gtest/gtest.h> + +#include <sstream> + +using namespace std; +using namespace isc; +using namespace isc::config; +using namespace isc::process; +using namespace isc::data; +using namespace boost::posix_time; + +namespace { + +/// @brief Test Class for verifying that configuration context cannot be null +/// during construction. +class DCtorTestCfgMgr : public DCfgMgrBase { +public: + /// @brief Constructor - Note that is passes in an empty configuration + /// pointer to the base class constructor. + DCtorTestCfgMgr() : DCfgMgrBase(ConfigPtr()) { + } + + /// @brief Destructor + virtual ~DCtorTestCfgMgr() { + } + + /// @brief Dummy implementation as this method is abstract. + virtual ConfigPtr createNewContext() { + return (ConfigPtr()); + } + + /// @brief Returns summary of configuration in the textual format. + virtual std::string getConfigSummary(const uint32_t) { + return (""); + } +}; + +/// @brief Test fixture class for testing DCfgMgrBase class. +/// It maintains an member instance of DStubCfgMgr and derives from +/// ConfigParseTest fixture, thus providing methods for converting JSON +/// strings to configuration element sets, checking parse results, +/// accessing the configuration context and trying to unparse. +class DStubCfgMgrTest : public ConfigParseTest { +public: + + /// @brief Constructor + DStubCfgMgrTest():cfg_mgr_(new DStubCfgMgr) { + } + + /// @brief Destructor + ~DStubCfgMgrTest() { + } + + /// @brief Convenience method which returns a DStubContextPtr to the + /// configuration context. + /// + /// @return returns a DStubContextPtr. + DStubContextPtr getStubContext() { + return (boost::dynamic_pointer_cast<DStubContext> + (cfg_mgr_->getContext())); + } + + /// @brief Configuration manager instance. + DStubCfgMgrPtr cfg_mgr_; +}; + +///@brief Tests basic construction/destruction of configuration manager. +/// Verifies that: +/// 1. Proper construction succeeds. +/// 2. Configuration context is initialized by construction. +/// 3. Destruction works properly. +/// 4. Construction with a null context is not allowed. +TEST(DCfgMgrBase, construction) { + DCfgMgrBasePtr cfg_mgr; + + // Verify that configuration manager constructions without error. + ASSERT_NO_THROW(cfg_mgr.reset(new DStubCfgMgr())); + + // Verify that the context can be retrieved and is not null. + ConfigPtr context = cfg_mgr->getContext(); + EXPECT_TRUE(context); + + // Verify that the manager can be destructed without error. + EXPECT_NO_THROW(cfg_mgr.reset()); + + // Verify that an attempt to construct a manger with a null context fails. + ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError); +} + +///@brief Tests fundamental aspects of configuration parsing. +/// Verifies that: +/// 1. A correctly formed simple configuration parses without error. +/// 2. An error building the element is handled. +/// 3. An error committing the element is handled. +/// 4. An unknown element error is handled. +TEST_F(DStubCfgMgrTest, basicParseTest) { + // Create a simple configuration. + string config = "{ \"test-value\": [] } "; + ASSERT_TRUE(fromJSON(config)); + + // Verify that we can parse a simple configuration. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + + // Verify that we can check a simple configuration. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, true); + EXPECT_TRUE(checkAnswer(0)); +} + +/// @brief Tests that element ids supported by the base class as well as those +/// added by the derived class function properly. +/// This test verifies that: +/// 1. Boolean parameters can be parsed and retrieved. +/// 2. Uint32 parameters can be parsed and retrieved. +/// 3. String parameters can be parsed and retrieved. +/// 4. Map elements can be parsed and retrieved. +/// 5. List elements can be parsed and retrieved. +/// 6. Parsing a second configuration, updates the existing context values +/// correctly. +TEST_F(DStubCfgMgrTest, simpleTypesTest) { + // Create a configuration with all of the parameters. + string config = "{ \"bool_test\": true , " + " \"uint32_test\": 77 , " + " \"string_test\": \"hmmm chewy\" , " + " \"map_test\" : {} , " + " \"list_test\": [] }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + ASSERT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + + // Create a configuration which "updates" all of the parameter values. + string config2 = "{ \"bool_test\": false , " + " \"uint32_test\": 88 , " + " \"string_test\": \"ewww yuk!\" , " + " \"map_test2\" : {} , " + " \"list_test2\": [] }"; + ASSERT_TRUE(fromJSON(config2)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + context = getStubContext(); + ASSERT_TRUE(context); +} + +/// @brief Tests that the configuration context is preserved after failure +/// during parsing causes a rollback. +/// 1. Verifies configuration context rollback. +TEST_F(DStubCfgMgrTest, rollBackTest) { + // Create a configuration with all of the parameters. + string config = "{ \"bool_test\": true , " + " \"uint32_test\": 77 , " + " \"string_test\": \"hmmm chewy\" , " + " \"map_test\" : {} , " + " \"list_test\": [] }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + + // Create a configuration which "updates" all of the parameter values + // plus one unknown at the end. + string config2 = "{ \"bool_test\": false , " + " \"uint32_test\": 88 , " + " \"string_test\": \"ewww yuk!\" , " + " \"map_test2\" : {} , " + " \"list_test2\": [] , " + " \"zeta_unknown\": 33 } "; + ASSERT_TRUE(fromJSON(config2)); +} + +/// @brief Tests that the configuration context is preserved during +/// check only parsing. +TEST_F(DStubCfgMgrTest, checkOnly) { + // Create a configuration with all of the parameters. + string config = "{ \"bool_test\": true , " + " \"uint32_test\": 77 , " + " \"string_test\": \"hmmm chewy\" , " + " \"map_test\" : {} , " + " \"list_test\": [] }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + + + // Create a configuration which "updates" all of the parameter values. + string config2 = "{ \"bool_test\": false , " + " \"uint32_test\": 88 , " + " \"string_test\": \"ewww yuk!\" , " + " \"map_test2\" : {} , " + " \"list_test2\": [] }"; + ASSERT_TRUE(fromJSON(config2)); + + answer_ = cfg_mgr_->simpleParseConfig(config_set_, true); + EXPECT_TRUE(checkAnswer(0)); + context = getStubContext(); + ASSERT_TRUE(context); + +} + +// Tests that configuration element position is returned by getParam variants. +TEST_F(DStubCfgMgrTest, paramPosition) { + // Create a configuration with one of each scalar types. We end them + // with line feeds so we can test position value. + string config = "{ \"bool_test\": true , \n" + " \"uint32_test\": 77 , \n" + " \"string_test\": \"hmmm chewy\" }"; + ASSERT_TRUE(fromJSON(config)); + + // Verify that the configuration parses without error. + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + ASSERT_TRUE(checkAnswer(0)); + DStubContextPtr context = getStubContext(); + ASSERT_TRUE(context); + +} + +// This tests if some aspects of simpleParseConfig are behaving properly. +// Thorough testing is only possible for specific implementations. This +// is done for control agent (see CtrlAgentControllerTest tests in +// src/bin/agent/tests/ctrl_agent_controller_unittest.cc for example). +// Also, shell tests in src/bin/agent/ctrl_agent_process_tests.sh test +// the whole CA process that uses simpleParseConfig. The alternative +// would be to implement whole parser that would set the context +// properly. The ROI for this is not worth the effort. +TEST_F(DStubCfgMgrTest, simpleParseConfig) { + using namespace isc::data; + + // Passing just null pointer should result in error return code + answer_ = cfg_mgr_->simpleParseConfig(ConstElementPtr(), false); + EXPECT_TRUE(checkAnswer(1)); + + // Ok, now try with a dummy, but valid json code + string config = "{ \"bool_test\": true , \n" + " \"uint32_test\": 77 , \n" + " \"string_test\": \"hmmm chewy\" }"; + ASSERT_NO_THROW(fromJSON(config)); + + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false); + EXPECT_TRUE(checkAnswer(0)); +} + +// This test checks that the post configuration callback function is +// executed by the simpleParseConfig function. +TEST_F(DStubCfgMgrTest, simpleParseConfigWithCallback) { + string config = "{ \"bool_test\": true , \n" + " \"uint32_test\": 77 , \n" + " \"string_test\": \"hmmm chewy\" }"; + ASSERT_NO_THROW(fromJSON(config)); + + answer_ = cfg_mgr_->simpleParseConfig(config_set_, false, + []() { + isc_throw(Unexpected, "unexpected configuration error"); + }); + EXPECT_TRUE(checkAnswer(1)); +} + +// This test checks that redactConfig works as expected. +TEST_F(DStubCfgMgrTest, redactConfig) { + // Basic case. + string config = "{ \"foo\": 1 }"; + ConstElementPtr elem; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ConstElementPtr ret; + ASSERT_NO_THROW(ret = redactConfig(elem)); + EXPECT_EQ(ret->str(), elem->str()); + + // Verify redaction. + config = "{ \"password\": \"foo\", \"secret\": \"bar\" }"; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ASSERT_NO_THROW(ret = redactConfig(elem)); + string expected = "{ \"password\": \"*****\", \"secret\": \"*****\" }"; + EXPECT_EQ(expected, ret->str()); + + // Verify that user context are skipped. + config = "{ \"user-context\": { \"password\": \"foo\" } }"; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ASSERT_NO_THROW(ret = redactConfig(elem)); + EXPECT_EQ(ret->str(), elem->str()); + + // Verify that only given subtrees are handled. + list<string> keys = { "foo" }; + config = "{ \"foo\": { \"password\": \"foo\" }, "; + config += "\"next\": { \"secret\": \"bar\" } }"; + ASSERT_NO_THROW(elem = Element::fromJSON(config)); + ASSERT_NO_THROW(ret = redactConfig(elem, keys)); + expected = "{ \"foo\": { \"password\": \"*****\" }, "; + expected += "\"next\": { \"secret\": \"bar\" } }"; + EXPECT_EQ(expected, ret->str()); +} + +// Test that user context is not touched when configuration is redacted. +TEST(RedactConfig, userContext) { + ConstElementPtr const config(Element::fromJSON(R"( + { + "some-database": { + "password": "sensitive", + "secret": "sensitive", + "user": "keatest", + "nested-map": { + "password": "sensitive", + "secret": "sensitive", + "user": "keatest" + } + }, + "user-context": { + "password": "keatest", + "secret": "keatest", + "user": "keatest", + "nested-map": { + "password": "keatest", + "secret": "keatest", + "user": "keatest" + } + } + } + )")); + ConstElementPtr const expected(Element::fromJSON(R"( + { + "some-database": { + "password": "*****", + "secret": "*****", + "user": "keatest", + "nested-map": { + "password": "*****", + "secret": "*****", + "user": "keatest" + } + }, + "user-context": { + "password": "keatest", + "secret": "keatest", + "user": "keatest", + "nested-map": { + "password": "keatest", + "secret": "keatest", + "user": "keatest" + } + } + } + )")); + ConstElementPtr redacted(redactConfig(config)); + EXPECT_TRUE(isEquivalent(redacted, expected)) + << "Actual:\n" << prettyPrint(redacted) << "\n" + "Expected:\n" << prettyPrint(expected); +} + +} // end of anonymous namespace diff --git a/src/lib/process/tests/d_controller_unittests.cc b/src/lib/process/tests/d_controller_unittests.cc new file mode 100644 index 0000000..4d7b73c --- /dev/null +++ b/src/lib/process/tests/d_controller_unittests.cc @@ -0,0 +1,470 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <kea_version.h> + +#include <asiolink/testutils/timed_signal.h> +#include <cc/command_interpreter.h> +#include <process/testutils/d_test_stubs.h> + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <gtest/gtest.h> + +#include <sstream> + +using namespace isc::asiolink::test; +using namespace boost::posix_time; + +namespace isc { +namespace process { + +/// @brief Test fixture class for testing DControllerBase class. This class +/// derives from DControllerTest and wraps a DStubController. DStubController +/// has been constructed to exercise DControllerBase. +class DStubControllerTest : public DControllerTest { +public: + /// @brief Constructor. + /// Note the constructor passes in the static DStubController instance + /// method. + DStubControllerTest() : DControllerTest(DStubController::instance) { + controller_ = boost::dynamic_pointer_cast<DStubController> + (DControllerTest:: + getController()); + } + + /// @brief The controller. + DStubControllerPtr controller_; +}; + +/// @brief Basic Controller instantiation testing. +/// Verifies that the controller singleton gets created and that the +/// basic derivation from the base class is intact. +TEST_F(DStubControllerTest, basicInstanceTesting) { + // Verify that the singleton exists and it is the correct type. + DControllerBasePtr& controller = DControllerTest::getController(); + ASSERT_TRUE(controller); + ASSERT_NO_THROW(boost::dynamic_pointer_cast<DStubController>(controller)); + + // Verify that controller's app name is correct. + EXPECT_TRUE(checkAppName(DStubController::stub_app_name_)); + + // Verify that controller's bin name is correct. + EXPECT_TRUE(checkBinName(DStubController::stub_bin_name_)); + + // Verify that controller's IOService exists. + EXPECT_TRUE(checkIOService()); + + // Verify that the Process does NOT exist. + EXPECT_FALSE(checkProcess()); +} + +/// @brief Tests basic command line processing. +/// Verifies that: +/// 1. Standard command line options are supported. +/// 2. Custom command line options are supported. +/// 3. Invalid options are detected. +/// 4. Extraneous command line information is detected. +TEST_F(DStubControllerTest, commandLineArgs) { + + // Verify that verbose flag is false initially. + EXPECT_TRUE(checkVerbose(false)); + + // Verify that standard options can be parsed without error. + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>("cfgName"), + const_cast<char*>("-d") }; + int argc = 4; + EXPECT_NO_THROW(parseArgs(argc, argv)); + + // Verify that verbose is true. + EXPECT_TRUE(checkVerbose(true)); + + // Verify configuration file name is correct + EXPECT_TRUE(checkConfigFileName("cfgName")); + + // Verify that the custom command line option is parsed without error. + char xopt[3] = "- "; + xopt[1] = *DStubController::stub_option_x_; + char* argv1[] = { const_cast<char*>("progName"), xopt}; + argc = 2; + EXPECT_NO_THROW (parseArgs(argc, argv1)); + + // Verify that an unknown option is detected. + char* argv2[] = { const_cast<char*>("progName"), + const_cast<char*>("-bs") }; + argc = 2; + EXPECT_THROW (parseArgs(argc, argv2), InvalidUsage); + + // Verify that extraneous information is detected. + char* argv3[] = { const_cast<char*>("progName"), + const_cast<char*>("extra"), + const_cast<char*>("information") }; + argc = 3; + EXPECT_THROW (parseArgs(argc, argv3), InvalidUsage); +} + +/// @brief Tests application process creation and initialization. +/// Verifies that: +/// 1. An error during process creation is handled. +/// 2. A NULL returned by process creation is handled. +/// 3. An error during process initialization is handled. +/// 4. Process can be successfully created and initialized. +TEST_F(DStubControllerTest, initProcessTesting) { + // Verify that a failure during process creation is caught. + SimFailure::set(SimFailure::ftCreateProcessException); + EXPECT_THROW(initProcess(), DControllerBaseError); + EXPECT_FALSE(checkProcess()); + + // Verify that a NULL returned by process creation is handled. + SimFailure::set(SimFailure::ftCreateProcessNull); + EXPECT_THROW(initProcess(), DControllerBaseError); + EXPECT_FALSE(checkProcess()); + + // Re-create controller, verify that we are starting clean + resetController(); + EXPECT_FALSE(checkProcess()); + + // Verify that an error during process initialization is handled. + SimFailure::set(SimFailure::ftProcessInit); + EXPECT_THROW(initProcess(), DProcessBaseError); + + // Re-create controller, verify that we are starting clean + resetController(); + EXPECT_FALSE(checkProcess()); + + // Verify that the application process can created and initialized. + ASSERT_NO_THROW(initProcess()); + EXPECT_TRUE(checkProcess()); +} + +/// @brief Tests launch handling of invalid command line. +/// This test launches with an invalid command line which should throw +/// an InvalidUsage. +TEST_F(DStubControllerTest, launchInvalidUsage) { + // Command line to run integrated + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-z") }; + int argc = 2; + + // Launch the controller in integrated mode. + EXPECT_THROW(launch(argc, argv), InvalidUsage); +} + +/// @brief Tests launch handling of failure in application process +/// initialization. This test launches with a valid command line but with +/// SimFailure set to fail during process creation. Launch should throw +/// ProcessInitError. +TEST_F(DStubControllerTest, launchProcessInitError) { + // Command line to run integrated + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>(DControllerTest::CFG_TEST_FILE), + const_cast<char*>("-d") }; + int argc = 4; + + // Launch the controller in stand alone mode. + SimFailure::set(SimFailure::ftCreateProcessException); + EXPECT_THROW(launch(argc, argv), ProcessInitError); +} + +/// @brief Tests launch and normal shutdown (stand alone mode). +/// This creates an interval timer to generate a normal shutdown and then +/// launches with a valid, command line, with a valid configuration file +/// and no simulated errors. +TEST_F(DStubControllerTest, launchNormalShutdown) { + // Write the valid, empty, config and then run launch() for 1000 ms + time_duration elapsed_time; + ASSERT_NO_THROW(runWithConfig("{}", 2000, elapsed_time)); + + // Verify that duration of the run invocation is the same as the + // timer duration. This demonstrates that the shutdown was driven + // by an io_service event and callback. + EXPECT_TRUE(elapsed_time.total_milliseconds() >= 1900 && + elapsed_time.total_milliseconds() <= 2300); +} + +/// @brief A variant of the launch and normal shutdown test using a callback. +TEST_F(DStubControllerTest, launchNormalShutdownWithCallback) { + // Write the valid, empty, config and then run launch() for 1000 ms + // Access to the internal state. + auto callback = [&] { EXPECT_FALSE(getProcess()->shouldShutdown()); }; + time_duration elapsed_time; + ASSERT_NO_THROW(runWithConfig("{}", 2000, + static_cast<const TestCallback&>(callback), + elapsed_time)); + + // Verify that duration of the run invocation is the same as the + // timer duration. This demonstrates that the shutdown was driven + // by an io_service event and callback. + EXPECT_TRUE(elapsed_time.total_milliseconds() >= 1900 && + elapsed_time.total_milliseconds() <= 2300); +} + +/// @brief Tests launch with an non-existing configuration file. +TEST_F(DStubControllerTest, nonExistingConfigFile) { + // command line to run standalone + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>("bogus-file"), + const_cast<char*>("-d") }; + int argc = 4; + + // Record start time, and invoke launch(). + EXPECT_THROW(launch(argc, argv), ProcessInitError); +} + +/// @brief Tests launch with configuration file argument but no file name +TEST_F(DStubControllerTest, missingConfigFileName) { + // command line to run standalone + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>("-d") }; + int argc = 3; + + // Record start time, and invoke launch(). + EXPECT_THROW(launch(argc, argv), ProcessInitError); +} + +/// @brief Tests launch with no configuration file argument +TEST_F(DStubControllerTest, missingConfigFileArgument) { + // command line to run standalone + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-d") }; + int argc = 2; + + // Record start time, and invoke launch(). + EXPECT_THROW(launch(argc, argv), LaunchError); +} + +/// @brief Tests launch with an operational error during application execution. +/// This test creates an interval timer to generate a runtime exception during +/// the process event loop. It launches with a valid, stand-alone command line +/// and no simulated errors. Launch should throw ProcessRunError. +TEST_F(DStubControllerTest, launchRuntimeError) { + // Use an asiolink IntervalTimer and callback to generate the + // shutdown invocation. (Note IntervalTimer setup is in milliseconds). + isc::asiolink::IntervalTimer timer(*getIOService()); + timer.setup(genFatalErrorCallback, 2000); + + // Write the valid, empty, config and then run launch() for 5000 ms + time_duration elapsed_time; + EXPECT_THROW(runWithConfig("{}", 5000, elapsed_time), ProcessRunError); + + // Verify that duration of the run invocation is the same as the + // timer duration. This demonstrates that the shutdown was driven + // by an io_service event and callback. + EXPECT_TRUE(elapsed_time.total_milliseconds() >= 1900 && + elapsed_time.total_milliseconds() <= 2300); +} + +/// @brief Configuration update event testing. +/// This really tests just the ability of the handlers to invoke the necessary +/// chain of methods and handle error conditions. Configuration parsing and +/// retrieval should be tested as part of the d2 configuration management +/// implementation. +/// This test verifies that: +/// 1. That a valid configuration update results in successful status return. +/// 2. That an application process error in configuration updating is handled +/// properly. +TEST_F(DStubControllerTest, configUpdateTests) { + int rcode = -1; + isc::data::ConstElementPtr answer; + + // Initialize the application process. + ASSERT_NO_THROW(initProcess()); + EXPECT_TRUE(checkProcess()); + + // Create a configuration set. Content is arbitrary, just needs to be + // valid JSON. + std::string config = "{ \"test-value\": 1000 } "; + isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config); + + // Verify that a valid config gets a successful update result. + answer = updateConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(0, rcode); + + // Verify that a valid config gets a successful check result. + answer = checkConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(0, rcode); + + // Verify that an error in process configure method is handled. + SimFailure::set(SimFailure::ftProcessConfigure); + answer = updateConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(1, rcode); + + // Verify that an error is handled too when the config is checked for. + SimFailure::set(SimFailure::ftProcessConfigure); + answer = checkConfig(config_set); + isc::config::parseAnswer(rcode, answer); + EXPECT_EQ(1, rcode); +} + +// Tests that handleOtherObjects behaves as expected. +TEST_F(DStubControllerTest, handleOtherObjects) { + using namespace isc::data; + + // A bad config. + ElementPtr config = Element::createMap(); + config->set(controller_->getAppName(), Element::create(1)); + config->set("foo", Element::create(2)); + config->set("bar", Element::create(3)); + + // Check the error message. + std::string errmsg; + EXPECT_NO_THROW(errmsg = controller_->handleOtherObjects(config)); + EXPECT_EQ(" contains unsupported 'bar' parameter (and 'foo')", errmsg); + + // Retry with no error. + config = Element::createMap(); + config->set(controller_->getAppName(), Element::create(1)); + EXPECT_NO_THROW(errmsg = controller_->handleOtherObjects(config)); + EXPECT_TRUE(errmsg.empty()); +} + +// Tests that registered signals are caught and handled. +TEST_F(DStubControllerTest, ioSignals) { + // Tell test controller just to record the signals, don't call the + // base class signal handler. + controller_->recordSignalOnly(true); + + // Setup to raise SIGHUP in 10 ms. + TimedSignal sighup(*getIOService(), SIGHUP, 10); + TimedSignal sigint(*getIOService(), SIGINT, 100); + TimedSignal sigterm(*getIOService(), SIGTERM, 200); + + // Write the valid, empty, config and then run launch() for 500 ms + time_duration elapsed_time; + runWithConfig("{}", 500, elapsed_time); + + // Verify that we caught the signals as expected. + std::vector<int>& signals = controller_->getProcessedSignals(); + ASSERT_EQ(3, signals.size()); + EXPECT_EQ(SIGHUP, signals[0]); + EXPECT_EQ(SIGINT, signals[1]); + EXPECT_EQ(SIGTERM, signals[2]); +} + +// Tests that the original configuration is retained after a SIGHUP triggered +// reconfiguration fails due to invalid config content. +TEST_F(DStubControllerTest, invalidConfigReload) { + // Schedule to rewrite the configuration file after launch. This way the + // file is updated after we have done the initial configuration. The + // new content is invalid JSON which will cause the config parse to fail. + scheduleTimedWrite("{ \"string_test\": BOGUS JSON }", 100); + + // Setup to raise SIGHUP in 200 ms. + TimedSignal sighup(*getIOService(), SIGHUP, 200); + + // Write the config and then run launch() for 500 ms + // After startup, which will load the initial configuration this enters + // the process's runIO() loop. We will first rewrite the config file. + // Next we process the SIGHUP signal which should cause us to reconfigure. + time_duration elapsed_time; + runWithConfig("{ \"string_test\": \"first value\" }", 500, elapsed_time); + + // Verify that we saw the signal. + std::vector<int>& signals = controller_->getProcessedSignals(); + ASSERT_EQ(1, signals.size()); + EXPECT_EQ(SIGHUP, signals[0]); +} + +// Tests that the original configuration is retained after a SIGHUP triggered +// reconfiguration fails due to invalid config content. +TEST_F(DStubControllerTest, alternateParsing) { + controller_->useAlternateParser(true); + + // Setup to raise SIGHUP in 200 ms. + TimedSignal sighup(*getIOService(), SIGHUP, 200); + + // Write the config and then run launch() for 500 ms + // After startup, which will load the initial configuration this enters + // the process's runIO() loop. We will first rewrite the config file. + // Next we process the SIGHUP signal which should cause us to reconfigure. + time_duration elapsed_time; + runWithConfig("{ \"string_test\": \"first value\" }", 500, elapsed_time); + + // Verify that we saw the signal. + std::vector<int>& signals = controller_->getProcessedSignals(); + ASSERT_EQ(1, signals.size()); + EXPECT_EQ(SIGHUP, signals[0]); +} + +// Tests that the original configuration is replaced after a SIGHUP triggered +// reconfiguration succeeds. +TEST_F(DStubControllerTest, validConfigReload) { + // Schedule to rewrite the configuration file after launch. This way the + // file is updated after we have done the initial configuration. + scheduleTimedWrite("{ \"string_test\": \"second value\" }", 100); + + // Setup to raise SIGHUP in 200 ms and another at 400 ms. + TimedSignal sighup(*getIOService(), SIGHUP, 200); + TimedSignal sighup2(*getIOService(), SIGHUP, 400); + + // Write the config and then run launch() for 800 ms + time_duration elapsed_time; + runWithConfig("{ \"string_test\": \"first value\" }", 800, elapsed_time); + + // Verify that we saw two occurrences of the signal. + std::vector<int>& signals = controller_->getProcessedSignals(); + ASSERT_EQ(2, signals.size()); + EXPECT_EQ(SIGHUP, signals[0]); + EXPECT_EQ(SIGHUP, signals[1]); +} + +// Tests that the SIGINT triggers a normal shutdown. +TEST_F(DStubControllerTest, sigintShutdown) { + // Setup to raise SIGHUP in 1 ms. + TimedSignal sighup(*getIOService(), SIGINT, 1); + + // Write the config and then run launch() for 1000 ms + time_duration elapsed_time; + runWithConfig("{ \"string_test\": \"first value\" }", 1000, elapsed_time); + + // Verify that we saw the signal. + std::vector<int>& signals = controller_->getProcessedSignals(); + ASSERT_EQ(1, signals.size()); + EXPECT_EQ(SIGINT, signals[0]); + + // Duration should be significantly less than our max run time. + EXPECT_TRUE(elapsed_time.total_milliseconds() < 300); +} + +// Verifies that version and extended version information is correct +TEST_F(DStubControllerTest, getVersion) { + std::string text = controller_->getVersion(false); + EXPECT_EQ(text,VERSION); + + text = controller_->getVersion(true); + EXPECT_NE(std::string::npos, text.find(VERSION)); + EXPECT_NE(std::string::npos, text.find(EXTENDED_VERSION)); + EXPECT_NE(std::string::npos, text.find(controller_->getVersionAddendum())); +} + +// Tests that the SIGTERM triggers a normal shutdown. +TEST_F(DStubControllerTest, sigtermShutdown) { + // Setup to raise SIGHUP in 1 ms. + TimedSignal sighup(*getIOService(), SIGTERM, 1); + + // Write the config and then run launch() for 1000 ms + time_duration elapsed_time; + runWithConfig("{ \"string_test\": \"first value\" }", 1000, elapsed_time); + + // Verify that we saw the signal. + std::vector<int>& signals = controller_->getProcessedSignals(); + ASSERT_EQ(1, signals.size()); + EXPECT_EQ(SIGTERM, signals[0]); + + // Duration should be significantly less than our max run time. + EXPECT_TRUE(elapsed_time.total_milliseconds() < 300); +} + +}; // end of isc::process namespace +}; // end of isc namespace diff --git a/src/lib/process/tests/daemon_unittest.cc b/src/lib/process/tests/daemon_unittest.cc new file mode 100644 index 0000000..bd80e25 --- /dev/null +++ b/src/lib/process/tests/daemon_unittest.cc @@ -0,0 +1,324 @@ +// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <cc/data.h> +#include <process/daemon.h> +#include <process/config_base.h> +#include <process/log_parser.h> +#include <log/logger_support.h> + +#include <gtest/gtest.h> + +#include <sys/wait.h> + +using namespace isc; +using namespace isc::process; +using namespace isc::data; + +namespace isc { +namespace process { + +// @brief Derived Daemon class +class DaemonImpl : public Daemon { +public: + static std::string getVersion(bool extended); + + using Daemon::makePIDFileName; +}; + +std::string DaemonImpl::getVersion(bool extended) { + if (extended) { + return (std::string("EXTENDED")); + } else { + return (std::string("BASIC")); + } +} + +}; +}; + +namespace { + +/// @brief Daemon Test test fixture class +class DaemonTest : public ::testing::Test { +public: + /// @brief Constructor + DaemonTest() : env_copy_() { + // Take a copy of KEA_PIDFILE_DIR environment variable value + char *env_value = getenv("KEA_PIDFILE_DIR"); + if (env_value) { + env_copy_ = std::string(env_value); + } + } + + /// @brief Destructor + /// + /// As some of the tests have the side-effect of altering the logging + /// settings (when configureLogger is called), the logging is reset to + /// the default after each test completes. + ~DaemonTest() { + isc::log::setDefaultLoggingOutput(); + // Restore KEA_PIDFILE_DIR environment variable value + if (env_copy_.empty()) { + static_cast<void>(unsetenv("KEA_PIDFILE_DIR")); + } else { + static_cast<void>(setenv("KEA_PIDFILE_DIR", env_copy_.c_str(), 1)); + } + } + +private: + /// @brief copy of KEA_PIDFILE_DIR environment variable value + std::string env_copy_; +}; + + +// Very simple test. Checks whether Daemon can be instantiated and its +// default parameters are sane +TEST_F(DaemonTest, constructor) { + // Disable KEA_PIDFILE_DIR + EXPECT_EQ(0, unsetenv("KEA_PIDFILE_DIR")); + + EXPECT_NO_THROW(Daemon instance1); + + // Check only instance values. + Daemon instance2; + EXPECT_TRUE(instance2.getConfigFile().empty()); + EXPECT_EQ(std::string(DATA_DIR), instance2.getPIDFileDir()); + EXPECT_TRUE(instance2.getPIDFileName().empty()); +} + +// Verify config file accessors +TEST_F(DaemonTest, getSetConfigFile) { + Daemon instance; + + EXPECT_NO_THROW(instance.setConfigFile("test.txt")); + EXPECT_EQ("test.txt", instance.getConfigFile()); + EXPECT_NO_THROW(instance.checkConfigFile()); +} + +// Verify config file checker. +TEST_F(DaemonTest, checkConfigFile) { + Daemon instance; + + EXPECT_THROW(instance.checkConfigFile(), BadValue); + EXPECT_NO_THROW(instance.setConfigFile("/tmp/")); + EXPECT_THROW(instance.checkConfigFile(), BadValue); + EXPECT_NO_THROW(instance.setConfigFile("/tmp/test.txt")); + EXPECT_NO_THROW(instance.checkConfigFile()); +} + +// Verify process name accessors +TEST_F(DaemonTest, getSetProcName) { + Daemon instance; + + EXPECT_NO_THROW(instance.setProcName("myproc")); + EXPECT_EQ("myproc", instance.getProcName()); +} + +// Verify PID file directory name accessors +TEST_F(DaemonTest, getSetPIDFileDir) { + Daemon instance; + + EXPECT_NO_THROW(instance.setPIDFileDir("/tmp")); + EXPECT_EQ("/tmp", instance.getPIDFileDir()); +} + +// Verify PID file name accessors. +TEST_F(DaemonTest, setPIDFileName) { + Daemon instance; + + // Verify that PID file name may not be set to empty + EXPECT_THROW(instance.setPIDFileName(""), BadValue); + + EXPECT_NO_THROW(instance.setPIDFileName("myproc")); + EXPECT_EQ("myproc", instance.getPIDFileName()); + + // Verify that setPIDFileName cannot be called twice on the same instance. + EXPECT_THROW(instance.setPIDFileName("again"), InvalidOperation); +} + +// Test the getVersion() redefinition +TEST_F(DaemonTest, getVersion) { + EXPECT_THROW(Daemon::getVersion(false), NotImplemented); + + ASSERT_NO_THROW(DaemonImpl::getVersion(false)); + + EXPECT_EQ(DaemonImpl::getVersion(false), "BASIC"); + + ASSERT_NO_THROW(DaemonImpl::getVersion(true)); + + EXPECT_EQ(DaemonImpl::getVersion(true), "EXTENDED"); +} + +// Verify makePIDFileName method +TEST_F(DaemonTest, makePIDFileName) { + DaemonImpl instance; + + // Verify that config file cannot be blank + instance.setProcName("notblank"); + EXPECT_THROW(instance.makePIDFileName(), InvalidOperation); + + // Verify that proc name cannot be blank + instance.setProcName(""); + instance.setConfigFile("notblank"); + EXPECT_THROW(instance.makePIDFileName(), InvalidOperation); + + // Verify that config file must contain a file name + instance.setProcName("myproc"); + instance.setConfigFile(".txt"); + EXPECT_THROW(instance.makePIDFileName(), BadValue); + instance.setConfigFile("/tmp/"); + EXPECT_THROW(instance.makePIDFileName(), BadValue); + + // Given a valid config file name and proc name we should good to go + instance.setConfigFile("/tmp/test.conf"); + std::string name; + EXPECT_NO_THROW(name = instance.makePIDFileName()); + + // Make sure the name is as we expect + std::ostringstream stream; + stream << instance.getPIDFileDir() << "/test.myproc.pid"; + EXPECT_EQ(stream.str(), name); + + // Verify that the default directory can be overridden + instance.setPIDFileDir("/tmp"); + EXPECT_NO_THROW(name = instance.makePIDFileName()); + EXPECT_EQ("/tmp/test.myproc.pid", name); +} + +// Verifies the creation a PID file and that a pre-existing PID file +// which points to a live PID causes a throw. +TEST_F(DaemonTest, createPIDFile) { + DaemonImpl instance; + + instance.setConfigFile("test.conf"); + instance.setProcName("daemon_test"); + instance.setPIDFileDir(TEST_DATA_BUILDDIR); + + EXPECT_NO_THROW(instance.createPIDFile()); + + std::ostringstream stream; + stream << TEST_DATA_BUILDDIR << "/test.daemon_test.pid"; + EXPECT_EQ(stream.str(), instance.getPIDFileName()); + + // If we try again, we should see our own PID file and fail + EXPECT_THROW(instance.createPIDFile(), DaemonPIDExists); +} + +// Verifies that a pre-existing PID file which points to a dead PID +// is overwritten. +TEST_F(DaemonTest, createPIDFileOverwrite) { + DaemonImpl instance; + + // We're going to use fork to generate a PID we KNOW is dead. + int pid = fork(); + ASSERT_GE(pid, 0); + + if (pid == 0) { + // This is the child, die right away. Tragic, no? + _exit (0); + } + + // Back in the parent test, we need to wait for the child to die + // As with debugging waitpid() can be interrupted ignore EINTR. + int stat; + int ret; + do { + ret = waitpid(pid, &stat, 0); + } while ((ret == -1) && (errno == EINTR)); + ASSERT_EQ(ret, pid); + + // Ok, so we should now have a PID that we know to be dead. + // Let's use it to create a PID file. + instance.setConfigFile("test.conf"); + instance.setProcName("daemon_test"); + instance.setPIDFileDir(TEST_DATA_BUILDDIR); + EXPECT_NO_THROW(instance.createPIDFile(pid)); + + // If we try to create the PID file again, this should work. + EXPECT_NO_THROW(instance.createPIDFile()); +} + +// Verifies that Daemon destruction deletes the PID file +TEST_F(DaemonTest, PIDFileCleanup) { + boost::shared_ptr<DaemonImpl> instance; + instance.reset(new DaemonImpl); + + instance->setConfigFile("test.conf"); + instance->setProcName("daemon_test"); + instance->setPIDFileDir(TEST_DATA_BUILDDIR); + EXPECT_NO_THROW(instance->createPIDFile()); + + // If we try again, we should see our own PID file + EXPECT_THROW(instance->createPIDFile(), DaemonPIDExists); + + // Save the pid file name + std::string pid_file_name = instance->getPIDFileName(); + + // Now delete the Daemon instance. This should remove the + // PID file. + instance.reset(); + + struct stat stat_buf; + ASSERT_EQ(-1, stat(pid_file_name.c_str(), &stat_buf)); + EXPECT_EQ(errno, ENOENT); +} + +// Checks that configureLogger method is behaving properly. +// More dedicated tests are available for LogConfigParser class. +// See logger_unittest.cc +TEST_F(DaemonTest, parsingConsoleOutput) { + Daemon::setVerbose(false); + + // Storage - parsed configuration will be stored here + ConfigPtr storage(new ConfigBase()); + + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"stdout\"" + " }" + " ]," + " \"debuglevel\": 99," + " \"severity\": \"DEBUG\"" + " }" + "]}"; + ConstElementPtr config = Element::fromJSON(config_txt); + + // Spawn a daemon and tell it to configure logger + Daemon x; + EXPECT_NO_THROW(x.configureLogger(config, storage)); + + // The parsed configuration should be processed by the daemon and + // stored in configuration storage. + ASSERT_EQ(1, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(99, storage->getLoggingInfo()[0].debuglevel_); + EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[0].severity_); + + ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_); +} + +TEST_F(DaemonTest, exitValue) { + DaemonImpl instance; + + EXPECT_EQ(EXIT_SUCCESS, instance.getExitValue()); + instance.setExitValue(77); + EXPECT_EQ(77, instance.getExitValue()); +} + + +// More tests will appear here as we develop Daemon class. + +}; diff --git a/src/lib/process/tests/log_parser_unittests.cc b/src/lib/process/tests/log_parser_unittests.cc new file mode 100644 index 0000000..96f6278 --- /dev/null +++ b/src/lib/process/tests/log_parser_unittests.cc @@ -0,0 +1,512 @@ +// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <cc/data.h> +#include <process/log_parser.h> +#include <process/process_messages.h> +#include <exceptions/exceptions.h> +#include <log/logger_support.h> +#include <process/d_log.h> +#include <testutils/io_utils.h> + +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::process; +using namespace isc::data; + +namespace { + +/// @brief Logging Test Fixture Class +/// +/// Trivial class that ensures that the logging is reset to its defaults after +/// each test. Strictly speaking this only resets the testing root logger (which +/// has the name "kea") but as the only other logger mentioned here ("wombat") +/// is not used elsewhere, that is sufficient. +class LoggingTest : public ::testing::Test { + public: + /// @brief Constructor + LoggingTest() {} + + /// @brief Destructor + /// + /// Reset root logger back to defaults. + ~LoggingTest() { + isc::log::initLogger(); + wipeFiles(); + } + + /// @brief Generates a log file name suffixed with a rotation number + /// @param rotation number to the append to the end of the file + std::string logName(int rotation) { + std::ostringstream os; + os << TEST_LOG_NAME << "." << rotation; + return (os.str()); + } + + /// @brief Removes the base log file name and 1 rotation + void wipeFiles() { + static_cast<void>(remove(TEST_LOG_NAME)); + for (int i = 1; i < TEST_MAX_VERS + 1; ++i) { + static_cast<void>(remove(logName(i).c_str())); + } + + // Remove the lock file + std::ostringstream os; + os << TEST_LOG_NAME << ".lock"; + static_cast<void>(remove(os.str().c_str())); + } + + /// @brief Name of the log file + static const char* TEST_LOG_NAME; + + /// @brief Maximum log size + static const int TEST_MAX_SIZE; + + /// @brief Maximum rotated log versions + static const int TEST_MAX_VERS; + +}; + +const char* LoggingTest::TEST_LOG_NAME = "kea.test.log"; +const int LoggingTest::TEST_MAX_SIZE = 204800; // Smallest without disabling rotation +const int LoggingTest::TEST_MAX_VERS = 2; // More than the default of 1 + +// Checks that the constructor is able to process specified storage properly. +TEST_F(LoggingTest, constructor) { + + ConfigPtr null_ptr; + EXPECT_THROW(LogConfigParser parser(null_ptr), BadValue); + + ConfigPtr nonnull(new ConfigBase()); + + EXPECT_NO_THROW(LogConfigParser parser(nonnull)); +} + +// Checks if the LogConfigParser class is able to transform JSON structures +// into Configuration usable by log4cplus. This test checks for output +// configured to stdout on debug level. +TEST_F(LoggingTest, parsingConsoleOutput) { + + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"stdout\"," + " \"flush\": true" + " }" + " ]," + " \"debuglevel\": 99," + " \"severity\": \"DEBUG\"" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_NO_THROW(parser.parseConfiguration(config)); + + ASSERT_EQ(1, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(99, storage->getLoggingInfo()[0].debuglevel_); + EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[0].severity_); + + ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_); + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_); +} + +// Checks if the LogConfigParser class fails when the configuration +// lacks a severity entry. +TEST_F(LoggingTest, parsingNoSeverity) { + + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"stdout\"," + " \"flush\": true" + " }" + " ]," + " \"debuglevel\": 99" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_THROW(parser.parseConfiguration(config), BadValue); +} + +// Checks if the LogConfigParser class is able to transform JSON structures +// into Configuration usable by log4cplus. This test checks for output +// configured to a file on INFO level. +TEST_F(LoggingTest, parsingFile) { + + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"logfile.txt\"" + " }" + " ]," + " \"severity\": \"INFO\"" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_NO_THROW(parser.parseConfiguration(config)); + + ASSERT_EQ(1, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_); + EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_); + + ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_); + // Default for immediate flush is true + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_); + + // Pattern should default to empty string. + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].pattern_.empty()); +} + +// Checks if the LogConfigParser class is able to transform data structures +// into Configuration usable by log4cplus. This test checks that more than +// one logger can be configured. +TEST_F(LoggingTest, multipleLoggers) { + + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"logfile.txt\"," + " \"flush\": true" + " }" + " ]," + " \"severity\": \"INFO\"" + " }," + " {" + " \"name\": \"wombat\"," + " \"output_options\": [" + " {" + " \"output\": \"logfile2.txt\"," + " \"flush\": false" + " }" + " ]," + " \"severity\": \"DEBUG\"," + " \"debuglevel\": 99" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_NO_THROW(parser.parseConfiguration(config)); + + ASSERT_EQ(2, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_); + EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_); + ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_); + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_); + + EXPECT_EQ("wombat", storage->getLoggingInfo()[1].name_); + EXPECT_EQ(99, storage->getLoggingInfo()[1].debuglevel_); + EXPECT_EQ(isc::log::DEBUG, storage->getLoggingInfo()[1].severity_); + ASSERT_EQ(1, storage->getLoggingInfo()[1].destinations_.size()); + EXPECT_EQ("logfile2.txt" , storage->getLoggingInfo()[1].destinations_[0].output_); + EXPECT_FALSE(storage->getLoggingInfo()[1].destinations_[0].flush_); +} + +// Checks if the LogConfigParser class is able to transform data structures +// into Configuration usable by log4cplus. This test checks that more than +// one logging destination can be configured. +TEST_F(LoggingTest, multipleLoggingDestinations) { + + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"logfile.txt\"" + " }," + " {" + " \"output\": \"stdout\"" + " }" + " ]," + " \"severity\": \"INFO\"" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_NO_THROW(parser.parseConfiguration(config)); + + ASSERT_EQ(1, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(0, storage->getLoggingInfo()[0].debuglevel_); + EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_); + ASSERT_EQ(2, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("logfile.txt" , storage->getLoggingInfo()[0].destinations_[0].output_); + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].flush_); + EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[1].output_); + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[1].flush_); +} + +// Verifies that log rotation occurs when configured. We do not +// worry about contents of the log files, only that rotation occurs. +// Such details are tested in lib/log. This test verifies that +// we can correctly configure logging such that rotation occurs as +// expected. +TEST_F(LoggingTest, logRotate) { + wipeFiles(); + + std::ostringstream os; + os << + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"" + << TEST_LOG_NAME << "\"," << + " \"flush\": true," + " \"maxsize\":" + << TEST_MAX_SIZE << "," << + " \"maxver\":" + << TEST_MAX_VERS << + " }" + " ]," + " \"debuglevel\": 99," + " \"severity\": \"DEBUG\"" + " }" + "]}"; + + // Create our server config container. + ConfigPtr server_cfg(new ConfigBase()); + + // LogConfigParser expects a list of loggers, so parse + // the JSON text and extract the "loggers" element from it + ConstElementPtr config = Element::fromJSON(os.str()); + config = config->get("loggers"); + + // Parse the config and then apply it. + LogConfigParser parser(server_cfg); + ASSERT_NO_THROW(parser.parseConfiguration(config)); + ASSERT_NO_THROW(server_cfg->applyLoggingCfg()); + + EXPECT_EQ(TEST_MAX_SIZE, server_cfg->getLoggingInfo()[0].destinations_[0].maxsize_); + EXPECT_EQ(TEST_MAX_VERS, server_cfg->getLoggingInfo()[0].destinations_[0].maxver_); + + // Make sure we have the initial log file. + ASSERT_TRUE(isc::test::fileExists(TEST_LOG_NAME)); + + // Now generate a log we know will be large enough to force a rotation. + // We borrow a one argument log message for the test. + std::string big_arg(TEST_MAX_SIZE, 'x'); + isc::log::Logger logger("kea"); + + for (int i = 1; i < TEST_MAX_VERS + 1; i++) { + // Output the big log and make sure we get the expected rotation file. + LOG_INFO(logger, DCTL_CONFIG_COMPLETE).arg(big_arg); + EXPECT_TRUE(isc::test::fileExists(logName(i).c_str())); + } + + // Clean up. + wipeFiles(); +} + +// Verifies that a valid output option,'pattern' paress correctly. +TEST_F(LoggingTest, validPattern) { + + // Note the backslash must be doubled in the pattern definition. + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"stdout\"," + " \"pattern\": \"mylog %m\\n\"" + " }" + " ]," + " \"severity\": \"INFO\"" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_NO_THROW(parser.parseConfiguration(config)); + + ASSERT_EQ(1, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_); + + ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_); + EXPECT_EQ(storage->getLoggingInfo()[0].destinations_[0].pattern_, + std::string("mylog %m\n")); +} + +// Verifies that output option,'pattern', may be an empty string +TEST_F(LoggingTest, emptyPattern) { + const char* config_txt = + "{ \"loggers\": [" + " {" + " \"name\": \"kea\"," + " \"output_options\": [" + " {" + " \"output\": \"stdout\"," + " \"pattern\": \"\"" + " }" + " ]," + " \"severity\": \"INFO\"" + " }" + "]}"; + + ConfigPtr storage(new ConfigBase()); + + LogConfigParser parser(storage); + + // We need to parse properly formed JSON and then extract + // "loggers" element from it. For some reason fromJSON is + // throwing at opening square bracket + ConstElementPtr config = Element::fromJSON(config_txt); + config = config->get("loggers"); + + EXPECT_NO_THROW(parser.parseConfiguration(config)); + + ASSERT_EQ(1, storage->getLoggingInfo().size()); + + EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_); + EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_); + + ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size()); + EXPECT_EQ("stdout" , storage->getLoggingInfo()[0].destinations_[0].output_); + EXPECT_TRUE(storage->getLoggingInfo()[0].destinations_[0].pattern_.empty()); +} + +void testMaxSize(uint64_t maxsize_candidate, uint64_t expected_maxsize) { + std::string const logger(R"( + { + "loggers": [ + { + + "debuglevel": 99, + "name": "kea", + "output_options": [ + { + "output": "kea.test.log", + "flush": true, + "maxsize": )" + std::to_string(maxsize_candidate) + R"(, + "maxver": 2 + } + ], + "severity": "DEBUG" + } + ] + } + )"); + + // Create our server config container. + ConfigPtr server_cfg(boost::make_shared<ConfigBase>()); + + // LogConfigParser expects a list of loggers, so parse + // the JSON text and extract the "loggers" element from it + ConstElementPtr config(Element::fromJSON(logger)); + config = config->get("loggers"); + + // Parse the config and then apply it. + LogConfigParser parser(server_cfg); + ASSERT_NO_THROW(parser.parseConfiguration(config)); + ASSERT_NO_THROW(server_cfg->applyLoggingCfg()); + + EXPECT_EQ(server_cfg->getLoggingInfo()[0].destinations_[0].maxsize_, + expected_maxsize); +} + +// Test that maxsize can be configured with high values. +TEST_F(LoggingTest, maxsize) { + testMaxSize(TEST_MAX_SIZE, TEST_MAX_SIZE); + testMaxSize(std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::max()); + testMaxSize(std::numeric_limits<uint32_t>::max(), std::numeric_limits<uint32_t>::max()); + testMaxSize(1000LL * std::numeric_limits<int32_t>::max(), 1000LL * std::numeric_limits<int32_t>::max()); + testMaxSize(1000000LL * std::numeric_limits<int32_t>::max(), 1000000LL * std::numeric_limits<int32_t>::max()); +} + +/// @todo Add tests for malformed logging configuration + +/// @todo There is no easy way to test applyConfiguration() and defaultLogging(). +/// To test them, it would require instrumenting log4cplus to actually fake +/// the logging set up. Alternatively, we could develop set of test suites +/// that check each logging destination separately (e.g. configure log file, then +/// check if the file is indeed created or configure stdout destination, then +/// swap console file descriptors and check that messages are really logged. + +} // namespace diff --git a/src/lib/process/tests/logging_info_unittests.cc b/src/lib/process/tests/logging_info_unittests.cc new file mode 100644 index 0000000..388a8a5 --- /dev/null +++ b/src/lib/process/tests/logging_info_unittests.cc @@ -0,0 +1,208 @@ +// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <process/daemon.h> +#include <process/logging_info.h> +#include <testutils/test_to_element.h> +#include <gtest/gtest.h> + +using namespace isc::process; +using namespace isc::test; +using namespace isc::data; + +namespace { + +// Checks if two destinations can be compared for equality. +TEST(LoggingDestination, equals) { + LoggingDestination dest1; + LoggingDestination dest2; + + EXPECT_TRUE(dest1.equals(dest2)); + + dest1.output_ = "stderr"; + EXPECT_FALSE(dest1.equals(dest2)); + + dest2.output_ = "stdout"; + EXPECT_FALSE(dest1.equals(dest2)); + + dest2.output_ = "stderr"; + EXPECT_TRUE(dest1.equals(dest2)); + + dest1.maxver_ = 10; + dest2.maxver_ = 5; + EXPECT_FALSE(dest1.equals(dest2)); + + dest2.maxver_ = 10; + EXPECT_TRUE(dest1.equals(dest2)); + + dest1.maxsize_ = 64; + dest2.maxsize_ = 32; + EXPECT_FALSE(dest1.equals(dest2)); + + dest1.maxsize_ = 32; + EXPECT_TRUE(dest1.equals(dest2)); +} + +/// @brief Test fixture class for testing @c LoggingInfo. +class LoggingInfoTest : public ::testing::Test { +public: + + /// @brief Constructor + LoggingInfoTest() = default; + + /// @brief Destructor + virtual ~LoggingInfoTest() = default; + + /// @brief Setup the test. + virtual void SetUp() { + Daemon::setVerbose(false); + } + + /// @brief Clear after the test. + virtual void TearDown() { + Daemon::setVerbose(false); + } +}; + +// Checks if default logging configuration is correct. +TEST_F(LoggingInfoTest, defaults) { + + // We now need to set the default logger explicitly. + // Otherwise leftovers from previous tests that use DController + // would leave the default logger set to TestBin. + Daemon::setDefaultLoggerName("kea"); + + LoggingInfo info_non_verbose; + + // The DStubTest framework sets up the default binary name to TestBin + EXPECT_EQ("kea", info_non_verbose.name_); + EXPECT_EQ(isc::log::INFO, info_non_verbose.severity_); + EXPECT_EQ(0, info_non_verbose.debuglevel_); + + ASSERT_EQ(1, info_non_verbose.destinations_.size()); + EXPECT_EQ("stdout", info_non_verbose.destinations_[0].output_); + + std::string header = "{\n"; + std::string begin = + "\"name\": \"kea\",\n" + "\"output_options\": [ {\n" + " \"output\": \"stdout\", \"flush\": true, \"pattern\": \"\" } ],\n" + "\"severity\": \""; + std::string dbglvl = "\",\n\"debuglevel\": "; + std::string trailer = "\n}\n"; + std::string expected = header + begin + "INFO" + dbglvl + "0" + trailer; + runToElementTest<LoggingInfo>(expected, info_non_verbose); + + // Add a user context + std::string comment = "\"comment\": \"foo\""; + std::string user_context = "{ " + comment + " }"; + std::string user_context_nl = "{\n" + comment + "\n}"; + EXPECT_FALSE(info_non_verbose.getContext()); + info_non_verbose.setContext(Element::fromJSON(user_context)); + ASSERT_TRUE(info_non_verbose.getContext()); + EXPECT_EQ(user_context, info_non_verbose.getContext()->str()); + expected = header; + expected += "\"user-context\": " + user_context_nl + ",\n"; + expected += begin + "INFO" + dbglvl + "0" + trailer; + runToElementTest<LoggingInfo>(expected, info_non_verbose); + + Daemon::setVerbose(true); + LoggingInfo info_verbose; + EXPECT_EQ("kea", info_verbose.name_); + EXPECT_EQ(isc::log::DEBUG, info_verbose.severity_); + EXPECT_EQ(99, info_verbose.debuglevel_); + + ASSERT_EQ(1, info_verbose.destinations_.size()); + EXPECT_EQ("stdout", info_verbose.destinations_[0].output_); + + EXPECT_EQ(10240000, info_verbose.destinations_[0].maxsize_); + EXPECT_EQ(1, info_verbose.destinations_[0].maxver_); + + expected = header + begin + "DEBUG" + dbglvl + "99" + trailer; + runToElementTest<LoggingInfo>(expected, info_verbose); + + // User comment again + EXPECT_FALSE(info_verbose.getContext()); + info_verbose.setContext(Element::fromJSON(user_context)); + ASSERT_TRUE(info_verbose.getContext()); + EXPECT_EQ(user_context, info_verbose.getContext()->str()); + expected = header; + expected += "\"user-context\": " + user_context_nl + ",\n"; + expected += begin + "DEBUG" + dbglvl + "99" + trailer; + runToElementTest<LoggingInfo>(expected, info_verbose); +} + +// Checks if (in)equality operators work for LoggingInfo. +TEST_F(LoggingInfoTest, equalityOperators) { + LoggingInfo info1; + LoggingInfo info2; + + // Initially, both objects are the same. + EXPECT_TRUE(info1 == info2); + + // Differ by name. + info1.name_ = "foo"; + info2.name_ = "bar"; + EXPECT_FALSE(info1 == info2); + EXPECT_TRUE(info1 != info2); + + // Names equal. + info2.name_ = "foo"; + EXPECT_TRUE(info1 == info2); + EXPECT_FALSE(info1 != info2); + + // Differ by severity. + info1.severity_ = isc::log::DEBUG; + info2.severity_ = isc::log::INFO; + EXPECT_FALSE(info1 == info2); + EXPECT_TRUE(info1 != info2); + + // Severities equal. + info2.severity_ = isc::log::DEBUG; + EXPECT_TRUE(info1 == info2); + EXPECT_FALSE(info1 != info2); + + // Differ by debug level. + info1.debuglevel_ = 99; + info2.debuglevel_ = 1; + EXPECT_FALSE(info1 == info2); + EXPECT_TRUE(info1 != info2); + + // Debug level equal. + info2.debuglevel_ = 99; + EXPECT_TRUE(info1 == info2); + EXPECT_FALSE(info1 != info2); + + // Create two different destinations. + LoggingDestination dest1; + LoggingDestination dest2; + dest1.output_ = "foo"; + dest2.output_ = "bar"; + + // Push destinations in some order to info1. + info1.destinations_.push_back(dest1); + info1.destinations_.push_back(dest2); + + // Push in reverse order to info2. Order shouldn't matter. + info2.destinations_.push_back(dest2); + info2.destinations_.push_back(dest1); + + EXPECT_TRUE(info1 == info2); + EXPECT_FALSE(info1 != info2); + + // Change one of the destinations. + LoggingDestination dest3; + dest3.output_ = "foobar"; + + info2.destinations_[2] = dest3; + + // The should now be unequal. + EXPECT_FALSE(info1 == info2); + EXPECT_TRUE(info1 != info2); +} + +} // end of anonymous namespace diff --git a/src/lib/process/tests/run_unittests.cc b/src/lib/process/tests/run_unittests.cc new file mode 100644 index 0000000..a7c1f68 --- /dev/null +++ b/src/lib/process/tests/run_unittests.cc @@ -0,0 +1,24 @@ +// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <log/logger_support.h> +#include <process/d_log.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + + // Override --localstatedir value for PID files + setenv("KEA_PIDFILE_DIR", TEST_DATA_BUILDDIR, 1); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/lib/process/testutils/Makefile.am b/src/lib/process/testutils/Makefile.am new file mode 100644 index 0000000..42ed4da --- /dev/null +++ b/src/lib/process/testutils/Makefile.am @@ -0,0 +1,27 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += -DDATABASE_SCRIPTS_DIR=\"$(abs_top_srcdir)/src/share/database/scripts\" +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda + +if HAVE_GTEST + +noinst_LTLIBRARIES = libprocesstest.la + +libprocesstest_la_SOURCES = d_test_stubs.cc d_test_stubs.h + +libprocesstest_la_CXXFLAGS = $(AM_CXXFLAGS) +libprocesstest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libprocesstest_la_LDFLAGS = $(AM_LDFLAGS) + +libprocesstest_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libprocesstest_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libprocesstest_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libprocesstest_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la +libprocesstest_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) + +endif diff --git a/src/lib/process/testutils/Makefile.in b/src/lib/process/testutils/Makefile.in new file mode 100644 index 0000000..d8e33fa --- /dev/null +++ b/src/lib/process/testutils/Makefile.in @@ -0,0 +1,846 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 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@ + +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 = src/lib/process/testutils +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_sysrepo.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libprocesstest_la_DEPENDENCIES = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__libprocesstest_la_SOURCES_DIST = d_test_stubs.cc d_test_stubs.h +@HAVE_GTEST_TRUE@am_libprocesstest_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libprocesstest_la-d_test_stubs.lo +libprocesstest_la_OBJECTS = $(am_libprocesstest_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libprocesstest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libprocesstest_la_CXXFLAGS) $(CXXFLAGS) \ + $(libprocesstest_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libprocesstest_la_rpath = +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 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libprocesstest_la-d_test_stubs.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libprocesstest_la_SOURCES) +DIST_SOURCES = $(am__libprocesstest_la_SOURCES_DIST) +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 +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 distdir-am +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 $(top_srcdir)/depcomp +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@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_SYSREPO = @HAVE_SYSREPO@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +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@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +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@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + -DDATABASE_SCRIPTS_DIR=\"$(abs_top_srcdir)/src/share/database/scripts\" \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) +CLEANFILES = *.gcno *.gcda +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libprocesstest.la +@HAVE_GTEST_TRUE@libprocesstest_la_SOURCES = d_test_stubs.cc d_test_stubs.h +@HAVE_GTEST_TRUE@libprocesstest_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libprocesstest_la_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libprocesstest_la_LDFLAGS = $(AM_LDFLAGS) +@HAVE_GTEST_TRUE@libprocesstest_la_LIBADD = $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(BOOST_LIBS) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(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) --foreign src/lib/process/testutils/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/process/testutils/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libprocesstest.la: $(libprocesstest_la_OBJECTS) $(libprocesstest_la_DEPENDENCIES) $(EXTRA_libprocesstest_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libprocesstest_la_LINK) $(am_libprocesstest_la_rpath) $(libprocesstest_la_OBJECTS) $(libprocesstest_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libprocesstest_la-d_test_stubs.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libprocesstest_la-d_test_stubs.lo: d_test_stubs.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocesstest_la_CPPFLAGS) $(CPPFLAGS) $(libprocesstest_la_CXXFLAGS) $(CXXFLAGS) -MT libprocesstest_la-d_test_stubs.lo -MD -MP -MF $(DEPDIR)/libprocesstest_la-d_test_stubs.Tpo -c -o libprocesstest_la-d_test_stubs.lo `test -f 'd_test_stubs.cc' || echo '$(srcdir)/'`d_test_stubs.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libprocesstest_la-d_test_stubs.Tpo $(DEPDIR)/libprocesstest_la-d_test_stubs.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='d_test_stubs.cc' object='libprocesstest_la-d_test_stubs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libprocesstest_la_CPPFLAGS) $(CPPFLAGS) $(libprocesstest_la_CXXFLAGS) $(CXXFLAGS) -c -o libprocesstest_la-d_test_stubs.lo `test -f 'd_test_stubs.cc' || echo '$(srcdir)/'`d_test_stubs.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# 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: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 $(LTLIBRARIES) +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: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libprocesstest_la-d_test_stubs.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile 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 ./$(DEPDIR)/libprocesstest_la-d_test_stubs.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +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 \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + 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-compile mostlyclean-generic \ + mostlyclean-libtool 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/src/lib/process/testutils/d_test_stubs.cc b/src/lib/process/testutils/d_test_stubs.cc new file mode 100644 index 0000000..1186729 --- /dev/null +++ b/src/lib/process/testutils/d_test_stubs.cc @@ -0,0 +1,336 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <asiolink/io_service.h> +#include <process/d_log.h> +#include <process/testutils/d_test_stubs.h> +#include <process/daemon.h> +#include <cc/command_interpreter.h> +#include <functional> + +using namespace boost::asio; + +namespace isc { +namespace process { + +// Initialize the static failure flag. +SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure; + +DStubProcess::DStubProcess(const char* name, asiolink::IOServicePtr io_service) + : DProcessBase(name, io_service, DCfgMgrBasePtr(new DStubCfgMgr())) { +}; + + +void +DStubProcess::init() { + if (SimFailure::shouldFailOn(SimFailure::ftProcessInit)) { + // Simulates a failure to instantiate the process. + isc_throw(DProcessBaseError, "DStubProcess simulated init() failure"); + } +}; + +void +DStubProcess::run() { + // Until shut down or an fatal error occurs, wait for and + // execute a single callback. This is a preliminary implementation + // that is likely to evolve as development progresses. + // To use run(), the "managing" layer must issue an io_service::stop + // or the call to run will continue to block, and shutdown will not + // occur. + asiolink::IOServicePtr& io_service = getIoService(); + while (!shouldShutdown()) { + try { + io_service->run_one(); + } catch (const std::exception& ex) { + isc_throw (DProcessBaseError, + std::string("Process run method failed: ") + ex.what()); + } + } +}; + +isc::data::ConstElementPtr +DStubProcess::shutdown(isc::data::ConstElementPtr /* args */) { + if (SimFailure::shouldFailOn(SimFailure::ftProcessShutdown)) { + // Simulates a failure during shutdown process. + isc_throw(DProcessBaseError, "DStubProcess simulated shutdown failure"); + } + + setShutdownFlag(true); + stopIOService(); + return (isc::config::createAnswer(0, "Shutdown initiated.")); +} + +isc::data::ConstElementPtr +DStubProcess::configure(isc::data::ConstElementPtr config_set, bool check_only) { + if (SimFailure::shouldFailOn(SimFailure::ftProcessConfigure)) { + // Simulates a process configure failure. + return (isc::config::createAnswer(1, + "Simulated process configuration error.")); + } + + return (getCfgMgr()->simpleParseConfig(config_set, check_only)); +} + +DStubProcess::~DStubProcess() { + Daemon::setVerbose(false); +}; + +//************************** DStubController ************************* + +// Define custom command line option command supported by DStubController. +const char* DStubController::stub_option_x_ = "x"; + +/// @brief Defines the app name used to construct the controller +const char* DStubController::stub_app_name_ = "TestService"; + +/// @brief Defines the bin name used to construct the controller +const char* DStubController::stub_bin_name_ = "TestBin"; + + +DControllerBasePtr& +DStubController::instance() { + // If the singleton hasn't been created, do it now. + if (!getController()) { + DControllerBasePtr p(new DStubController()); + setController(p); + } + + return (getController()); +} + +DStubController::DStubController() + : DControllerBase(stub_app_name_, stub_bin_name_), + processed_signals_(), record_signal_only_(false), use_alternate_parser_(false) { +} + +bool +DStubController::customOption(int option, char* /* optarg */) +{ + // Check for the custom option supported by DStubController. + if (static_cast<char>(option) == *stub_option_x_) { + return (true); + } + + return (false); +} + +DProcessBase* DStubController::createProcess() { + if (SimFailure::shouldFailOn(SimFailure::ftCreateProcessException)) { + // Simulates a failure to instantiate the process due to exception. + throw std::runtime_error("SimFailure::ftCreateProcess"); + } + + if (SimFailure::shouldFailOn(SimFailure::ftCreateProcessNull)) { + // Simulates a failure to instantiate the process. + return (NULL); + } + + // This should be a successful instantiation. + return (new DStubProcess(getAppName().c_str(), getIOService())); +} + +const std::string DStubController::getCustomOpts() const { + // Return the "list" of custom options supported by DStubController. + return (std::string(stub_option_x_)); +} + +void +DStubController::processSignal(int signum){ + processed_signals_.push_back(signum); + if (record_signal_only_) { + return; + } + + DControllerBase::processSignal(signum); +} + +isc::data::ConstElementPtr +DStubController::parseFile(const std::string& /*file_name*/) { + isc::data::ConstElementPtr elements; + if (use_alternate_parser_) { + std::ostringstream os; + + os << "{ \"" << getController()->getAppName() + << "\": " << std::endl; + os << "{ \"string_test\": \"alt value\" } "; + os << " } " << std::endl; + elements = isc::data::Element::fromJSON(os.str()); + } + + return (elements); +} + +DStubController::~DStubController() { +} + +//************************** DControllerTest ************************* + +void +DControllerTest::writeFile(const std::string& content, + const std::string& module_name) { + std::ofstream out(CFG_TEST_FILE, std::ios::trunc); + ASSERT_TRUE(out.is_open()); + + out << "{ \"" << (!module_name.empty() ? module_name + : getController()->getAppName()) + << "\": " << std::endl; + + out << content; + out << " } " << std::endl; + out.close(); +} + +void +DControllerTest::timedWriteCallback() { + writeFile(new_cfg_content_); +} + +void +DControllerTest::scheduleTimedWrite(const std::string& config, + int write_time_ms) { + new_cfg_content_ = config; + write_timer_.reset(new asiolink::IntervalTimer(*getIOService())); + write_timer_->setup(std::bind(&DControllerTest::timedWriteCallback, this), + write_time_ms, asiolink::IntervalTimer::ONE_SHOT); +} + +void +DControllerTest::runWithConfig(const std::string& config, int run_time_ms, + time_duration& elapsed_time) { + // Create the config file. + writeFile(config); + + // Shutdown (without error) after runtime. + isc::asiolink::IntervalTimer timer(*getIOService()); + timer.setup(genShutdownCallback, run_time_ms); + + // Record start time, and invoke launch(). + // We catch and rethrow to allow testing error scenarios. + ptime start = microsec_clock::universal_time(); + try { + // Set up valid command line arguments + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>(DControllerTest::CFG_TEST_FILE), + const_cast<char*>("-d") }; + launch(4, argv); + } catch (...) { + // calculate elapsed time, then rethrow it + elapsed_time = microsec_clock::universal_time() - start; + throw; + } + + elapsed_time = microsec_clock::universal_time() - start; +} + +void +DControllerTest::runWithConfig(const std::string& config, int run_time_ms, + const TestCallback& callback, + time_duration& elapsed_time) { + // Create the config file. + writeFile(config); + + // Shutdown (without error) after runtime. + isc::asiolink::IntervalTimer timer(*getIOService()); + timer.setup([&] { callback(); genShutdownCallback(); }, run_time_ms); + + // Record start time, and invoke launch(). + // We catch and rethrow to allow testing error scenarios. + ptime start = microsec_clock::universal_time(); + try { + // Set up valid command line arguments + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>(DControllerTest::CFG_TEST_FILE), + const_cast<char*>("-d") }; + launch(4, argv); + } catch (...) { + // calculate elapsed time, then rethrow it + elapsed_time = microsec_clock::universal_time() - start; + throw; + } + + elapsed_time = microsec_clock::universal_time() - start; +} + +DProcessBasePtr +DControllerTest:: getProcess() { + DProcessBasePtr p; + if (getController()) { + p = getController()->getProcess(); + } + return (p); +} + +DCfgMgrBasePtr +DControllerTest::getCfgMgr() { + DCfgMgrBasePtr p; + if (getProcess()) { + p = getProcess()->getCfgMgr(); + } + + return (p); +} + +ConfigPtr +DControllerTest::getContext() { + ConfigPtr p; + if (getCfgMgr()) { + p = getCfgMgr()->getContext(); + } + + return (p); +} + +// Initialize controller wrapper's static instance getter member. +DControllerTest::InstanceGetter DControllerTest::instanceGetter_ = NULL; + +/// @brief Defines the name of the configuration file to use +const char* DControllerTest::CFG_TEST_FILE = "d2-test-config.json"; + +//************************** DStubContext ************************* + +DStubContext::DStubContext() { +} + +DStubContext::~DStubContext() { +} + +ConfigPtr +DStubContext::clone() { + return (ConfigPtr(new DStubContext(*this))); +} + +DStubContext::DStubContext(const DStubContext& rhs): ConfigBase(rhs) { +} + +isc::data::ElementPtr +DStubContext::toElement() const { + return (isc::data::Element::createMap()); +} + +//************************** DStubCfgMgr ************************* + +DStubCfgMgr::DStubCfgMgr() + : DCfgMgrBase(ConfigPtr(new DStubContext())) { +} + +DStubCfgMgr::~DStubCfgMgr() { +} + +ConfigPtr +DStubCfgMgr::createNewContext() { + return (ConfigPtr (new DStubContext())); +} + +isc::data::ConstElementPtr +DStubCfgMgr::parse(isc::data::ConstElementPtr /*config*/, bool /*check_only*/) { + return (isc::config::createAnswer(0, "It all went fine. I promise")); +} + +} // namespace isc::process +} // namespace isc diff --git a/src/lib/process/testutils/d_test_stubs.h b/src/lib/process/testutils/d_test_stubs.h new file mode 100644 index 0000000..9881ee4 --- /dev/null +++ b/src/lib/process/testutils/d_test_stubs.h @@ -0,0 +1,753 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D_TEST_STUBS_H +#define D_TEST_STUBS_H + +#include <asiolink/interval_timer.h> + +#include <cc/data.h> +#include <cc/command_interpreter.h> + +#include <log/logger_support.h> + +#include <process/d_controller.h> +#include <process/d_cfg_mgr.h> + +#include <boost/date_time/posix_time/posix_time.hpp> + +using namespace boost::posix_time; + +#include <gtest/gtest.h> + +#include <fstream> +#include <iostream> +#include <sstream> + +namespace isc { +namespace process { + +/// @brief Class is used to set a globally accessible value that indicates +/// a specific type of failure to simulate. Test derivations of base classes +/// can exercise error handling code paths by testing for specific SimFailure +/// values at the appropriate places and then causing the error to "occur". +/// The class consists of an enumerated set of failures, and static methods +/// for getting, setting, and testing the current value. +class SimFailure { +public: + enum FailureType { + ftUnknown = -1, + ftNoFailure = 0, + ftCreateProcessException, + ftCreateProcessNull, + ftProcessInit, + ftProcessConfigure, + ftProcessShutdown, + ftElementBuild, + ftElementCommit, + ftElementUnknown + }; + + /// @brief Sets the SimFailure value to the given value. + /// + /// @param value is the new value to assign to the global value. + static void set(enum FailureType value) { + failure_type_ = value; + } + + /// @brief Gets the current global SimFailure value + /// + /// @return returns the current SimFailure value + static enum FailureType get() { + return (failure_type_); + } + + /// @brief One-shot test of the SimFailure value. If the global + /// SimFailure value is equal to the given value, clear the global + /// value and return true. This makes it convenient for code to + /// test and react without having to explicitly clear the global + /// value. + /// + /// @param value is the value against which the global value is + /// to be compared. + /// + /// @return returns true if current SimFailure value matches the + /// given value. + static bool shouldFailOn(enum FailureType value) { + if (failure_type_ == value) { + clear(); + return (true); + } + + return (false); + } + + /// @brief Resets the failure type to none. + static void clear() { + failure_type_ = ftNoFailure; + } + + /// @brief Static value for holding the failure type to simulate. + static enum FailureType failure_type_; +}; + +/// @brief Test Derivation of the DProcessBase class. +/// +/// This class is used primarily to server as a test process class for testing +/// DControllerBase. It provides minimal, but sufficient implementation to +/// test the majority of DControllerBase functionality. +class DStubProcess : public DProcessBase { +public: + + /// @brief Constructor + /// + /// @param name name is a text label for the process. Generally used + /// in log statements, but otherwise arbitrary. + /// @param io_service is the io_service used by the caller for + /// asynchronous event handling. + /// + /// @throw DProcessBaseError is io_service is NULL. + DStubProcess(const char* name, asiolink::IOServicePtr io_service); + + /// @brief Invoked after process instantiation to perform initialization. + /// This implementation supports simulating an error initializing the + /// process by throwing a DProcessBaseError if SimFailure is set to + /// ftProcessInit. + virtual void init(); + + /// @brief Implements the process's event loop. + /// This implementation is quite basic, surrounding calls to + /// io_service->runOne() with a test of the shutdown flag. Once invoked, + /// the method will continue until the process itself is exiting due to a + /// request to shutdown or some anomaly forces an exit. + /// @return returns 0 upon a successful, "normal" termination, non-zero to + /// indicate an abnormal termination. + virtual void run(); + + /// @brief Implements the process shutdown procedure. + /// + /// This sets the instance shutdown flag monitored by run() and stops + /// the IO service. + virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr); + + /// @brief Processes the given configuration. + /// + /// This implementation fails if SimFailure is set to ftProcessConfigure. + /// Otherwise it will complete successfully. It does not check the content + /// of the inbound configuration. + /// + /// @param config_set a new configuration (JSON) for the process + /// @param check_only true if configuration is to be verified only, not applied + /// @return an Element that contains the results of configuration composed + /// of an integer status value (0 means successful, non-zero means failure), + /// and a string explanation of the outcome. + virtual isc::data::ConstElementPtr + configure(isc::data::ConstElementPtr config_set, bool check_only); + + /// @brief Returns configuration summary in the textual format. + /// + /// @return Always an empty string. + virtual std::string getConfigSummary(const uint32_t) { + return (""); + } + + /// @brief Destructor + virtual ~DStubProcess(); +}; + + +/// @brief Test Derivation of the DControllerBase class. +/// +/// DControllerBase is an abstract class and therefore requires a derivation +/// for testing. It allows testing the majority of the base class code +/// without polluting production derivations (e.g. D2Process). It uses +/// DStubProcess as its application process class. It is a full enough +/// implementation to support running both stand alone and integrated. +class DStubController : public DControllerBase { +public: + /// @brief Static singleton instance method. This method returns the + /// base class singleton instance member. It instantiates the singleton + /// and sets the base class instance member upon first invocation. + /// + /// @return returns a pointer reference to the singleton instance. + static DControllerBasePtr& instance(); + + /// @brief Defines a custom command line option supported by + /// DStubController. + static const char* stub_option_x_; + + /// @brief Defines the app name used to construct the controller + static const char* stub_app_name_; + + /// @brief Defines the executable name used to construct the controller + static const char* stub_bin_name_; + + /// @brief Gets the list of signals that have been caught and processed. + std::vector<int>& getProcessedSignals() { + return (processed_signals_); + } + + /// @brief Deals with other (i.e. not application name) global objects. + using DControllerBase::handleOtherObjects; + + /// @brief Controls whether signals are processed in full or merely + /// recorded. + /// + /// If true, signal handling will stop after recording the signal. + /// Otherwise the base class signal handler, + /// DControllerBase::processSignals will also be invoked. This switch is + /// useful for ensuring that IOSignals are delivered as expected without + /// incurring the full impact such as reconfiguring or shutting down. + /// + /// @param value boolean which if true enables record-only behavior + void recordSignalOnly(bool value) { + record_signal_only_ = value; + } + + /// @brief Determines if parseFile() implementation is used + /// + /// If true, parseFile() will return a Map of elements with fixed content, + /// mimicking a controller which is using alternate JSON parsing. + /// If false, parseFile() will return an empty pointer mimicking a + /// controller which is using original JSON parsing supplied by the + /// Element class. + /// + /// @param value boolean which if true enables record-only behavior + void useAlternateParser(bool value) { + use_alternate_parser_ = value; + } + +protected: + /// @brief Handles additional command line options that are supported + /// by DStubController. This implementation supports an option "-x". + /// + /// @param option is the option "character" from the command line, without + /// any prefixing hyphen(s) + /// @optarg optarg is the argument value (if one) associated with the option + /// + /// @return returns true if the option is "x", otherwise ti returns false. + virtual bool customOption(int option, char *optarg); + + /// @brief Instantiates an instance of DStubProcess. + /// + /// This implementation will fail if SimFailure is set to + /// ftCreateProcessException OR ftCreateProcessNull. + /// + /// @return returns a pointer to the new process instance (DProcessBase*) + /// or NULL if SimFailure is set to ftCreateProcessNull. + /// @throw throws std::runtime_error if SimFailure is set to + /// ftCreateProcessException. + virtual DProcessBase* createProcess(); + + /// @brief Provides a string of the additional command line options + /// supported by DStubController. DStubController supports one + /// addition option, stub_option_x_. + /// + /// @return returns a string containing the option letters. + virtual const std::string getCustomOpts() const; + + /// @brief Application-level "signal handler" + /// + /// Overrides the base class implementation such that this method + /// is invoked each time an IOSignal is processed. It records the + /// signal received and unless we are in record-only behavior, it + /// in invokes the base class implementation. + /// + /// @param signum OS signal value received + virtual void processSignal(int signum); + + /// @brief Provides alternate parse file implementation + /// + /// Overrides the base class implementation to mimic controllers which + /// implement alternate file parsing. If enabled via useAlternateParser() + /// the method will return a fixed map of elements reflecting the following + /// JSON: + /// + /// @code + /// { "<name>getController()->getAppName()" : + /// { "string_test": "alt value" }; + /// } + /// + /// @endcode + /// + /// where <name> is getController()->getAppName() + /// + /// otherwise it return an empty pointer. + virtual isc::data::ConstElementPtr parseFile(const std::string&); + +public: + + /// @brief Provides additional extended version text + /// + /// Overrides the base class implementation so we can + /// verify the getting the extended version text + /// contains derivation specific contributions. + virtual std::string getVersionAddendum() { + return ("StubController Version Text"); + } + +private: + /// @brief Constructor is private to protect singleton integrity. + DStubController(); + + /// @brief Vector to record the signal values received. + std::vector<int> processed_signals_; + + /// @brief Boolean for controlling if signals are merely recorded. + bool record_signal_only_; + + /// @brief Boolean for controlling if parseFile is "implemented" + bool use_alternate_parser_; + +public: + virtual ~DStubController(); +}; + +/// @brief Defines a pointer to a DStubController. +typedef boost::shared_ptr<DStubController> DStubControllerPtr; + +/// @brief Abstract Test fixture class that wraps a DControllerBase. This class +/// is a friend class of DControllerBase which allows it access to class +/// content to facilitate testing. It provides numerous wrapper methods for +/// the protected and private methods and member of the base class. +class DControllerTest : public ::testing::Test { +public: + + /// @brief Defines a function pointer for controller singleton fetchers. + typedef DControllerBasePtr& (*InstanceGetter)(); + + /// @brief Static storage of the controller class's singleton fetcher. + /// We need this this statically available for callbacks. + static InstanceGetter instanceGetter_; + + /// @brief Constructor + /// + /// @param instance_getter is a function pointer to the static instance + /// method of the DControllerBase derivation under test. + DControllerTest(InstanceGetter instance_getter) + : write_timer_(), new_cfg_content_() { + // Set the static fetcher member, then invoke it via getController. + // This ensures the singleton is instantiated. + instanceGetter_ = instance_getter; + getController(); + } + + /// @brief Destructor + /// Note the controller singleton is destroyed. This is essential to ensure + /// a clean start between tests. + virtual ~DControllerTest() { + // Some unit tests update the logging configuration which has a side + // effect that all subsequent tests print the output to stdout. This + // is to ensure that the logging settings are back to default. + isc::log::setDefaultLoggingOutput(); + + if (write_timer_) { + write_timer_->cancel(); + } + + getController().reset(); + static_cast<void>(remove(CFG_TEST_FILE)); + } + + /// @brief Convenience method that destructs and then recreates the + /// controller singleton under test. This is handy for tests within + /// tests. + void resetController() { + getController().reset(); + getController(); + } + + /// @brief Static method which returns the instance of the controller + /// under test. + /// @return returns a reference to the controller instance. + static DControllerBasePtr& getController() { + return ((*instanceGetter_)()); + } + + /// @brief Returns true if the Controller's app name matches the + /// given value. + /// + /// @param should_be is the value to compare against. + /// + /// @return returns true if the values are equal. + bool checkAppName(const std::string& should_be) { + return (getController()->getAppName().compare(should_be) == 0); + } + + /// @brief Returns true if the Controller's service name matches the + /// given value. + /// + /// @param should_be is the value to compare against. + /// + /// @return returns true if the values are equal. + bool checkBinName(const std::string& should_be) { + return (getController()->getBinName().compare(should_be) == 0); + } + + /// @brief Tests the existence of the Controller's application process. + /// + /// @return returns true if the process instance exists. + bool checkProcess() { + return (getController()->process_.get() != 0); + } + + /// @brief Tests the existence of the Controller's IOService. + /// + /// @return returns true if the IOService exists. + bool checkIOService() { + return (getController()->io_service_.get() != 0); + } + + /// @brief Gets the Controller's IOService. + /// + /// @return returns a reference to the IOService + asiolink::IOServicePtr& getIOService() { + return (getController()->io_service_); + } + + /// @brief Compares verbose flag with the given value. + /// + /// @param value + /// + /// @return returns true if the verbose flag is equal to the given value. + bool checkVerbose(bool value) { + return (getController()->isVerbose() == value); + } + + /// @brief Compares configuration file name with the given value. + /// + /// @param value file name to compare against + /// + /// @return returns true if the verbose flag is equal to the given value. + bool checkConfigFileName(const std::string& value) { + return (getController()->getConfigFile() == value); + } + + /// @Wrapper to invoke the Controller's parseArgs method. Please refer to + /// DControllerBase::parseArgs for details. + void parseArgs(int argc, char* argv[]) { + getController()->parseArgs(argc, argv); + } + + /// @Wrapper to invoke the Controller's init method. Please refer to + /// DControllerBase::init for details. + void initProcess() { + getController()->initProcess(); + } + + /// @Wrapper to invoke the Controller's launch method. Please refer to + /// DControllerBase::launch for details. + void launch(int argc, char* argv[]) { + optind = 1; + getController()->launch(argc, argv, true); + } + + /// @Wrapper to invoke the Controller's updateConfig method. Please + /// refer to DControllerBase::updateConfig for details. + isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr + new_config) { + return (getController()->updateConfig(new_config)); + } + + /// @Wrapper to invoke the Controller's checkConfig method. Please + /// refer to DControllerBase::checkConfig for details. + isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr + new_config) { + return (getController()->checkConfig(new_config)); + } + + /// @brief Callback that will generate shutdown command via the + /// command callback function. + static void genShutdownCallback() { + isc::data::ElementPtr arg_set; + getController()->shutdownHandler(SHUT_DOWN_COMMAND, arg_set); + } + + /// @brief Callback that throws an exception. + static void genFatalErrorCallback() { + isc_throw (DProcessBaseError, "simulated fatal error"); + } + + /// @brief writes specified content to a well known file + /// + /// Writes given JSON content to CFG_TEST_FILE. It will wrap the + /// content within a JSON element whose name is equal to the controller's + /// app name or the given module name if not blank: + /// + /// @code + /// { "<app_name>" : <content> } + /// @endcode + /// + /// suffix the content within a JSON element with the given module + /// name or wrapped by a JSON + /// element . Tests will + /// attempt to read that file. + /// + /// @param content JSON text to be written to file + /// @param module_name content content to be written to file + void writeFile(const std::string& content, + const std::string& module_name = ""); + + /// @brief Method used as timer callback to invoke writeFile. + /// + /// Wraps a call to writeFile passing in new_cfg_content_. This allows + /// the method to be bound as an IntervalTimer callback. + virtual void timedWriteCallback(); + + /// @brief Schedules the given content to overwrite the config file. + /// + /// Creates a one-shot IntervalTimer whose callback will overwrite the + /// configuration with the given content. This allows the configuration + /// file to replaced write_time_ms after DControllerBase::launch() has + /// invoked runProcess(). + /// + /// @param config JSON string containing the desired content for the config + /// file. + /// @param write_time_ms time in milliseconds to delay before writing the + /// file. + void scheduleTimedWrite(const std::string& config, int write_time_ms); + + /// @brief Convenience method for invoking standard, valid launch + /// + /// This method sets up a timed run of the DController::launch. It does + /// the following: + /// - It creates command line argument variables argc/argv + /// - Invokes writeFile to create the config file with the given content + /// - Schedules a shutdown time timer to call DController::executeShutdown + /// after the interval + /// - Records the start time + /// - Invokes DController::launch() with the command line arguments + /// - After launch returns, it calculates the elapsed time and returns it + /// + /// @param config configuration file content to write before calling launch + /// @param run_time_ms maximum amount of time to allow runProcess() to + /// continue. + /// @param[out] elapsed_time the actual time in ms spent in launch(). + void runWithConfig(const std::string& config, int run_time_ms, + time_duration& elapsed_time); + + /// @brief Type of testing callbacks + typedef std::function<void()> TestCallback; + + /// @brief Convenience method for invoking standard, valid launch + /// with a testing callback + /// + /// This method sets up a timed run of the DController::launch. It does + /// the following: + /// - It creates command line argument variables argc/argv + /// - Invokes writeFile to create the config file with the given content + /// - Schedules a shutdown time timer to call DController::executeShutdown + /// after the interval + /// - Records the start time + /// - Invokes DController::launch() with the command line arguments + /// - After launch returns, it calculates the elapsed time and returns it + /// + /// @note the callback is called before the shutdown and MUST NOT throw + /// @param config configuration file content to write before calling launch + /// @param run_time_ms maximum amount of time to allow runProcess() to + /// continue. + /// @param callback testing callback of TestCallback type + /// @param[out] elapsed_time the actual time in ms spent in launch(). + void runWithConfig(const std::string& config, int run_time_ms, + const TestCallback& callback, + time_duration& elapsed_time); + + /// @brief Fetches the controller's process + /// + /// @return A pointer to the process which may be null if it has not yet + /// been instantiated. + DProcessBasePtr getProcess(); + + /// @brief Fetches the process's configuration manager + /// + /// @return A pointer to the manager which may be null if it has not yet + /// been instantiated. + DCfgMgrBasePtr getCfgMgr(); + + /// @brief Fetches the configuration manager's context + /// + /// @return A pointer to the context which may be null if it has not yet + /// been instantiated. + ConfigPtr getContext(); + + /// @brief Timer used for delayed configuration file writing. + asiolink::IntervalTimerPtr write_timer_; + + /// @brief String which contains the content delayed file writing will use. + std::string new_cfg_content_; + + /// @brief Name of a config file used during tests + static const char* CFG_TEST_FILE; +}; + +/// @brief Test Derivation of the ConfigBase class. +/// +/// This class is used to test basic functionality of configuration context. +/// It adds an additional storage container "extra values" to mimic an +/// application extension of configuration storage. This permits testing that +/// both the base class content as well as the application content is +/// correctly copied during cloning. This is vital to configuration backup +/// and rollback during configuration parsing. +class DStubContext : public ConfigBase { +public: + + /// @brief Constructor + DStubContext(); + + /// @brief Destructor + virtual ~DStubContext(); + + /// @brief Creates a clone of a DStubContext. + /// + /// @return returns a pointer to the new clone. + virtual ConfigPtr clone(); + +protected: + /// @brief Copy constructor + DStubContext(const DStubContext& rhs); + +private: + /// @brief Private assignment operator, not implemented. + DStubContext& operator=(const DStubContext& rhs); + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; +}; + +/// @brief Defines a pointer to DStubContext. +typedef boost::shared_ptr<DStubContext> DStubContextPtr; + +/// @brief Test Derivation of the DCfgMgrBase class. +/// +/// This class is used to test basic functionality of configuration management. +/// It supports the following configuration elements: +/// +/// "bool_test" - Boolean element, tests parsing and committing a boolean +/// configuration parameter. +/// "uint32_test" - Uint32 element, tests parsing and committing a uint32_t +/// configuration parameter. +/// "string_test" - String element, tests parsing and committing a string +/// configuration parameter. +/// "extra_test" - "Extra" element, tests parsing and committing an extra +/// configuration parameter. (This is used to demonstrate +/// derivation's addition of storage to configuration context. +/// +/// It also keeps track of the element ids that are parsed in the order they +/// are parsed. This is used to test ordered and non-ordered parsing. +class DStubCfgMgr : public DCfgMgrBase { +public: + /// @brief Constructor + DStubCfgMgr(); + + /// @brief Destructor + virtual ~DStubCfgMgr(); + + /// @brief Pretends to parse the config + /// + /// This method pretends to parse the configuration specified on input + /// and returns a positive answer. The check_only flag is currently ignored. + /// + /// @param config configuration specified + /// @param check_only whether it's real configuration (false) or just + /// configuration check (true) + /// @return always positive answer + /// + isc::data::ConstElementPtr + parse(isc::data::ConstElementPtr config, bool check_only); + + /// @brief Returns a summary of the configuration in the textual format. + /// + /// @return Always an empty string. + virtual std::string getConfigSummary(const uint32_t) { + return (""); + } + + /// @todo + virtual ConfigPtr createNewContext(); +}; + +/// @brief Defines a pointer to DStubCfgMgr. +typedef boost::shared_ptr<DStubCfgMgr> DStubCfgMgrPtr; + +/// @brief Test fixture base class for any fixtures which test parsing. +/// It provides methods for converting JSON strings to configuration element +/// sets and checking parse results +class ConfigParseTest : public ::testing::Test { +public: + + /// @brief Constructor + ConfigParseTest(){ + } + + /// @brief Destructor + ~ConfigParseTest() { + } + + /// @brief Converts a given JSON string into an Element set and stores the + /// result the member variable, config_set_. + /// + /// @param json_text contains the configuration text in JSON format to + /// convert. + /// @return returns AssertionSuccess if there were no parsing errors, + /// AssertionFailure otherwise. + ::testing::AssertionResult fromJSON(const std::string& json_text) { + try { + config_set_ = isc::data::Element::fromJSON(json_text); + } catch (const isc::Exception &ex) { + return (::testing::AssertionFailure(::testing::Message() << + "JSON text failed to parse:" + << ex.what())); + } + + return (::testing::AssertionSuccess()); + } + + /// @brief Compares the status in the parse result stored in member + /// variable answer_ to a given value. + /// + /// @param should_be is an integer against which to compare the status. + /// + /// @return returns AssertionSuccess if there were no parsing errors, + /// AssertionFailure otherwise. + ::testing::AssertionResult checkAnswer(int should_be) { + return (checkAnswer(answer_, should_be)); + } + + /// @brief Compares the status in the given parse result to a given value. + /// + /// @param answer Element set containing an integer response and string + /// comment. + /// @param should_be is an integer against which to compare the status. + /// + /// @return returns AssertionSuccess if there were no parsing errors, + /// AssertionFailure otherwise. + ::testing::AssertionResult checkAnswer(isc::data::ConstElementPtr answer, + int should_be) { + int rcode = 0; + isc::data::ConstElementPtr comment; + comment = isc::config::parseAnswer(rcode, answer); + if (rcode == should_be) { + return (testing::AssertionSuccess()); + } + + return (::testing::AssertionFailure(::testing::Message() << + "checkAnswer rcode:" << rcode + << " comment: " << *comment)); + } + + /// @brief Configuration set being tested. + isc::data::ElementPtr config_set_; + + /// @brief Results of most recent element parsing. + isc::data::ConstElementPtr answer_; +}; + +} // namespace isc::process +} // namespace isc + +#endif |