summaryrefslogtreecommitdiffstats
path: root/src/lib/process
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/process')
-rw-r--r--src/lib/process/Makefile.am100
-rw-r--r--src/lib/process/Makefile.in1122
-rw-r--r--src/lib/process/cb_ctl_base.h373
-rw-r--r--src/lib/process/config_base.cc137
-rw-r--r--src/lib/process/config_base.h181
-rw-r--r--src/lib/process/config_ctl_info.cc130
-rw-r--r--src/lib/process/config_ctl_info.h247
-rw-r--r--src/lib/process/config_ctl_parser.cc66
-rw-r--r--src/lib/process/config_ctl_parser.h35
-rw-r--r--src/lib/process/d_cfg_mgr.cc163
-rw-r--r--src/lib/process/d_cfg_mgr.h252
-rw-r--r--src/lib/process/d_controller.cc845
-rw-r--r--src/lib/process/d_controller.h653
-rw-r--r--src/lib/process/d_log.cc21
-rw-r--r--src/lib/process/d_log.h23
-rw-r--r--src/lib/process/d_process.h220
-rw-r--r--src/lib/process/daemon.cc266
-rw-r--r--src/lib/process/daemon.h296
-rw-r--r--src/lib/process/libprocess.dox195
-rw-r--r--src/lib/process/log_parser.cc163
-rw-r--r--src/lib/process/log_parser.h93
-rw-r--r--src/lib/process/logging_info.cc212
-rw-r--r--src/lib/process/logging_info.h146
-rw-r--r--src/lib/process/process_messages.cc83
-rw-r--r--src/lib/process/process_messages.h45
-rw-r--r--src/lib/process/process_messages.mes156
-rw-r--r--src/lib/process/redact_config.cc97
-rw-r--r--src/lib/process/redact_config.h37
-rw-r--r--src/lib/process/tests/Makefile.am58
-rw-r--r--src/lib/process/tests/Makefile.in1164
-rw-r--r--src/lib/process/tests/cb_ctl_base_unittests.cc718
-rw-r--r--src/lib/process/tests/config_base_unittests.cc230
-rw-r--r--src/lib/process/tests/config_ctl_info_unittests.cc180
-rw-r--r--src/lib/process/tests/config_ctl_parser_unittests.cc102
-rw-r--r--src/lib/process/tests/d_cfg_mgr_unittests.cc375
-rw-r--r--src/lib/process/tests/d_controller_unittests.cc470
-rw-r--r--src/lib/process/tests/daemon_unittest.cc324
-rw-r--r--src/lib/process/tests/log_parser_unittests.cc512
-rw-r--r--src/lib/process/tests/logging_info_unittests.cc208
-rw-r--r--src/lib/process/tests/run_unittests.cc24
-rw-r--r--src/lib/process/testutils/Makefile.am27
-rw-r--r--src/lib/process/testutils/Makefile.in846
-rw-r--r--src/lib/process/testutils/d_test_stubs.cc336
-rw-r--r--src/lib/process/testutils/d_test_stubs.h753
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=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ 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