summaryrefslogtreecommitdiffstats
path: root/src/bin/agent
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/agent')
-rw-r--r--src/bin/agent/Makefile.am126
-rw-r--r--src/bin/agent/Makefile.in1048
-rw-r--r--src/bin/agent/agent.dox128
-rw-r--r--src/bin/agent/agent_hooks.dox69
-rw-r--r--src/bin/agent/agent_lexer.cc4112
-rw-r--r--src/bin/agent/agent_lexer.ll904
-rw-r--r--src/bin/agent/agent_parser.cc2552
-rw-r--r--src/bin/agent/agent_parser.h2534
-rw-r--r--src/bin/agent/agent_parser.yy869
-rw-r--r--src/bin/agent/ca_cfg_mgr.cc216
-rw-r--r--src/bin/agent/ca_cfg_mgr.h316
-rw-r--r--src/bin/agent/ca_command_mgr.cc278
-rw-r--r--src/bin/agent/ca_command_mgr.h118
-rw-r--r--src/bin/agent/ca_controller.cc113
-rw-r--r--src/bin/agent/ca_controller.h85
-rw-r--r--src/bin/agent/ca_log.cc17
-rw-r--r--src/bin/agent/ca_log.h23
-rw-r--r--src/bin/agent/ca_messages.cc47
-rw-r--r--src/bin/agent/ca_messages.h27
-rw-r--r--src/bin/agent/ca_messages.mes61
-rw-r--r--src/bin/agent/ca_process.cc237
-rw-r--r--src/bin/agent/ca_process.h155
-rw-r--r--src/bin/agent/ca_response_creator.cc205
-rw-r--r--src/bin/agent/ca_response_creator.h85
-rw-r--r--src/bin/agent/ca_response_creator_factory.h55
-rw-r--r--src/bin/agent/location.hh306
-rw-r--r--src/bin/agent/main.cc49
-rw-r--r--src/bin/agent/parser_context.cc204
-rw-r--r--src/bin/agent/parser_context.h317
-rw-r--r--src/bin/agent/parser_context_decl.h20
-rw-r--r--src/bin/agent/simple_parser.cc191
-rw-r--r--src/bin/agent/simple_parser.h58
-rw-r--r--src/bin/agent/tests/Makefile.am120
-rw-r--r--src/bin/agent/tests/Makefile.in1324
-rw-r--r--src/bin/agent/tests/basic_auth_library.cc289
-rw-r--r--src/bin/agent/tests/ca_cfg_mgr_unittests.cc703
-rw-r--r--src/bin/agent/tests/ca_command_mgr_unittests.cc425
-rw-r--r--src/bin/agent/tests/ca_controller_unittests.cc836
-rw-r--r--src/bin/agent/tests/ca_process_tests.sh.in174
-rw-r--r--src/bin/agent/tests/ca_process_unittests.cc90
-rw-r--r--src/bin/agent/tests/ca_response_creator_factory_unittests.cc39
-rw-r--r--src/bin/agent/tests/ca_response_creator_unittests.cc465
-rw-r--r--src/bin/agent/tests/ca_unittests.cc27
-rw-r--r--src/bin/agent/tests/callout_library.cc72
-rw-r--r--src/bin/agent/tests/get_config_unittest.cc309
-rw-r--r--src/bin/agent/tests/parser_unittests.cc931
-rw-r--r--src/bin/agent/tests/test_basic_auth_libraries.h.in24
-rw-r--r--src/bin/agent/tests/test_callout_libraries.h.in24
-rw-r--r--src/bin/agent/tests/test_data_files_config.h.in9
-rw-r--r--src/bin/agent/tests/testdata/get_config.json55
-rw-r--r--src/bin/agent/tests/testdata/hiddenp1
-rw-r--r--src/bin/agent/tests/testdata/hiddens1
-rw-r--r--src/bin/agent/tests/testdata/hiddenu1
53 files changed, 21444 insertions, 0 deletions
diff --git a/src/bin/agent/Makefile.am b/src/bin/agent/Makefile.am
new file mode 100644
index 0000000..9d9f342
--- /dev/null
+++ b/src/bin/agent/Makefile.am
@@ -0,0 +1,126 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+EXTRA_DIST = agent.dox agent_hooks.dox
+
+
+# convenience archive
+
+noinst_LTLIBRARIES = libagent.la
+
+libagent_la_SOURCES = agent_parser.cc agent_parser.h
+libagent_la_SOURCES += agent_lexer.cc
+libagent_la_SOURCES += ca_cfg_mgr.cc ca_cfg_mgr.h
+libagent_la_SOURCES += ca_controller.cc ca_controller.h
+libagent_la_SOURCES += ca_command_mgr.cc ca_command_mgr.h
+libagent_la_SOURCES += ca_log.cc ca_log.h
+libagent_la_SOURCES += ca_process.cc ca_process.h
+libagent_la_SOURCES += ca_response_creator.cc ca_response_creator.h
+libagent_la_SOURCES += ca_response_creator_factory.h
+libagent_la_SOURCES += simple_parser.cc simple_parser.h
+libagent_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
+libagent_la_SOURCES += agent_lexer.ll location.hh
+libagent_la_SOURCES += ca_messages.h ca_messages.cc
+EXTRA_DIST += ca_messages.mes
+EXTRA_DIST += agent_lexer.ll
+EXTRA_DIST += agent_parser.yy
+
+sbin_PROGRAMS = kea-ctrl-agent
+
+kea_ctrl_agent_SOURCES = main.cc
+
+kea_ctrl_agent_LDADD = libagent.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/database/libkea-database.la
+kea_ctrl_agent_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS)
+
+kea_ctrl_agent_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS)
+
+# 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 ca_messages.h ca_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: ca_messages.h ca_messages.cc
+ @echo Message files regenerated
+
+ca_messages.h ca_messages.cc: ca_messages.mes
+ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/agent/ca_messages.mes
+
+else
+
+messages ca_messages.h ca_messages.cc:
+ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+endif
+
+if GENERATE_PARSER
+
+# Generate parser first.
+all-recursive: agent_lexer.cc location.hh agent_parser.cc agent_parser.h
+
+parser: agent_lexer.cc location.hh agent_parser.cc agent_parser.h
+ @echo "Flex/bison files regenerated"
+
+# --- Flex/Bison stuff below --------------------------------------------------
+# When debugging grammar issues, it's useful to add -v to bison parameters.
+# bison will generate parser.output file that explains the whole grammar.
+# It can be used to manually follow what's going on in the parser.
+# This is especially useful if yydebug_ is set to 1 as that variable
+# will cause parser to print out its internal state.
+# Call flex with -s to check that the default rule can be suppressed
+# Call bison with -W to get warnings like unmarked empty rules
+# Note C++11 deprecated register still used by flex < 2.6.0
+location.hh agent_parser.cc agent_parser.h: agent_parser.yy
+ $(YACC) -Wno-yacc --defines=agent_parser.h --report=all \
+ --report-file=agent_parser.report -o agent_parser.cc agent_parser.yy
+
+agent_lexer.cc: agent_lexer.ll
+ $(LEX) --prefix agent_ -o agent_lexer.cc agent_lexer.ll
+
+else
+
+parser location.hh agent_parser.cc agent_parser.h agent_lexer.cc:
+ @echo Parser generation disabled. Configure with --enable-generate-parser to enable it.
+
+endif
diff --git a/src/bin/agent/Makefile.in b/src/bin/agent/Makefile.in
new file mode 100644
index 0000000..cfd42ba
--- /dev/null
+++ b/src/bin/agent/Makefile.in
@@ -0,0 +1,1048 @@
+# 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@
+sbin_PROGRAMS = kea-ctrl-agent$(EXEEXT)
+subdir = src/bin/agent
+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 =
+am__installdirs = "$(DESTDIR)$(sbindir)"
+PROGRAMS = $(sbin_PROGRAMS)
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libagent_la_LIBADD =
+am_libagent_la_OBJECTS = agent_parser.lo agent_lexer.lo ca_cfg_mgr.lo \
+ ca_controller.lo ca_command_mgr.lo ca_log.lo ca_process.lo \
+ ca_response_creator.lo simple_parser.lo parser_context.lo \
+ agent_lexer.lo ca_messages.lo
+libagent_la_OBJECTS = $(am_libagent_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 =
+am_kea_ctrl_agent_OBJECTS = main.$(OBJEXT)
+kea_ctrl_agent_OBJECTS = $(am_kea_ctrl_agent_OBJECTS)
+am__DEPENDENCIES_1 =
+kea_ctrl_agent_DEPENDENCIES = libagent.la \
+ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \
+ $(top_builddir)/src/lib/process/libkea-process.la \
+ $(top_builddir)/src/lib/eval/libkea-eval.la \
+ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+ $(top_builddir)/src/lib/stats/libkea-stats.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/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.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 \
+ $(top_builddir)/src/lib/database/libkea-database.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+kea_ctrl_agent_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(AM_CXXFLAGS) $(CXXFLAGS) $(kea_ctrl_agent_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)/agent_lexer.Plo \
+ ./$(DEPDIR)/agent_parser.Plo ./$(DEPDIR)/ca_cfg_mgr.Plo \
+ ./$(DEPDIR)/ca_command_mgr.Plo ./$(DEPDIR)/ca_controller.Plo \
+ ./$(DEPDIR)/ca_log.Plo ./$(DEPDIR)/ca_messages.Plo \
+ ./$(DEPDIR)/ca_process.Plo ./$(DEPDIR)/ca_response_creator.Plo \
+ ./$(DEPDIR)/main.Po ./$(DEPDIR)/parser_context.Plo \
+ ./$(DEPDIR)/simple_parser.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 =
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
+YLWRAP = $(top_srcdir)/ylwrap
+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 = $(libagent_la_SOURCES) $(kea_ctrl_agent_SOURCES)
+DIST_SOURCES = $(libagent_la_SOURCES) $(kea_ctrl_agent_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
+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 \
+ $(top_srcdir)/ylwrap agent_lexer.cc
+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 = . tests
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \
+ -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin \
+ $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+CLEANFILES = *.gcno *.gcda
+EXTRA_DIST = agent.dox agent_hooks.dox ca_messages.mes agent_lexer.ll \
+ agent_parser.yy
+
+# convenience archive
+noinst_LTLIBRARIES = libagent.la
+libagent_la_SOURCES = agent_parser.cc agent_parser.h agent_lexer.cc \
+ ca_cfg_mgr.cc ca_cfg_mgr.h ca_controller.cc ca_controller.h \
+ ca_command_mgr.cc ca_command_mgr.h ca_log.cc ca_log.h \
+ ca_process.cc ca_process.h ca_response_creator.cc \
+ ca_response_creator.h ca_response_creator_factory.h \
+ simple_parser.cc simple_parser.h parser_context.cc \
+ parser_context.h parser_context_decl.h agent_lexer.ll \
+ location.hh ca_messages.h ca_messages.cc
+kea_ctrl_agent_SOURCES = main.cc
+kea_ctrl_agent_LDADD = libagent.la \
+ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \
+ $(top_builddir)/src/lib/process/libkea-process.la \
+ $(top_builddir)/src/lib/eval/libkea-eval.la \
+ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+ $(top_builddir)/src/lib/stats/libkea-stats.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/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.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 \
+ $(top_builddir)/src/lib/database/libkea-database.la \
+ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS)
+kea_ctrl_agent_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .ll .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/bin/agent/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/bin/agent/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-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_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
+
+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}; \
+ }
+
+libagent.la: $(libagent_la_OBJECTS) $(libagent_la_DEPENDENCIES) $(EXTRA_libagent_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(CXXLINK) $(libagent_la_OBJECTS) $(libagent_la_LIBADD) $(LIBS)
+
+kea-ctrl-agent$(EXEEXT): $(kea_ctrl_agent_OBJECTS) $(kea_ctrl_agent_DEPENDENCIES) $(EXTRA_kea_ctrl_agent_DEPENDENCIES)
+ @rm -f kea-ctrl-agent$(EXEEXT)
+ $(AM_V_CXXLD)$(kea_ctrl_agent_LINK) $(kea_ctrl_agent_OBJECTS) $(kea_ctrl_agent_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agent_lexer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agent_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_cfg_mgr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_command_mgr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_controller.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_messages.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_process.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_response_creator.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser_context.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_parser.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 $@ $<
+
+.ll.cc:
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+
+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 $(PROGRAMS) $(LTLIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(sbindir)"; 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)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f agent_lexer.cc
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/agent_lexer.Plo
+ -rm -f ./$(DEPDIR)/agent_parser.Plo
+ -rm -f ./$(DEPDIR)/ca_cfg_mgr.Plo
+ -rm -f ./$(DEPDIR)/ca_command_mgr.Plo
+ -rm -f ./$(DEPDIR)/ca_controller.Plo
+ -rm -f ./$(DEPDIR)/ca_log.Plo
+ -rm -f ./$(DEPDIR)/ca_messages.Plo
+ -rm -f ./$(DEPDIR)/ca_process.Plo
+ -rm -f ./$(DEPDIR)/ca_response_creator.Plo
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/parser_context.Plo
+ -rm -f ./$(DEPDIR)/simple_parser.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-sbinPROGRAMS
+
+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)/agent_lexer.Plo
+ -rm -f ./$(DEPDIR)/agent_parser.Plo
+ -rm -f ./$(DEPDIR)/ca_cfg_mgr.Plo
+ -rm -f ./$(DEPDIR)/ca_command_mgr.Plo
+ -rm -f ./$(DEPDIR)/ca_controller.Plo
+ -rm -f ./$(DEPDIR)/ca_log.Plo
+ -rm -f ./$(DEPDIR)/ca_messages.Plo
+ -rm -f ./$(DEPDIR)/ca_process.Plo
+ -rm -f ./$(DEPDIR)/ca_response_creator.Plo
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/parser_context.Plo
+ -rm -f ./$(DEPDIR)/simple_parser.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-sbinPROGRAMS
+
+.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 clean-sbinPROGRAMS 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-sbinPROGRAMS 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-sbinPROGRAMS
+
+.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 ca_messages.h ca_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: ca_messages.h ca_messages.cc
+@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated
+
+@GENERATE_MESSAGES_TRUE@ca_messages.h ca_messages.cc: ca_messages.mes
+@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/agent/ca_messages.mes
+
+@GENERATE_MESSAGES_FALSE@messages ca_messages.h ca_messages.cc:
+@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+# Generate parser first.
+@GENERATE_PARSER_TRUE@all-recursive: agent_lexer.cc location.hh agent_parser.cc agent_parser.h
+
+@GENERATE_PARSER_TRUE@parser: agent_lexer.cc location.hh agent_parser.cc agent_parser.h
+@GENERATE_PARSER_TRUE@ @echo "Flex/bison files regenerated"
+
+# --- Flex/Bison stuff below --------------------------------------------------
+# When debugging grammar issues, it's useful to add -v to bison parameters.
+# bison will generate parser.output file that explains the whole grammar.
+# It can be used to manually follow what's going on in the parser.
+# This is especially useful if yydebug_ is set to 1 as that variable
+# will cause parser to print out its internal state.
+# Call flex with -s to check that the default rule can be suppressed
+# Call bison with -W to get warnings like unmarked empty rules
+# Note C++11 deprecated register still used by flex < 2.6.0
+@GENERATE_PARSER_TRUE@location.hh agent_parser.cc agent_parser.h: agent_parser.yy
+@GENERATE_PARSER_TRUE@ $(YACC) -Wno-yacc --defines=agent_parser.h --report=all \
+@GENERATE_PARSER_TRUE@ --report-file=agent_parser.report -o agent_parser.cc agent_parser.yy
+
+@GENERATE_PARSER_TRUE@agent_lexer.cc: agent_lexer.ll
+@GENERATE_PARSER_TRUE@ $(LEX) --prefix agent_ -o agent_lexer.cc agent_lexer.ll
+
+@GENERATE_PARSER_FALSE@parser location.hh agent_parser.cc agent_parser.h agent_lexer.cc:
+@GENERATE_PARSER_FALSE@ @echo Parser generation disabled. Configure with --enable-generate-parser 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/bin/agent/agent.dox b/src/bin/agent/agent.dox
new file mode 100644
index 0000000..9e1f69d
--- /dev/null
+++ b/src/bin/agent/agent.dox
@@ -0,0 +1,128 @@
+// Copyright (C) 2017 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 controlAgent Control Agent Component
+
+Kea 1.2 release has introduced the Control Agent component (CA), which
+is started by the "kea-ctrl-agent" binary. The CA exposes a RESTful API
+which is used by the administrators to manage Kea servers' instances.
+
+In the most typical case, the CA forwards commands received over the
+RESTful API to the respective Kea servers, e.g. DHCPv4 server, DHCPv6
+server etc. The communication between the CA and other servers is
+established with the use of unix domain sockets. This is possible because
+the CA is running on the same system as other Kea services to which
+messages are forwarded.
+
+The CA can forward the same command to multiple Kea services and return
+an aggregated response (from all services) over the RESTful API. The
+"service" parameter included in the client's command can contain one
+or more services at which the command is targeted. The CA will
+iterate over this list and forward the command to each of them
+individually.
+
+In some cases, the commands containing the "service" value can be handled
+directly by the CA. This is usually the case when the CA is running
+with hooks libraries attached. The hooks libraries must implement
+callouts for the "control_command_receive" hook point, which will be
+invoked by the CA when the command is received. If the hooks libraries
+set the 'skip' status, it is an indication to the CA that the command
+has been processed by the CA and that it should return the response created
+by the hooks libraries to the client. An example of the hooks library attached
+to the CA and handling the commands for other services is a library which
+stores or retrieves some data from the SQL database.
+
+The "service" parameter is optional. If it is not included in the command
+(or it is an empty list), this indicates that the command relates to the
+CA and that the CA should handle it, e.g. return its own configuration in
+response to a "config-get" command.
+
+@section ctrlAgentHttp Receiving commands over HTTP
+
+Control Agent uses libkea-http library to establish HTTP connections,
+receive messages and send responses over HTTP. This library uses boost ASIO
+for creating TCP connections and asynchronously receive and send the data
+over the sockets.
+
+The @ref isc::http::HttpListener provides an entry point to this library.
+It is used by the CA to bind the acceptor to the specific address and
+port. When the client connects to this address and port, the acceptor's
+callback function is invoked which opens a new connection and starts
+receiving data over that socket. The @ref isc::http::HttpConnection
+implements the logic to read and parse received data. Each new TCP
+connection is associated with unique instance of the @ref isc::http::HttpConnection
+When a portion of data is received (asynchronously) over
+the socket it is provided to the instance of the
+@ref isc::http::HttpRequestParser object (unique per connection) and
+data parsing is continued until the parser runs out of data or until
+the entire HTTP request has been received. The
+@ref isc::http::HttpRequestParser signals these events using the
+@ref isc::http::HttpRequestParser::needData and
+@ref isc::http::HttpRequestParser::httpParseOk respectively.
+
+libkea-http is designed to handle processing messages carrying different
+content types. The Control Agent uses "application/json" content
+type which describes messages with JSON structures carried within the
+message body. The JSON structures represent commands sent to the Kea
+server(s) by controlling clients. libkea-http provides generic classes
+(derived from @ref isc::http::HttpRequest) which facilitate validation of
+messages holding various content types.
+CA uses @ref isc::http::PostHttpRequestJson, which encapsulate messages
+sent using HTTP POST and including JSON content, to represent received messages.
+
+@section ctrlAgentCreatingResponse Creating HTTP responses
+
+The @ref isc::http::HttpResponseCreatorFactory is an interface which should
+be implemented by components using libkea-http to generate instances of
+the HTTP responses of a desired type. The instance of the factory class is
+provided to the @ref isc::http::HttpListener via its constructor. The listener
+calls an implementation of the
+@ref isc::http::HttpResponseCreatorFactory::create when a new HTTP
+message has been received and parsed.
+
+The CA component includes the @ref isc::agent::CtrlAgentResponseCreatorFactory
+class. Its @c create() method implementation returns
+an instance of the @ref isc::agent::CtrlAgentResponseCreator, which is a
+derivation of the @ref isc::http::HttpResponseCreator. This creator creates
+instances of the @ref isc::http::HttpResponseJson, holding responses to
+the commands in the JSON format.
+
+@section ctrlAgentCommandMgr Handling commands with Command Manager
+
+The @ref isc::agent::CtrlAgentCommandMgr is a derivation of the
+@ref isc::config::HookedCommandMgr which adds the capability to forward
+commands received over HTTP to specific Kea servers. The
+@ref isc::agent::CtrlAgentCommandMgr forwards commands over a Unix domain
+socket, using @ref isc::asiolink::UnixDomainSocket class. All responses
+to a particular command (possibly received from multiple Kea servers) are
+aggregated within a JSON list and sent back to the controlling client over
+HTTP.
+
+In some cases the responses may be generated locally (without forwarding).
+Typically, the command will be generated by the CA when the command sent
+by the client lacks the "service" parameter, which indicates that the
+command is targeted at the CA itself. In some cases the commands can also
+be processed by the hooks libraries attached to the CA.
+
+@section CtrlAgentSecurity Security considerations
+
+The Control Agent doesn't provide any mechanisms to secure the communication
+over the RESTful API. In the design of the CA we have considered including built-in
+HTTPS solutions (HTTP + TLS), making use of crypto libraries supported by Kea.
+It was eventually decided to not implement the secure layer within Kea for the following reasons:
+- additional code complexity which requires maintenance, bug fixing and
+ monitoring for security vulnerabilities in the OpenSSL/Botan code,
+- OpenSSL/Botan code may be awkward to use and it is likely we wouldn't
+ implement it right,
+- need to support two crypto backends: OpenSSL and Botan which puts significant
+ burden on Kea maintenance.
+
+In the installations where securing command channel is critical (most of the
+installations?), a reverse HTTP proxy can be setup using one of the third
+party HTTP server implementations, e.g. Apache, nginx etc.
+
+*/
diff --git a/src/bin/agent/agent_hooks.dox b/src/bin/agent/agent_hooks.dox
new file mode 100644
index 0000000..be2c833
--- /dev/null
+++ b/src/bin/agent/agent_hooks.dox
@@ -0,0 +1,69 @@
+// Copyright (C) 2017-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/.
+
+/**
+@page agentHooks The Hooks API for the Control Agent
+
+@section agentHooksIntroduction Introduction
+The Kea Control Agent features "Hooks" API that allows the user-written
+code to be integrated with the Control Agent and handle some
+of the control commands. The hooks library can be either used to provide
+support for the new commands (not supported natively by the Control Agent)
+or "override" implementation of the existing handlers. The hooks library
+signals to the Control Agent that it has processed the given command by
+setting "next step status" value to SKIP.
+
+The hooks library can also be used to perform some additional tasks
+related to reception of the control command instead of handling it, e.g.
+logging or notifying some external service about reception of the
+command.
+
+@section agentHooksHookPoints Hooks in the Control Agent
+
+ @subsection agentHooksAuth auth
+
+ - @b Arguments:
+ - name: @b request, type: isc::http::HttpRequestPtr, direction: <b>in/out</b>
+ - name: @b response, type: isc::http::HttpResponseJsonPtr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when Control Agent receives a
+ control command over the RESTful interface (HTTP).
+ The "request" argument is a pointer to the request, in fact a
+ PostHttpRequestJsonPtr. The "response" argument is the response in case
+ of errors. The purpose of this callout is to implement authentication
+ and authorization. It is called after basic HTTP authentication.
+ The next step status is used only to ask to reset the handle: if the
+ response is set the processing will stop and the response is returned.
+ In particular the command is not forwarded.
+
+ @subsection agentHooksResponse response
+
+ - @b Arguments:
+ - name: @b request, type: isc::http::HttpRequestPtr, direction: <b>in</b>
+ - name: @b response, type: isc::http::HttpResponseJsonPtr, direction: <b>in/out</b>
+
+ - @b Description: this callout is executed when Control Agent executed
+ a control command over the RESTful interface (HTTP).
+ The "request" argument is a pointer to the request. It is used as a
+ reference and for callout contexts. The "response" argument is the
+ response which will be sent back to the requesting client. It is
+ called after command processing. The next step status is ignored:
+ the response possibly modified will be sent back.
+
+@section agentHooksHandle Handle and hook unload
+
+The callout handle attached to the "request" argument can keep a pointer
+to the hook address space which prevents the hook to be unloaded
+when the "config-get" or "config-reload" command is executed.
+
+The "next step status" of the "auth" callout point can be set to any
+value other than CONTINUE to ask the callout handle to be reset. This
+must be done when the command is "config-get" or "config-reload" or
+when the "response" callout point is not used or when the callout
+context does not transmit values between the "auth" and "response"
+callout points.
+
+*/
diff --git a/src/bin/agent/agent_lexer.cc b/src/bin/agent/agent_lexer.cc
new file mode 100644
index 0000000..6c83046
--- /dev/null
+++ b/src/bin/agent/agent_lexer.cc
@@ -0,0 +1,4112 @@
+#line 1 "agent_lexer.cc"
+
+#line 3 "agent_lexer.cc"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+/* %not-for-header */
+/* %if-c-only */
+/* %if-not-reentrant */
+#define yy_create_buffer agent__create_buffer
+#define yy_delete_buffer agent__delete_buffer
+#define yy_scan_buffer agent__scan_buffer
+#define yy_scan_string agent__scan_string
+#define yy_scan_bytes agent__scan_bytes
+#define yy_init_buffer agent__init_buffer
+#define yy_flush_buffer agent__flush_buffer
+#define yy_load_buffer_state agent__load_buffer_state
+#define yy_switch_to_buffer agent__switch_to_buffer
+#define yypush_buffer_state agent_push_buffer_state
+#define yypop_buffer_state agent_pop_buffer_state
+#define yyensure_buffer_stack agent_ensure_buffer_stack
+#define yy_flex_debug agent__flex_debug
+#define yyin agent_in
+#define yyleng agent_leng
+#define yylex agent_lex
+#define yylineno agent_lineno
+#define yyout agent_out
+#define yyrestart agent_restart
+#define yytext agent_text
+#define yywrap agent_wrap
+#define yyalloc agent_alloc
+#define yyrealloc agent_realloc
+#define yyfree agent_free
+
+/* %endif */
+/* %endif */
+/* %ok-for-header */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* %if-c++-only */
+/* %endif */
+
+/* %if-c-only */
+#ifdef yy_create_buffer
+#define agent__create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer agent__create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define agent__delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer agent__delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define agent__scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer agent__scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define agent__scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string agent__scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define agent__scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes agent__scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define agent__init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer agent__init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define agent__flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer agent__flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define agent__load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state agent__load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define agent__switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer agent__switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define agent_push_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state agent_push_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define agent_pop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state agent_pop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define agent_ensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack agent_ensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define agent_lex_ALREADY_DEFINED
+#else
+#define yylex agent_lex
+#endif
+
+#ifdef yyrestart
+#define agent_restart_ALREADY_DEFINED
+#else
+#define yyrestart agent_restart
+#endif
+
+#ifdef yylex_init
+#define agent_lex_init_ALREADY_DEFINED
+#else
+#define yylex_init agent_lex_init
+#endif
+
+#ifdef yylex_init_extra
+#define agent_lex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra agent_lex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define agent_lex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy agent_lex_destroy
+#endif
+
+#ifdef yyget_debug
+#define agent_get_debug_ALREADY_DEFINED
+#else
+#define yyget_debug agent_get_debug
+#endif
+
+#ifdef yyset_debug
+#define agent_set_debug_ALREADY_DEFINED
+#else
+#define yyset_debug agent_set_debug
+#endif
+
+#ifdef yyget_extra
+#define agent_get_extra_ALREADY_DEFINED
+#else
+#define yyget_extra agent_get_extra
+#endif
+
+#ifdef yyset_extra
+#define agent_set_extra_ALREADY_DEFINED
+#else
+#define yyset_extra agent_set_extra
+#endif
+
+#ifdef yyget_in
+#define agent_get_in_ALREADY_DEFINED
+#else
+#define yyget_in agent_get_in
+#endif
+
+#ifdef yyset_in
+#define agent_set_in_ALREADY_DEFINED
+#else
+#define yyset_in agent_set_in
+#endif
+
+#ifdef yyget_out
+#define agent_get_out_ALREADY_DEFINED
+#else
+#define yyget_out agent_get_out
+#endif
+
+#ifdef yyset_out
+#define agent_set_out_ALREADY_DEFINED
+#else
+#define yyset_out agent_set_out
+#endif
+
+#ifdef yyget_leng
+#define agent_get_leng_ALREADY_DEFINED
+#else
+#define yyget_leng agent_get_leng
+#endif
+
+#ifdef yyget_text
+#define agent_get_text_ALREADY_DEFINED
+#else
+#define yyget_text agent_get_text
+#endif
+
+#ifdef yyget_lineno
+#define agent_get_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno agent_get_lineno
+#endif
+
+#ifdef yyset_lineno
+#define agent_set_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno agent_set_lineno
+#endif
+
+#ifdef yywrap
+#define agent_wrap_ALREADY_DEFINED
+#else
+#define yywrap agent_wrap
+#endif
+
+/* %endif */
+
+#ifdef yyalloc
+#define agent_alloc_ALREADY_DEFINED
+#else
+#define yyalloc agent_alloc
+#endif
+
+#ifdef yyrealloc
+#define agent_realloc_ALREADY_DEFINED
+#else
+#define yyrealloc agent_realloc
+#endif
+
+#ifdef yyfree
+#define agent_free_ALREADY_DEFINED
+#else
+#define yyfree agent_free
+#endif
+
+/* %if-c-only */
+
+#ifdef yytext
+#define agent_text_ALREADY_DEFINED
+#else
+#define yytext agent_text
+#endif
+
+#ifdef yyleng
+#define agent_leng_ALREADY_DEFINED
+#else
+#define yyleng agent_leng
+#endif
+
+#ifdef yyin
+#define agent_in_ALREADY_DEFINED
+#else
+#define yyin agent_in
+#endif
+
+#ifdef yyout
+#define agent_out_ALREADY_DEFINED
+#else
+#define yyout agent_out
+#endif
+
+#ifdef yy_flex_debug
+#define agent__flex_debug_ALREADY_DEFINED
+#else
+#define yy_flex_debug agent__flex_debug
+#endif
+
+#ifdef yylineno
+#define agent_lineno_ALREADY_DEFINED
+#else
+#define yylineno agent_lineno
+#endif
+
+/* %endif */
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+/* %if-c-only */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+/* %endif */
+
+/* %if-tables-serialization */
+/* %endif */
+/* end standard C headers. */
+
+/* %if-c-or-c++ */
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* %endif */
+
+/* begin standard C++ headers. */
+/* %if-c++-only */
+/* %endif */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* %not-for-header */
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+/* %ok-for-header */
+
+/* %not-for-header */
+/* Promotes a possibly negative, possibly signed char to an
+ * integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+/* %ok-for-header */
+
+/* %if-reentrant */
+/* %endif */
+
+/* %if-not-reentrant */
+
+/* %endif */
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+/* %if-not-reentrant */
+extern int yyleng;
+/* %endif */
+
+/* %if-c-only */
+/* %if-not-reentrant */
+extern FILE *yyin, *yyout;
+/* %endif */
+/* %endif */
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+ #define YY_LINENO_REWIND_TO(ptr)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+/* %if-c-only */
+ FILE *yy_input_file;
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+/* %if-not-reentrant */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */
+/* %endif */
+/* %ok-for-header */
+
+/* %endif */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* %if-c-only Standard (non-C++) definition */
+
+/* %if-not-reentrant */
+/* %not-for-header */
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = NULL;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+/* %ok-for-header */
+
+/* %endif */
+
+void yyrestart ( FILE *input_file );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size );
+void yy_delete_buffer ( YY_BUFFER_STATE b );
+void yy_flush_buffer ( YY_BUFFER_STATE b );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state ( void );
+
+static void yyensure_buffer_stack ( void );
+static void yy_load_buffer_state ( void );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len );
+
+/* %endif */
+
+void *yyalloc ( yy_size_t );
+void *yyrealloc ( void *, yy_size_t );
+void yyfree ( void * );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
+/* Begin user sect3 */
+
+#define agent_wrap() (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+#define FLEX_DEBUG
+typedef flex_uint8_t YY_CHAR;
+
+FILE *yyin = NULL, *yyout = NULL;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+int yylineno = 1;
+
+extern char *yytext;
+#ifdef yytext_ptr
+#undef yytext_ptr
+#endif
+#define yytext_ptr yytext
+
+/* %% [1.5] DFA */
+
+/* %if-c-only Standard (non-C++) definition */
+
+static yy_state_type yy_get_previous_state ( void );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state );
+static int yy_get_next_buffer ( void );
+static void yynoreturn yy_fatal_error ( const char* msg );
+
+/* %endif */
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+/* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
+ yyleng = (int) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+/* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
+ (yy_c_buf_p) = yy_cp;
+/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
+#define YY_NUM_RULES 70
+#define YY_END_OF_BUFFER 71
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static const flex_int16_t yy_accept[408] =
+ { 0,
+ 63, 63, 0, 0, 0, 0, 0, 0, 0, 0,
+ 71, 69, 10, 11, 69, 1, 63, 60, 63, 63,
+ 69, 62, 61, 69, 69, 69, 69, 69, 56, 57,
+ 69, 69, 69, 58, 59, 5, 5, 5, 69, 69,
+ 69, 10, 11, 0, 0, 51, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 63, 63, 0, 62, 63,
+ 3, 2, 6, 0, 63, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 9, 0, 52, 0, 0, 0,
+ 54, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 8, 0, 0, 53,
+ 55, 0, 0, 0, 0, 0, 0, 0, 34, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 68, 66, 0, 65, 64, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 67,
+
+ 64, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 42, 0, 0, 0, 0, 0, 0, 0,
+ 0, 18, 37, 23, 0, 0, 0, 0, 19, 0,
+ 0, 0, 0, 0, 0, 32, 33, 0, 45, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 20, 0, 0, 0, 0, 0, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 47, 44, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 22,
+
+ 16, 0, 0, 0, 0, 0, 0, 0, 39, 41,
+ 46, 0, 0, 0, 48, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 29, 0, 0, 25, 0, 50, 0, 0, 0,
+ 0, 0, 0, 0, 28, 0, 0, 0, 21, 0,
+ 13, 14, 0, 0, 0, 0, 0, 0, 0, 24,
+ 0, 0, 0, 0, 49, 0, 0, 40, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 36, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 27, 15, 12, 0, 30, 0, 0, 0, 26,
+
+ 17, 0, 0, 43, 31, 38, 0
+ } ;
+
+static const YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 5, 6, 7, 5, 5, 5, 5, 5,
+ 5, 8, 9, 10, 11, 12, 13, 14, 14, 15,
+ 14, 16, 14, 17, 14, 14, 14, 18, 5, 19,
+ 5, 20, 21, 5, 22, 23, 24, 23, 25, 26,
+ 5, 5, 5, 5, 5, 27, 5, 28, 5, 5,
+ 5, 29, 30, 31, 32, 5, 5, 5, 5, 5,
+ 33, 34, 35, 5, 36, 5, 37, 38, 39, 40,
+
+ 41, 42, 43, 44, 45, 5, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 5, 63, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5
+ } ;
+
+static const YY_CHAR yy_meta[64] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1
+ } ;
+
+static const flex_int16_t yy_base[416] =
+ { 0,
+ 0, 0, 62, 65, 68, 0, 66, 70, 50, 67,
+ 321, 2720, 87, 317, 131, 0, 104, 2720, 110, 125,
+ 84, 142, 2720, 296, 91, 106, 58, 107, 2720, 2720,
+ 116, 116, 123, 2720, 2720, 2720, 142, 283, 246, 0,
+ 266, 145, 234, 155, 189, 2720, 195, 193, 202, 210,
+ 216, 237, 253, 259, 267, 273, 282, 288, 296, 309,
+ 325, 331, 339, 348, 0, 346, 363, 392, 398, 402,
+ 2720, 0, 2720, 289, 341, 147, 173, 132, 183, 187,
+ 170, 2720, 193, 222, 2720, 187, 2720, 392, 388, 218,
+ 430, 459, 452, 460, 486, 502, 509, 515, 521, 527,
+
+ 539, 550, 561, 568, 574, 580, 587, 595, 614, 625,
+ 634, 640, 649, 663, 669, 689, 698, 707, 0, 179,
+ 245, 200, 223, 297, 226, 168, 2720, 713, 201, 2720,
+ 2720, 755, 732, 753, 782, 798, 804, 811, 2720, 817,
+ 823, 841, 847, 853, 859, 866, 877, 883, 889, 901,
+ 913, 919, 926, 935, 948, 960, 969, 975, 982, 990,
+ 1004, 239, 2720, 2720, 288, 2720, 2720, 107, 1011, 1051,
+ 996, 1019, 1025, 1038, 1045, 1079, 1093, 1099, 1105, 1116,
+ 1122, 1140, 1128, 1146, 1152, 1162, 1176, 1186, 1196, 1202,
+ 1210, 1220, 1226, 1232, 1239, 1245, 1256, 1269, 1275, 2720,
+
+ 2720, 122, 1281, 1318, 1290, 1296, 1311, 1317, 1346, 1304,
+ 1360, 1367, 1375, 1381, 1387, 1397, 1410, 1417, 1423, 1431,
+ 1437, 1444, 2720, 1457, 1466, 1479, 1486, 1493, 1500, 1508,
+ 1515, 2720, 2720, 2720, 1522, 63, 1534, 1545, 2720, 1551,
+ 1564, 1570, 1581, 1587, 1605, 2720, 2720, 1611, 2720, 1617,
+ 1624, 1641, 1647, 1653, 1664, 1670, 1676, 1683, 1694, 1700,
+ 1706, 2720, 1720, 1730, 1736, 1742, 1755, 2720, 1776, 1782,
+ 1765, 1792, 1800, 1812, 1818, 1829, 1835, 1847, 1853, 1859,
+ 1865, 1871, 1877, 1888, 2720, 2720, 1894, 1907, 1913, 1923,
+ 1929, 1936, 1948, 1958, 1965, 1971, 1977, 1992, 2000, 2720,
+
+ 2720, 2012, 2018, 2026, 2034, 2041, 2047, 2061, 2720, 2720,
+ 2720, 2076, 2082, 2088, 2720, 2102, 2111, 2117, 2123, 2131,
+ 2137, 2153, 2166, 2173, 2179, 2187, 2196, 2208, 2214, 2225,
+ 2232, 2720, 2238, 2244, 2720, 2250, 2720, 2262, 2268, 2274,
+ 2280, 2288, 2298, 2310, 2720, 2322, 2328, 2334, 2720, 2340,
+ 2720, 2720, 2346, 2352, 2358, 2370, 2381, 2387, 2393, 2720,
+ 2399, 2406, 2422, 2428, 2720, 2441, 2452, 2720, 2458, 2464,
+ 2470, 2476, 2482, 2494, 2505, 2511, 2518, 2524, 2530, 2540,
+ 2720, 2720, 2559, 2565, 2574, 2581, 2588, 2594, 2600, 2608,
+ 2617, 2720, 2720, 2720, 2623, 2720, 2630, 2637, 2644, 2720,
+
+ 2720, 2652, 2664, 2720, 2720, 2720, 2720, 2698, 2701, 2704,
+ 97, 2707, 2710, 2713, 2716
+ } ;
+
+static const flex_int16_t yy_def[416] =
+ { 0,
+ 407, 1, 408, 408, 1, 5, 5, 5, 5, 5,
+ 407, 407, 407, 407, 409, 410, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 411,
+ 407, 407, 407, 412, 409, 407, 409, 413, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 410, 407, 407, 407, 407, 407,
+ 407, 414, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 411, 407, 412, 407, 407, 409, 415,
+ 409, 413, 409, 409, 409, 409, 409, 409, 409, 409,
+
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 414, 407,
+ 407, 407, 407, 407, 407, 407, 407, 409, 415, 407,
+ 407, 92, 409, 409, 409, 409, 409, 409, 407, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 407, 407, 407, 407, 407, 407, 407, 409, 92,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 407,
+
+ 407, 407, 409, 92, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 407, 409, 409, 409, 409, 409, 409, 409,
+ 409, 407, 407, 407, 409, 407, 409, 409, 407, 409,
+ 409, 409, 409, 409, 409, 407, 407, 409, 407, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 407, 409, 409, 409, 409, 409, 407, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 407, 407, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 407,
+
+ 407, 409, 409, 409, 409, 409, 409, 409, 407, 407,
+ 407, 409, 409, 409, 407, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 407, 409, 409, 407, 409, 407, 409, 409, 409,
+ 409, 409, 409, 409, 407, 409, 409, 409, 407, 409,
+ 407, 407, 409, 409, 409, 409, 409, 409, 409, 407,
+ 409, 409, 409, 409, 407, 409, 409, 407, 409, 409,
+ 409, 409, 409, 409, 409, 409, 409, 409, 409, 409,
+ 407, 407, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 407, 407, 407, 409, 407, 409, 409, 409, 407,
+
+ 407, 409, 409, 407, 407, 407, 0, 407, 407, 407,
+ 407, 407, 407, 407, 407
+ } ;
+
+static const flex_int16_t yy_nxt[2784] =
+ { 0,
+ 12, 13, 14, 13, 12, 15, 16, 12, 17, 18,
+ 19, 20, 21, 22, 22, 22, 22, 23, 24, 12,
+ 12, 12, 12, 12, 25, 26, 12, 27, 12, 12,
+ 28, 12, 29, 12, 30, 12, 12, 12, 12, 12,
+ 25, 31, 12, 12, 12, 12, 12, 12, 32, 12,
+ 12, 12, 12, 12, 33, 12, 12, 12, 12, 12,
+ 12, 34, 35, 37, 14, 37, 37, 14, 37, 38,
+ 41, 40, 38, 12, 12, 40, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 41, 42, 77,
+ 42, 71, 12, 12, 12, 12, 72, 84, 12, 74,
+
+ 12, 74, 12, 268, 75, 75, 75, 75, 12, 12,
+ 12, 12, 39, 77, 12, 66, 12, 67, 67, 67,
+ 67, 66, 12, 69, 69, 69, 69, 76, 68, 12,
+ 12, 44, 44, 44, 68, 78, 46, 76, 70, 70,
+ 70, 70, 76, 42, 68, 42, 42, 77, 42, 68,
+ 68, 78, 79, 66, 47, 69, 69, 69, 69, 78,
+ 87, 236, 202, 122, 48, 68, 68, 49, 50, 51,
+ 52, 80, 53, 120, 54, 81, 55, 56, 57, 58,
+ 59, 60, 68, 61, 62, 63, 64, 122, 88, 44,
+ 44, 44, 87, 120, 46, 44, 44, 44, 91, 121,
+
+ 46, 122, 44, 44, 44, 45, 130, 46, 162, 120,
+ 44, 44, 44, 121, 168, 46, 44, 44, 44, 121,
+ 88, 46, 48, 130, 164, 125, 45, 127, 48, 123,
+ 45, 126, 162, 124, 45, 48, 43, 44, 44, 44,
+ 164, 45, 46, 48, 89, 45, 94, 45, 92, 48,
+ 164, 98, 162, 44, 44, 44, 95, 93, 46, 44,
+ 44, 44, 96, 200, 46, 97, 167, 44, 44, 44,
+ 48, 163, 46, 44, 44, 44, 165, 99, 46, 200,
+ 100, 101, 44, 44, 44, 85, 48, 46, 44, 44,
+ 44, 163, 48, 46, 83, 82, 44, 44, 44, 102,
+
+ 48, 46, 75, 75, 75, 75, 48, 105, 103, 44,
+ 44, 44, 200, 104, 46, 48, 73, 106, 108, 43,
+ 407, 48, 107, 163, 109, 44, 44, 44, 201, 48,
+ 46, 44, 44, 44, 407, 407, 46, 407, 407, 44,
+ 44, 44, 48, 166, 46, 111, 407, 407, 44, 44,
+ 44, 110, 407, 46, 75, 75, 75, 75, 48, 70,
+ 70, 70, 70, 407, 48, 112, 407, 407, 407, 407,
+ 68, 113, 48, 407, 66, 407, 67, 67, 67, 67,
+ 114, 48, 407, 407, 407, 407, 68, 68, 44, 44,
+ 44, 115, 407, 46, 407, 407, 117, 86, 116, 407,
+
+ 74, 118, 74, 68, 86, 75, 75, 75, 75, 66,
+ 407, 69, 69, 69, 69, 70, 70, 70, 70, 407,
+ 407, 48, 68, 407, 407, 86, 68, 407, 407, 86,
+ 44, 44, 44, 86, 407, 46, 128, 407, 68, 407,
+ 86, 407, 68, 407, 86, 407, 86, 86, 407, 407,
+ 407, 407, 44, 44, 44, 407, 407, 46, 407, 407,
+ 44, 44, 44, 48, 131, 46, 407, 407, 407, 407,
+ 407, 407, 132, 132, 132, 132, 407, 407, 407, 407,
+ 132, 132, 132, 132, 132, 48, 44, 44, 44, 407,
+ 407, 46, 407, 48, 407, 132, 132, 132, 132, 132,
+
+ 132, 407, 44, 44, 44, 407, 133, 46, 407, 44,
+ 44, 44, 407, 134, 46, 44, 44, 44, 407, 48,
+ 139, 44, 44, 44, 407, 407, 46, 44, 44, 44,
+ 407, 407, 46, 407, 407, 48, 407, 407, 135, 44,
+ 44, 44, 48, 407, 46, 407, 136, 407, 48, 407,
+ 44, 44, 44, 407, 48, 46, 137, 138, 140, 407,
+ 48, 44, 44, 44, 407, 141, 46, 407, 44, 44,
+ 44, 407, 48, 46, 44, 44, 44, 407, 407, 46,
+ 44, 44, 44, 48, 407, 46, 407, 44, 44, 44,
+ 407, 142, 46, 407, 48, 44, 44, 44, 407, 407,
+
+ 46, 48, 407, 407, 407, 143, 407, 48, 407, 407,
+ 144, 407, 407, 48, 44, 44, 44, 147, 407, 46,
+ 48, 407, 145, 407, 407, 44, 44, 44, 48, 148,
+ 46, 407, 407, 146, 44, 44, 44, 407, 407, 46,
+ 44, 44, 44, 407, 407, 46, 407, 48, 407, 44,
+ 44, 44, 407, 149, 46, 407, 407, 407, 48, 407,
+ 407, 150, 407, 44, 44, 44, 407, 48, 46, 44,
+ 44, 44, 407, 48, 46, 407, 155, 407, 407, 151,
+ 407, 407, 48, 407, 407, 407, 152, 153, 154, 44,
+ 44, 44, 407, 407, 46, 407, 48, 407, 44, 44,
+
+ 44, 157, 48, 46, 407, 156, 407, 44, 44, 44,
+ 407, 407, 46, 44, 44, 44, 407, 407, 46, 407,
+ 407, 407, 48, 407, 158, 407, 407, 407, 407, 407,
+ 407, 48, 44, 44, 44, 407, 407, 46, 407, 159,
+ 48, 407, 160, 407, 407, 407, 48, 161, 407, 407,
+ 407, 407, 407, 44, 44, 44, 407, 407, 46, 407,
+ 407, 407, 407, 407, 407, 48, 407, 169, 170, 170,
+ 170, 170, 407, 407, 407, 171, 170, 170, 170, 170,
+ 170, 407, 44, 44, 44, 407, 48, 46, 407, 407,
+ 407, 170, 170, 170, 170, 170, 170, 172, 44, 44,
+
+ 44, 407, 407, 46, 44, 44, 44, 407, 407, 46,
+ 407, 44, 44, 44, 407, 48, 46, 44, 44, 44,
+ 407, 407, 46, 44, 44, 44, 407, 407, 46, 407,
+ 407, 48, 407, 407, 407, 407, 173, 48, 174, 407,
+ 407, 44, 44, 44, 48, 407, 46, 44, 44, 44,
+ 48, 175, 46, 44, 44, 44, 48, 407, 46, 44,
+ 44, 44, 407, 407, 46, 176, 44, 44, 44, 407,
+ 407, 46, 177, 178, 48, 407, 183, 44, 44, 44,
+ 48, 179, 46, 44, 44, 44, 48, 407, 46, 44,
+ 44, 44, 48, 407, 46, 407, 407, 407, 181, 48,
+
+ 180, 44, 44, 44, 407, 407, 46, 407, 407, 182,
+ 48, 407, 407, 44, 44, 44, 48, 407, 46, 44,
+ 44, 44, 48, 407, 46, 185, 44, 44, 44, 184,
+ 407, 46, 407, 407, 48, 44, 44, 44, 407, 407,
+ 46, 188, 186, 407, 407, 187, 48, 407, 44, 44,
+ 44, 407, 48, 46, 407, 190, 407, 407, 407, 48,
+ 44, 44, 44, 189, 407, 46, 407, 407, 48, 44,
+ 44, 44, 407, 407, 46, 44, 44, 44, 407, 191,
+ 46, 48, 44, 44, 44, 407, 407, 46, 407, 192,
+ 44, 44, 44, 48, 193, 46, 44, 44, 44, 407,
+
+ 194, 46, 48, 407, 44, 44, 44, 407, 48, 46,
+ 407, 44, 44, 44, 195, 48, 46, 407, 407, 44,
+ 44, 44, 197, 48, 46, 44, 44, 44, 196, 48,
+ 46, 407, 407, 407, 407, 207, 205, 48, 44, 44,
+ 44, 407, 407, 46, 48, 44, 44, 44, 198, 407,
+ 46, 407, 48, 407, 407, 407, 199, 206, 48, 407,
+ 407, 407, 407, 203, 204, 204, 204, 204, 407, 407,
+ 407, 48, 204, 204, 204, 204, 204, 407, 48, 44,
+ 44, 44, 407, 407, 46, 209, 208, 204, 204, 204,
+ 204, 204, 204, 44, 44, 44, 407, 407, 46, 44,
+
+ 44, 44, 407, 407, 46, 44, 44, 44, 407, 407,
+ 46, 407, 48, 407, 212, 213, 44, 44, 44, 407,
+ 407, 46, 44, 44, 44, 407, 48, 46, 44, 44,
+ 44, 210, 48, 46, 407, 211, 407, 407, 48, 407,
+ 44, 44, 44, 214, 407, 46, 44, 44, 44, 48,
+ 217, 46, 44, 44, 44, 48, 407, 46, 407, 215,
+ 407, 48, 44, 44, 44, 407, 407, 46, 407, 218,
+ 407, 407, 407, 48, 407, 216, 44, 44, 44, 48,
+ 407, 46, 219, 407, 407, 48, 44, 44, 44, 407,
+ 407, 223, 220, 407, 407, 48, 44, 44, 44, 407,
+
+ 407, 46, 44, 44, 44, 407, 221, 46, 407, 48,
+ 44, 44, 44, 407, 407, 46, 222, 407, 407, 48,
+ 44, 44, 44, 407, 407, 46, 44, 44, 44, 48,
+ 407, 46, 44, 44, 44, 48, 407, 46, 407, 44,
+ 44, 44, 407, 48, 46, 44, 44, 44, 407, 225,
+ 46, 224, 407, 48, 407, 407, 44, 44, 44, 48,
+ 227, 232, 407, 407, 407, 48, 407, 226, 407, 44,
+ 44, 44, 48, 228, 233, 44, 44, 44, 48, 230,
+ 234, 44, 44, 44, 229, 235, 46, 407, 407, 48,
+ 44, 44, 44, 407, 407, 46, 44, 44, 44, 231,
+
+ 407, 239, 48, 407, 44, 44, 44, 407, 48, 46,
+ 407, 44, 44, 44, 48, 407, 46, 44, 44, 44,
+ 407, 407, 46, 48, 407, 407, 407, 407, 407, 48,
+ 237, 45, 45, 45, 45, 407, 407, 48, 238, 45,
+ 45, 45, 45, 45, 48, 407, 44, 44, 44, 407,
+ 48, 46, 240, 244, 45, 45, 45, 45, 45, 45,
+ 44, 44, 44, 241, 407, 46, 407, 44, 44, 44,
+ 407, 242, 246, 407, 407, 44, 44, 44, 407, 48,
+ 247, 44, 44, 44, 407, 407, 46, 44, 44, 44,
+ 407, 407, 249, 48, 243, 407, 407, 44, 44, 44,
+
+ 48, 407, 46, 407, 407, 407, 245, 250, 48, 407,
+ 44, 44, 44, 407, 48, 46, 407, 44, 44, 44,
+ 48, 407, 46, 44, 44, 44, 407, 407, 46, 407,
+ 48, 44, 44, 44, 407, 248, 46, 44, 44, 44,
+ 407, 407, 46, 48, 44, 44, 44, 407, 407, 46,
+ 48, 407, 407, 251, 407, 407, 48, 44, 44, 44,
+ 252, 253, 46, 407, 48, 407, 44, 44, 44, 407,
+ 48, 46, 407, 407, 407, 254, 407, 48, 407, 44,
+ 44, 44, 407, 255, 46, 407, 44, 44, 44, 407,
+ 48, 46, 407, 44, 44, 44, 257, 256, 262, 48,
+
+ 44, 44, 44, 407, 407, 46, 259, 407, 44, 44,
+ 44, 258, 48, 46, 407, 44, 44, 44, 407, 48,
+ 46, 407, 44, 44, 44, 265, 48, 46, 260, 407,
+ 407, 407, 407, 48, 44, 44, 44, 407, 261, 46,
+ 407, 48, 407, 407, 263, 44, 44, 44, 48, 407,
+ 46, 44, 44, 44, 407, 48, 46, 407, 407, 407,
+ 266, 407, 264, 267, 44, 44, 44, 48, 407, 46,
+ 44, 44, 44, 407, 407, 46, 407, 407, 48, 407,
+ 269, 44, 44, 44, 48, 407, 46, 44, 44, 44,
+ 407, 407, 46, 407, 407, 271, 407, 48, 407, 270,
+
+ 407, 407, 407, 48, 272, 44, 44, 44, 407, 407,
+ 46, 44, 44, 44, 48, 407, 46, 44, 44, 44,
+ 48, 407, 46, 273, 44, 44, 44, 407, 407, 46,
+ 407, 407, 407, 275, 407, 274, 407, 407, 48, 407,
+ 407, 44, 44, 44, 48, 276, 46, 44, 44, 44,
+ 48, 407, 46, 44, 44, 44, 407, 48, 46, 407,
+ 277, 407, 407, 278, 44, 44, 44, 407, 407, 46,
+ 44, 44, 44, 279, 48, 46, 44, 44, 44, 407,
+ 48, 285, 407, 44, 44, 44, 48, 407, 286, 407,
+ 280, 407, 407, 281, 44, 44, 44, 48, 407, 46,
+
+ 44, 44, 44, 48, 407, 46, 44, 44, 44, 48,
+ 284, 46, 282, 407, 407, 407, 48, 283, 287, 407,
+ 44, 44, 44, 407, 407, 46, 407, 48, 407, 407,
+ 44, 44, 44, 48, 407, 46, 44, 44, 44, 48,
+ 292, 46, 44, 44, 44, 407, 407, 46, 288, 407,
+ 407, 407, 289, 48, 290, 44, 44, 44, 407, 407,
+ 46, 407, 407, 48, 407, 44, 44, 44, 407, 48,
+ 46, 407, 293, 407, 291, 48, 44, 44, 44, 407,
+ 407, 46, 44, 44, 44, 407, 296, 46, 48, 407,
+ 407, 294, 44, 44, 44, 407, 407, 46, 48, 295,
+
+ 44, 44, 44, 407, 407, 300, 407, 407, 407, 48,
+ 407, 298, 44, 44, 44, 48, 407, 301, 44, 44,
+ 44, 407, 407, 46, 407, 48, 297, 407, 302, 44,
+ 44, 44, 407, 48, 46, 44, 44, 44, 407, 407,
+ 46, 407, 407, 299, 407, 48, 407, 44, 44, 44,
+ 407, 48, 46, 44, 44, 44, 407, 407, 46, 44,
+ 44, 44, 48, 407, 46, 44, 44, 44, 48, 407,
+ 46, 44, 44, 44, 407, 407, 309, 44, 44, 44,
+ 48, 407, 310, 407, 407, 303, 48, 304, 44, 44,
+ 44, 305, 48, 311, 44, 44, 44, 407, 48, 46,
+
+ 407, 407, 407, 407, 48, 308, 306, 44, 44, 44,
+ 48, 307, 46, 44, 44, 44, 407, 407, 46, 407,
+ 407, 48, 407, 44, 44, 44, 407, 48, 315, 44,
+ 44, 44, 407, 407, 46, 407, 44, 44, 44, 407,
+ 48, 46, 407, 312, 407, 407, 48, 313, 44, 44,
+ 44, 407, 314, 46, 407, 407, 48, 407, 44, 44,
+ 44, 407, 48, 46, 407, 44, 44, 44, 407, 48,
+ 46, 44, 44, 44, 407, 407, 46, 44, 44, 44,
+ 407, 48, 46, 407, 317, 407, 407, 407, 316, 407,
+ 318, 48, 44, 44, 44, 407, 319, 46, 48, 407,
+
+ 44, 44, 44, 407, 48, 46, 320, 322, 407, 407,
+ 48, 321, 44, 44, 44, 323, 407, 46, 44, 44,
+ 44, 407, 407, 46, 407, 48, 44, 44, 44, 407,
+ 407, 46, 324, 48, 44, 44, 44, 407, 407, 46,
+ 407, 44, 44, 44, 407, 48, 46, 44, 44, 44,
+ 407, 48, 46, 407, 407, 325, 407, 407, 327, 48,
+ 407, 44, 44, 44, 407, 326, 332, 48, 407, 407,
+ 407, 329, 407, 407, 48, 407, 44, 44, 44, 407,
+ 48, 46, 44, 44, 44, 328, 407, 46, 44, 44,
+ 44, 407, 407, 335, 48, 330, 407, 407, 336, 407,
+
+ 407, 331, 44, 44, 44, 407, 407, 337, 407, 48,
+ 407, 44, 44, 44, 407, 48, 46, 44, 44, 44,
+ 407, 48, 46, 44, 44, 44, 333, 407, 46, 407,
+ 407, 44, 44, 44, 334, 48, 46, 44, 44, 44,
+ 407, 407, 46, 407, 48, 407, 407, 338, 407, 407,
+ 48, 407, 407, 44, 44, 44, 48, 407, 46, 407,
+ 407, 340, 407, 407, 48, 407, 44, 44, 44, 407,
+ 48, 46, 407, 44, 44, 44, 339, 342, 345, 44,
+ 44, 44, 407, 407, 46, 341, 48, 44, 44, 44,
+ 407, 407, 46, 407, 407, 343, 44, 44, 44, 48,
+
+ 407, 46, 344, 407, 407, 407, 48, 407, 44, 44,
+ 44, 407, 48, 349, 44, 44, 44, 407, 407, 46,
+ 48, 407, 407, 346, 407, 44, 44, 44, 407, 48,
+ 351, 407, 44, 44, 44, 407, 347, 352, 44, 44,
+ 44, 48, 348, 46, 44, 44, 44, 48, 407, 46,
+ 44, 44, 44, 407, 407, 46, 407, 407, 48, 407,
+ 407, 407, 44, 44, 44, 48, 350, 46, 44, 44,
+ 44, 48, 407, 46, 44, 44, 44, 48, 407, 46,
+ 44, 44, 44, 48, 407, 46, 407, 407, 44, 44,
+ 44, 355, 353, 360, 407, 48, 407, 354, 44, 44,
+
+ 44, 48, 407, 46, 407, 407, 407, 48, 407, 356,
+ 44, 44, 44, 48, 407, 46, 407, 358, 357, 407,
+ 359, 48, 44, 44, 44, 407, 407, 46, 44, 44,
+ 44, 48, 407, 46, 44, 44, 44, 407, 361, 365,
+ 44, 44, 44, 48, 407, 46, 44, 44, 44, 407,
+ 407, 46, 44, 44, 44, 48, 407, 368, 44, 44,
+ 44, 48, 407, 46, 362, 407, 364, 48, 407, 407,
+ 44, 44, 44, 48, 363, 46, 366, 407, 407, 48,
+ 407, 44, 44, 44, 407, 48, 46, 44, 44, 44,
+ 367, 48, 46, 44, 44, 44, 407, 407, 46, 44,
+
+ 44, 44, 369, 48, 46, 407, 44, 44, 44, 407,
+ 370, 46, 407, 407, 48, 407, 407, 407, 407, 407,
+ 48, 371, 44, 44, 44, 407, 48, 46, 44, 44,
+ 44, 407, 48, 46, 407, 407, 372, 407, 407, 48,
+ 407, 44, 44, 44, 407, 407, 46, 374, 407, 407,
+ 375, 373, 44, 44, 44, 48, 407, 46, 44, 44,
+ 44, 48, 376, 46, 44, 44, 44, 407, 407, 381,
+ 44, 44, 44, 377, 48, 382, 44, 44, 44, 407,
+ 407, 46, 44, 44, 44, 48, 407, 46, 407, 407,
+ 407, 48, 407, 378, 44, 44, 44, 48, 407, 46,
+
+ 407, 379, 407, 48, 380, 44, 44, 44, 407, 48,
+ 46, 44, 44, 44, 407, 48, 46, 407, 44, 44,
+ 44, 407, 407, 46, 44, 44, 44, 48, 383, 46,
+ 44, 44, 44, 407, 407, 46, 384, 407, 48, 407,
+ 44, 44, 44, 407, 48, 46, 407, 407, 385, 407,
+ 387, 48, 407, 407, 386, 407, 407, 48, 388, 44,
+ 44, 44, 407, 48, 392, 44, 44, 44, 389, 407,
+ 393, 407, 407, 48, 44, 44, 44, 407, 390, 394,
+ 391, 44, 44, 44, 407, 407, 46, 407, 44, 44,
+ 44, 407, 48, 396, 44, 44, 44, 407, 48, 46,
+
+ 44, 44, 44, 407, 407, 46, 407, 48, 44, 44,
+ 44, 407, 407, 46, 48, 407, 407, 44, 44, 44,
+ 407, 48, 400, 44, 44, 44, 407, 48, 401, 395,
+ 44, 44, 44, 48, 407, 46, 407, 44, 44, 44,
+ 398, 48, 46, 407, 44, 44, 44, 407, 397, 404,
+ 48, 407, 44, 44, 44, 407, 48, 405, 407, 407,
+ 407, 399, 407, 48, 44, 44, 44, 407, 407, 406,
+ 48, 407, 407, 407, 407, 407, 407, 48, 407, 407,
+ 407, 407, 407, 402, 407, 48, 407, 407, 407, 407,
+ 403, 407, 407, 407, 407, 407, 407, 48, 36, 36,
+
+ 36, 45, 45, 45, 65, 407, 65, 86, 86, 86,
+ 90, 90, 90, 119, 407, 119, 129, 129, 129, 11,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407
+ } ;
+
+static const flex_int16_t yy_chk[2784] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 3, 3, 3, 4, 4, 4, 3,
+ 9, 7, 4, 5, 5, 8, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 10, 13, 27,
+ 13, 21, 5, 5, 9, 5, 21, 411, 5, 25,
+
+ 5, 25, 5, 236, 25, 25, 25, 25, 5, 5,
+ 7, 10, 5, 27, 8, 17, 5, 17, 17, 17,
+ 17, 19, 5, 19, 19, 19, 19, 26, 17, 5,
+ 5, 15, 15, 15, 19, 28, 15, 31, 20, 20,
+ 20, 20, 26, 37, 17, 37, 42, 32, 42, 20,
+ 19, 33, 31, 22, 15, 22, 22, 22, 22, 28,
+ 44, 202, 168, 78, 15, 20, 22, 15, 15, 15,
+ 15, 32, 15, 76, 15, 33, 15, 15, 15, 15,
+ 15, 15, 22, 15, 15, 15, 15, 78, 44, 45,
+ 45, 45, 86, 76, 45, 47, 47, 47, 48, 77,
+
+ 47, 81, 49, 49, 49, 48, 129, 49, 120, 79,
+ 50, 50, 50, 80, 126, 50, 51, 51, 51, 77,
+ 86, 51, 45, 90, 122, 81, 48, 84, 47, 79,
+ 48, 83, 120, 80, 48, 49, 43, 52, 52, 52,
+ 122, 48, 52, 50, 47, 48, 50, 48, 48, 51,
+ 125, 52, 123, 53, 53, 53, 51, 49, 53, 54,
+ 54, 54, 51, 162, 54, 51, 125, 55, 55, 55,
+ 52, 121, 55, 56, 56, 56, 123, 52, 56, 162,
+ 52, 52, 57, 57, 57, 41, 53, 57, 58, 58,
+ 58, 121, 54, 58, 39, 38, 59, 59, 59, 53,
+
+ 55, 59, 74, 74, 74, 74, 56, 55, 54, 60,
+ 60, 60, 165, 54, 60, 57, 24, 56, 57, 14,
+ 11, 58, 56, 124, 58, 61, 61, 61, 165, 59,
+ 61, 62, 62, 62, 0, 0, 62, 0, 0, 63,
+ 63, 63, 60, 124, 63, 60, 0, 0, 64, 64,
+ 64, 59, 0, 64, 75, 75, 75, 75, 61, 66,
+ 66, 66, 66, 0, 62, 61, 0, 0, 0, 0,
+ 66, 62, 63, 0, 67, 0, 67, 67, 67, 67,
+ 62, 64, 0, 0, 0, 0, 66, 67, 89, 89,
+ 89, 63, 0, 89, 0, 0, 64, 88, 63, 0,
+
+ 68, 64, 68, 67, 88, 68, 68, 68, 68, 69,
+ 0, 69, 69, 69, 69, 70, 70, 70, 70, 0,
+ 0, 89, 69, 0, 0, 88, 70, 0, 0, 88,
+ 91, 91, 91, 88, 0, 91, 89, 0, 69, 0,
+ 88, 0, 70, 0, 88, 0, 88, 88, 0, 0,
+ 0, 0, 93, 93, 93, 0, 0, 93, 0, 0,
+ 94, 94, 94, 91, 92, 94, 0, 0, 0, 0,
+ 0, 0, 92, 92, 92, 92, 0, 0, 0, 0,
+ 92, 92, 92, 92, 92, 93, 95, 95, 95, 0,
+ 0, 95, 0, 94, 0, 92, 92, 92, 92, 92,
+
+ 92, 0, 96, 96, 96, 0, 93, 96, 0, 97,
+ 97, 97, 0, 94, 97, 98, 98, 98, 0, 95,
+ 98, 99, 99, 99, 0, 0, 99, 100, 100, 100,
+ 0, 0, 100, 0, 0, 96, 0, 0, 95, 101,
+ 101, 101, 97, 0, 101, 0, 96, 0, 98, 0,
+ 102, 102, 102, 0, 99, 102, 97, 97, 99, 0,
+ 100, 103, 103, 103, 0, 100, 103, 0, 104, 104,
+ 104, 0, 101, 104, 105, 105, 105, 0, 0, 105,
+ 106, 106, 106, 102, 0, 106, 0, 107, 107, 107,
+ 0, 101, 107, 0, 103, 108, 108, 108, 0, 0,
+
+ 108, 104, 0, 0, 0, 102, 0, 105, 0, 0,
+ 103, 0, 0, 106, 109, 109, 109, 106, 0, 109,
+ 107, 0, 104, 0, 0, 110, 110, 110, 108, 107,
+ 110, 0, 0, 105, 111, 111, 111, 0, 0, 111,
+ 112, 112, 112, 0, 0, 112, 0, 109, 0, 113,
+ 113, 113, 0, 108, 113, 0, 0, 0, 110, 0,
+ 0, 109, 0, 114, 114, 114, 0, 111, 114, 115,
+ 115, 115, 0, 112, 115, 0, 112, 0, 0, 110,
+ 0, 0, 113, 0, 0, 0, 111, 111, 111, 116,
+ 116, 116, 0, 0, 116, 0, 114, 0, 117, 117,
+
+ 117, 114, 115, 117, 0, 113, 0, 118, 118, 118,
+ 0, 0, 118, 128, 128, 128, 0, 0, 128, 0,
+ 0, 0, 116, 0, 115, 0, 0, 0, 0, 0,
+ 0, 117, 133, 133, 133, 0, 0, 133, 0, 116,
+ 118, 0, 117, 0, 0, 0, 128, 118, 0, 0,
+ 0, 0, 0, 134, 134, 134, 0, 0, 134, 0,
+ 0, 0, 0, 0, 0, 133, 0, 128, 132, 132,
+ 132, 132, 0, 0, 0, 133, 132, 132, 132, 132,
+ 132, 0, 135, 135, 135, 0, 134, 135, 0, 0,
+ 0, 132, 132, 132, 132, 132, 132, 134, 136, 136,
+
+ 136, 0, 0, 136, 137, 137, 137, 0, 0, 137,
+ 0, 138, 138, 138, 0, 135, 138, 140, 140, 140,
+ 0, 0, 140, 141, 141, 141, 0, 0, 141, 0,
+ 0, 136, 0, 0, 0, 0, 135, 137, 136, 0,
+ 0, 142, 142, 142, 138, 0, 142, 143, 143, 143,
+ 140, 137, 143, 144, 144, 144, 141, 0, 144, 145,
+ 145, 145, 0, 0, 145, 138, 146, 146, 146, 0,
+ 0, 146, 140, 141, 142, 0, 146, 147, 147, 147,
+ 143, 142, 147, 148, 148, 148, 144, 0, 148, 149,
+ 149, 149, 145, 0, 149, 0, 0, 0, 144, 146,
+
+ 143, 150, 150, 150, 0, 0, 150, 0, 0, 145,
+ 147, 0, 0, 151, 151, 151, 148, 0, 151, 152,
+ 152, 152, 149, 0, 152, 148, 153, 153, 153, 147,
+ 0, 153, 0, 0, 150, 154, 154, 154, 0, 0,
+ 154, 150, 149, 0, 0, 149, 151, 0, 155, 155,
+ 155, 0, 152, 155, 0, 152, 0, 0, 0, 153,
+ 156, 156, 156, 151, 0, 156, 0, 0, 154, 157,
+ 157, 157, 0, 0, 157, 158, 158, 158, 0, 153,
+ 158, 155, 159, 159, 159, 0, 0, 159, 0, 154,
+ 160, 160, 160, 156, 155, 160, 171, 171, 171, 0,
+
+ 156, 171, 157, 0, 161, 161, 161, 0, 158, 161,
+ 0, 169, 169, 169, 157, 159, 169, 0, 0, 172,
+ 172, 172, 159, 160, 172, 173, 173, 173, 158, 171,
+ 173, 0, 0, 0, 0, 173, 171, 161, 174, 174,
+ 174, 0, 0, 174, 169, 175, 175, 175, 160, 0,
+ 175, 0, 172, 0, 0, 0, 161, 172, 173, 0,
+ 0, 0, 0, 169, 170, 170, 170, 170, 0, 0,
+ 0, 174, 170, 170, 170, 170, 170, 0, 175, 176,
+ 176, 176, 0, 0, 176, 175, 174, 170, 170, 170,
+ 170, 170, 170, 177, 177, 177, 0, 0, 177, 178,
+
+ 178, 178, 0, 0, 178, 179, 179, 179, 0, 0,
+ 179, 0, 176, 0, 178, 178, 180, 180, 180, 0,
+ 0, 180, 181, 181, 181, 0, 177, 181, 183, 183,
+ 183, 176, 178, 183, 0, 177, 0, 0, 179, 0,
+ 182, 182, 182, 179, 0, 182, 184, 184, 184, 180,
+ 182, 184, 185, 185, 185, 181, 0, 185, 0, 180,
+ 0, 183, 186, 186, 186, 0, 0, 186, 0, 183,
+ 0, 0, 0, 182, 0, 181, 187, 187, 187, 184,
+ 0, 187, 184, 0, 0, 185, 188, 188, 188, 0,
+ 0, 188, 185, 0, 0, 186, 189, 189, 189, 0,
+
+ 0, 189, 190, 190, 190, 0, 186, 190, 0, 187,
+ 191, 191, 191, 0, 0, 191, 187, 0, 0, 188,
+ 192, 192, 192, 0, 0, 192, 193, 193, 193, 189,
+ 0, 193, 194, 194, 194, 190, 0, 194, 0, 195,
+ 195, 195, 0, 191, 195, 196, 196, 196, 0, 190,
+ 196, 189, 0, 192, 0, 0, 197, 197, 197, 193,
+ 192, 197, 0, 0, 0, 194, 0, 191, 0, 198,
+ 198, 198, 195, 193, 198, 199, 199, 199, 196, 195,
+ 199, 203, 203, 203, 194, 199, 203, 0, 0, 197,
+ 205, 205, 205, 0, 0, 205, 206, 206, 206, 196,
+
+ 0, 206, 198, 0, 210, 210, 210, 0, 199, 210,
+ 0, 207, 207, 207, 203, 0, 207, 208, 208, 208,
+ 0, 0, 208, 205, 0, 0, 0, 0, 0, 206,
+ 203, 204, 204, 204, 204, 0, 0, 210, 205, 204,
+ 204, 204, 204, 204, 207, 0, 209, 209, 209, 0,
+ 208, 209, 207, 210, 204, 204, 204, 204, 204, 204,
+ 211, 211, 211, 207, 0, 211, 0, 212, 212, 212,
+ 0, 208, 212, 0, 0, 213, 213, 213, 0, 209,
+ 213, 214, 214, 214, 0, 0, 214, 215, 215, 215,
+ 0, 0, 215, 211, 209, 0, 0, 216, 216, 216,
+
+ 212, 0, 216, 0, 0, 0, 211, 216, 213, 0,
+ 217, 217, 217, 0, 214, 217, 0, 218, 218, 218,
+ 215, 0, 218, 219, 219, 219, 0, 0, 219, 0,
+ 216, 220, 220, 220, 0, 214, 220, 221, 221, 221,
+ 0, 0, 221, 217, 222, 222, 222, 0, 0, 222,
+ 218, 0, 0, 217, 0, 0, 219, 224, 224, 224,
+ 217, 218, 224, 0, 220, 0, 225, 225, 225, 0,
+ 221, 225, 0, 0, 0, 219, 0, 222, 0, 226,
+ 226, 226, 0, 220, 226, 0, 227, 227, 227, 0,
+ 224, 227, 0, 228, 228, 228, 222, 221, 228, 225,
+
+ 229, 229, 229, 0, 0, 229, 225, 0, 230, 230,
+ 230, 224, 226, 230, 0, 231, 231, 231, 0, 227,
+ 231, 0, 235, 235, 235, 231, 228, 235, 226, 0,
+ 0, 0, 0, 229, 237, 237, 237, 0, 227, 237,
+ 0, 230, 0, 0, 229, 238, 238, 238, 231, 0,
+ 238, 240, 240, 240, 0, 235, 240, 0, 0, 0,
+ 235, 0, 230, 235, 241, 241, 241, 237, 0, 241,
+ 242, 242, 242, 0, 0, 242, 0, 0, 238, 0,
+ 237, 243, 243, 243, 240, 0, 243, 244, 244, 244,
+ 0, 0, 244, 0, 0, 240, 0, 241, 0, 238,
+
+ 0, 0, 0, 242, 241, 245, 245, 245, 0, 0,
+ 245, 248, 248, 248, 243, 0, 248, 250, 250, 250,
+ 244, 0, 250, 242, 251, 251, 251, 0, 0, 251,
+ 0, 0, 0, 244, 0, 243, 0, 0, 245, 0,
+ 0, 252, 252, 252, 248, 245, 252, 253, 253, 253,
+ 250, 0, 253, 254, 254, 254, 0, 251, 254, 0,
+ 248, 0, 0, 250, 255, 255, 255, 0, 0, 255,
+ 256, 256, 256, 251, 252, 256, 257, 257, 257, 0,
+ 253, 257, 0, 258, 258, 258, 254, 0, 258, 0,
+ 252, 0, 0, 253, 259, 259, 259, 255, 0, 259,
+
+ 260, 260, 260, 256, 0, 260, 261, 261, 261, 257,
+ 256, 261, 254, 0, 0, 0, 258, 255, 258, 0,
+ 263, 263, 263, 0, 0, 263, 0, 259, 0, 0,
+ 264, 264, 264, 260, 0, 264, 265, 265, 265, 261,
+ 264, 265, 266, 266, 266, 0, 0, 266, 259, 0,
+ 0, 0, 260, 263, 261, 267, 267, 267, 0, 0,
+ 267, 0, 0, 264, 0, 271, 271, 271, 0, 265,
+ 271, 0, 265, 0, 263, 266, 269, 269, 269, 0,
+ 0, 269, 270, 270, 270, 0, 269, 270, 267, 0,
+ 0, 266, 272, 272, 272, 0, 0, 272, 271, 267,
+
+ 273, 273, 273, 0, 0, 273, 0, 0, 0, 269,
+ 0, 271, 274, 274, 274, 270, 0, 274, 275, 275,
+ 275, 0, 0, 275, 0, 272, 270, 0, 275, 276,
+ 276, 276, 0, 273, 276, 277, 277, 277, 0, 0,
+ 277, 0, 0, 272, 0, 274, 0, 278, 278, 278,
+ 0, 275, 278, 279, 279, 279, 0, 0, 279, 280,
+ 280, 280, 276, 0, 280, 281, 281, 281, 277, 0,
+ 281, 282, 282, 282, 0, 0, 282, 283, 283, 283,
+ 278, 0, 283, 0, 0, 276, 279, 277, 284, 284,
+ 284, 278, 280, 284, 287, 287, 287, 0, 281, 287,
+
+ 0, 0, 0, 0, 282, 281, 279, 288, 288, 288,
+ 283, 280, 288, 289, 289, 289, 0, 0, 289, 0,
+ 0, 284, 0, 290, 290, 290, 0, 287, 290, 291,
+ 291, 291, 0, 0, 291, 0, 292, 292, 292, 0,
+ 288, 292, 0, 287, 0, 0, 289, 288, 293, 293,
+ 293, 0, 289, 293, 0, 0, 290, 0, 294, 294,
+ 294, 0, 291, 294, 0, 295, 295, 295, 0, 292,
+ 295, 296, 296, 296, 0, 0, 296, 297, 297, 297,
+ 0, 293, 297, 0, 292, 0, 0, 0, 291, 0,
+ 292, 294, 298, 298, 298, 0, 293, 298, 295, 0,
+
+ 299, 299, 299, 0, 296, 299, 294, 296, 0, 0,
+ 297, 295, 302, 302, 302, 297, 0, 302, 303, 303,
+ 303, 0, 0, 303, 0, 298, 304, 304, 304, 0,
+ 0, 304, 298, 299, 305, 305, 305, 0, 0, 305,
+ 0, 306, 306, 306, 0, 302, 306, 307, 307, 307,
+ 0, 303, 307, 0, 0, 299, 0, 0, 303, 304,
+ 0, 308, 308, 308, 0, 302, 308, 305, 0, 0,
+ 0, 305, 0, 0, 306, 0, 312, 312, 312, 0,
+ 307, 312, 313, 313, 313, 304, 0, 313, 314, 314,
+ 314, 0, 0, 314, 308, 306, 0, 0, 314, 0,
+
+ 0, 307, 316, 316, 316, 0, 0, 316, 0, 312,
+ 0, 317, 317, 317, 0, 313, 317, 318, 318, 318,
+ 0, 314, 318, 319, 319, 319, 312, 0, 319, 0,
+ 0, 320, 320, 320, 313, 316, 320, 321, 321, 321,
+ 0, 0, 321, 0, 317, 0, 0, 317, 0, 0,
+ 318, 0, 0, 322, 322, 322, 319, 0, 322, 0,
+ 0, 319, 0, 0, 320, 0, 323, 323, 323, 0,
+ 321, 323, 0, 324, 324, 324, 318, 321, 324, 325,
+ 325, 325, 0, 0, 325, 320, 322, 326, 326, 326,
+ 0, 0, 326, 0, 0, 322, 327, 327, 327, 323,
+
+ 0, 327, 323, 0, 0, 0, 324, 0, 328, 328,
+ 328, 0, 325, 328, 329, 329, 329, 0, 0, 329,
+ 326, 0, 0, 325, 0, 330, 330, 330, 0, 327,
+ 330, 0, 331, 331, 331, 0, 326, 331, 333, 333,
+ 333, 328, 327, 333, 334, 334, 334, 329, 0, 334,
+ 336, 336, 336, 0, 0, 336, 0, 0, 330, 0,
+ 0, 0, 338, 338, 338, 331, 329, 338, 339, 339,
+ 339, 333, 0, 339, 340, 340, 340, 334, 0, 340,
+ 341, 341, 341, 336, 0, 341, 0, 0, 342, 342,
+ 342, 336, 333, 342, 0, 338, 0, 334, 343, 343,
+
+ 343, 339, 0, 343, 0, 0, 0, 340, 0, 338,
+ 344, 344, 344, 341, 0, 344, 0, 340, 339, 0,
+ 341, 342, 346, 346, 346, 0, 0, 346, 347, 347,
+ 347, 343, 0, 347, 348, 348, 348, 0, 343, 348,
+ 350, 350, 350, 344, 0, 350, 353, 353, 353, 0,
+ 0, 353, 354, 354, 354, 346, 0, 354, 355, 355,
+ 355, 347, 0, 355, 344, 0, 347, 348, 0, 0,
+ 356, 356, 356, 350, 346, 356, 350, 0, 0, 353,
+ 0, 357, 357, 357, 0, 354, 357, 358, 358, 358,
+ 353, 355, 358, 359, 359, 359, 0, 0, 359, 361,
+
+ 361, 361, 355, 356, 361, 0, 362, 362, 362, 0,
+ 356, 362, 0, 0, 357, 0, 0, 0, 0, 0,
+ 358, 357, 363, 363, 363, 0, 359, 363, 364, 364,
+ 364, 0, 361, 364, 0, 0, 358, 0, 0, 362,
+ 0, 366, 366, 366, 0, 0, 366, 361, 0, 0,
+ 362, 359, 367, 367, 367, 363, 0, 367, 369, 369,
+ 369, 364, 363, 369, 370, 370, 370, 0, 0, 370,
+ 371, 371, 371, 364, 366, 371, 372, 372, 372, 0,
+ 0, 372, 373, 373, 373, 367, 0, 373, 0, 0,
+ 0, 369, 0, 366, 374, 374, 374, 370, 0, 374,
+
+ 0, 367, 0, 371, 369, 375, 375, 375, 0, 372,
+ 375, 376, 376, 376, 0, 373, 376, 0, 377, 377,
+ 377, 0, 0, 377, 378, 378, 378, 374, 372, 378,
+ 379, 379, 379, 0, 0, 379, 373, 0, 375, 0,
+ 380, 380, 380, 0, 376, 380, 0, 0, 374, 0,
+ 376, 377, 0, 0, 375, 0, 0, 378, 377, 383,
+ 383, 383, 0, 379, 383, 384, 384, 384, 378, 0,
+ 384, 0, 0, 380, 385, 385, 385, 0, 379, 385,
+ 380, 386, 386, 386, 0, 0, 386, 0, 387, 387,
+ 387, 0, 383, 387, 388, 388, 388, 0, 384, 388,
+
+ 389, 389, 389, 0, 0, 389, 0, 385, 390, 390,
+ 390, 0, 0, 390, 386, 0, 0, 391, 391, 391,
+ 0, 387, 391, 395, 395, 395, 0, 388, 395, 386,
+ 397, 397, 397, 389, 0, 397, 0, 398, 398, 398,
+ 389, 390, 398, 0, 399, 399, 399, 0, 388, 399,
+ 391, 0, 402, 402, 402, 0, 395, 402, 0, 0,
+ 0, 390, 0, 397, 403, 403, 403, 0, 0, 403,
+ 398, 0, 0, 0, 0, 0, 0, 399, 0, 0,
+ 0, 0, 0, 397, 0, 402, 0, 0, 0, 0,
+ 398, 0, 0, 0, 0, 0, 0, 403, 408, 408,
+
+ 408, 409, 409, 409, 410, 0, 410, 412, 412, 412,
+ 413, 413, 413, 414, 0, 414, 415, 415, 415, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 1;
+
+static const flex_int16_t yy_rule_linenum[70] =
+ { 0,
+ 134, 136, 138, 143, 144, 149, 150, 151, 163, 166,
+ 171, 178, 187, 196, 205, 218, 231, 240, 249, 258,
+ 267, 276, 285, 294, 303, 312, 321, 330, 339, 348,
+ 357, 366, 375, 384, 393, 402, 411, 420, 429, 438,
+ 447, 456, 465, 474, 483, 492, 501, 510, 519, 528,
+ 537, 638, 654, 703, 711, 726, 727, 728, 729, 730,
+ 731, 733, 751, 764, 769, 773, 775, 777, 779
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "agent_lexer.ll"
+/* Copyright (C) 2017-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/. */
+#line 8 "agent_lexer.ll"
+
+/* Generated files do not make clang static analyser so happy */
+#ifndef __clang_analyzer__
+
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <string>
+#include <agent/parser_context.h>
+#include <asiolink/io_address.h>
+#include <boost/lexical_cast.hpp>
+#include <exceptions/exceptions.h>
+#include <cc/dhcp_config_error.h>
+
+/* Please avoid C++ style comments (// ... eol) as they break flex 2.6.2 */
+
+/* Work around an incompatibility in flex (at least versions
+ 2.5.31 through 2.5.33): it generates code that does
+ not conform to C89. See Debian bug 333231
+ <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */
+# undef yywrap
+# define yywrap() 1
+
+namespace {
+
+bool start_token_flag = false;
+
+isc::agent::ParserContext::ParserType start_token_value;
+unsigned int comment_start_line = 0;
+
+using namespace isc;
+using isc::agent::AgentParser;
+
+};
+
+/* To avoid the call to exit... oops! */
+#define YY_FATAL_ERROR(msg) isc::agent::ParserContext::fatal(msg)
+#line 1585 "agent_lexer.cc"
+/* noyywrap disables automatic rewinding for the next file to parse. Since we
+ always parse only a single string, there's no need to do any wraps. And
+ using yywrap requires linking with -lfl, which provides the default yywrap
+ implementation that always returns 1 anyway. */
+/* nounput simplifies the lexer, by removing support for putting a character
+ back into the input stream. We never use such capability anyway. */
+/* batch means that we'll never use the generated lexer interactively. */
+/* avoid to get static global variables to remain with C++. */
+/* in last resort %option reentrant */
+/* Enables debug mode. To see the debug messages, one needs to also set
+ yy_flex_debug to 1, then the debug messages will be printed on stderr. */
+/* I have no idea what this option does, except it was specified in the bison
+ examples and Postgres folks added it to remove gcc 4.3 warnings. Let's
+ be on the safe side and keep it. */
+#define YY_NO_INPUT 1
+
+/* These are not token expressions yet, just convenience expressions that
+ can be used during actual token definitions. Note some can match
+ incorrect inputs (e.g., IP addresses) which must be checked. */
+/* for errors */
+#line 97 "agent_lexer.ll"
+/* This code run each time a pattern is matched. It updates the location
+ by moving it ahead by yyleng bytes. yyleng specifies the length of the
+ currently matched token. */
+#define YY_USER_ACTION driver.loc_.columns(yyleng);
+#line 1611 "agent_lexer.cc"
+#line 1612 "agent_lexer.cc"
+
+#define INITIAL 0
+#define COMMENT 1
+#define DIR_ENTER 2
+#define DIR_INCLUDE 3
+#define DIR_EXIT 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+/* %if-c-only */
+#include <unistd.h>
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* %if-c-only Reentrant structure and macros (non-C++). */
+/* %if-reentrant */
+/* %if-c-only */
+
+static int yy_init_globals ( void );
+
+/* %endif */
+/* %if-reentrant */
+/* %endif */
+/* %endif End reentrant structures and macros. */
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( void );
+
+int yyget_debug ( void );
+
+void yyset_debug ( int debug_flag );
+
+YY_EXTRA_TYPE yyget_extra ( void );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined );
+
+FILE *yyget_in ( void );
+
+void yyset_in ( FILE * _in_str );
+
+FILE *yyget_out ( void );
+
+void yyset_out ( FILE * _out_str );
+
+ int yyget_leng ( void );
+
+char *yyget_text ( void );
+
+int yyget_lineno ( void );
+
+void yyset_lineno ( int _line_number );
+
+/* %if-bison-bridge */
+/* %endif */
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( void );
+#else
+extern int yywrap ( void );
+#endif
+#endif
+
+/* %not-for-header */
+#ifndef YY_NO_UNPUT
+
+#endif
+/* %ok-for-header */
+
+/* %endif */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * );
+#endif
+
+#ifndef YY_NO_INPUT
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+#ifdef __cplusplus
+static int yyinput ( void );
+#else
+static int input ( void );
+#endif
+/* %ok-for-header */
+
+/* %endif */
+#endif
+
+/* %if-c-only */
+
+/* %endif */
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* %if-c-only Standard (non-C++) definition */
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+/* %% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \ */\
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+/* %if-c++-only C++ definition \ */\
+/* %endif */
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+/* %if-c-only */
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+#endif
+
+/* %if-tables-serialization structures and prototypes */
+/* %not-for-header */
+/* %ok-for-header */
+
+/* %not-for-header */
+/* %tables-yydmap generated elements */
+/* %endif */
+/* end tables serialization structures and prototypes */
+
+/* %ok-for-header */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+/* %if-c-only Standard (non-C++) definition */
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+/* %% [6.0] YY_RULE_SETUP definition goes here */
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/* %not-for-header */
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ yy_state_type yy_current_state;
+ char *yy_cp, *yy_bp;
+ int yy_act;
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+/* %if-c-only */
+ yyin = stdin;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+ if ( ! yyout )
+/* %if-c-only */
+ yyout = stdout;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ {
+/* %% [7.0] user's declarations go here */
+#line 103 "agent_lexer.ll"
+
+
+
+#line 107 "agent_lexer.ll"
+ /* This part of the code is copied over to the verbatim to the top
+ of the generated yylex function. Explanation:
+ http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html */
+
+ /* Code run each time yylex is called. */
+ driver.loc_.step();
+
+ /* We currently have 3 points of entries defined:
+ START_JSON - which expects any valid JSON
+ START_AGENT - which expects full configuration (with outer map and Control-agent
+ object in it.
+ START_SUB_AGENT - which expects only content of the Control-agent, this is
+ primarily useful for testing. */
+ if (start_token_flag) {
+ start_token_flag = false;
+ switch (start_token_value) {
+ case ParserContext::PARSER_JSON:
+ default:
+ return isc::agent::AgentParser::make_START_JSON(driver.loc_);
+ case ParserContext::PARSER_AGENT:
+ return isc::agent::AgentParser::make_START_AGENT(driver.loc_);
+ case ParserContext::PARSER_SUB_AGENT:
+ return isc::agent::AgentParser::make_START_SUB_AGENT(driver.loc_);
+ }
+ }
+
+
+#line 1926 "agent_lexer.cc"
+
+ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
+ {
+/* %% [8.0] yymore()-related code goes here */
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+/* %% [9.0] code to set up and find next match goes here */
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 408 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 407 );
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+
+yy_find_action:
+/* %% [10.0] code to find the action number goes here */
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+/* %% [11.0] code for yylineno update goes here */
+
+do_action: /* This label is used only to access EOF actions. */
+
+/* %% [12.0] debug code goes here */
+ if ( yy_flex_debug )
+ {
+ if ( yy_act == 0 )
+ fprintf( stderr, "--scanner backing up\n" );
+ else if ( yy_act < 70 )
+ fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
+ (long)yy_rule_linenum[yy_act], yytext );
+ else if ( yy_act == 70 )
+ fprintf( stderr, "--accepting default rule (\"%s\")\n",
+ yytext );
+ else if ( yy_act == 71 )
+ fprintf( stderr, "--(end of buffer or a NUL)\n" );
+ else
+ fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
+ }
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+/* %% [13.0] actions go here */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 134 "agent_lexer.ll"
+;
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 136 "agent_lexer.ll"
+;
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 138 "agent_lexer.ll"
+{
+ BEGIN(COMMENT);
+ comment_start_line = driver.loc_.end.line;;
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 143 "agent_lexer.ll"
+BEGIN(INITIAL);
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 144 "agent_lexer.ll"
+;
+ YY_BREAK
+case YY_STATE_EOF(COMMENT):
+#line 145 "agent_lexer.ll"
+{
+ isc_throw(ParseError, "Comment not closed. (/* in line " << comment_start_line);
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 149 "agent_lexer.ll"
+BEGIN(DIR_ENTER);
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 150 "agent_lexer.ll"
+BEGIN(DIR_INCLUDE);
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 151 "agent_lexer.ll"
+{
+ /* Include directive. */
+
+ /* Extract the filename. */
+ std::string tmp(yytext+1);
+ tmp.resize(tmp.size() - 1);
+
+ driver.includeFile(tmp);
+}
+ YY_BREAK
+case YY_STATE_EOF(DIR_ENTER):
+case YY_STATE_EOF(DIR_INCLUDE):
+case YY_STATE_EOF(DIR_EXIT):
+#line 160 "agent_lexer.ll"
+{
+ isc_throw(ParseError, "Directive not closed.");
+}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 163 "agent_lexer.ll"
+BEGIN(INITIAL);
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 166 "agent_lexer.ll"
+{
+ /* Ok, we found a with space. Let's ignore it and update loc variable. */
+ driver.loc_.step();
+}
+ YY_BREAK
+case 11:
+/* rule 11 can match eol */
+YY_RULE_SETUP
+#line 171 "agent_lexer.ll"
+{
+ /* Newline found. Let's update the location and continue. */
+ driver.loc_.lines(yyleng);
+ driver.loc_.step();
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 178 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONFIG:
+ return AgentParser::make_CONTROL_AGENT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("Control-agent", driver.loc_);
+ }
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 187 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_HTTP_HOST(driver.loc_);
+ default:
+ return AgentParser::make_STRING("http-host", driver.loc_);
+ }
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 196 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_HTTP_PORT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("http-port", driver.loc_);
+ }
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 205 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ case ParserContext::AUTHENTICATION:
+ case ParserContext::CLIENTS:
+ case ParserContext::SERVER:
+ case ParserContext::LOGGERS:
+ return AgentParser::make_USER_CONTEXT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("user-context", driver.loc_);
+ }
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 218 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ case ParserContext::AUTHENTICATION:
+ case ParserContext::CLIENTS:
+ case ParserContext::SERVER:
+ case ParserContext::LOGGERS:
+ return AgentParser::make_COMMENT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("comment", driver.loc_);
+ }
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 231 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_AUTHENTICATION(driver.loc_);
+ default:
+ return AgentParser::make_STRING("authentication", driver.loc_);
+ }
+}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 240 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_TYPE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("type", driver.loc_);
+ }
+}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 249 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AUTH_TYPE:
+ return AgentParser::make_BASIC(driver.loc_);
+ default:
+ return AgentParser::make_STRING("basic", driver.loc_);
+ }
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 258 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_REALM(driver.loc_);
+ default:
+ return AgentParser::make_STRING("realm", driver.loc_);
+ }
+}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 267 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_DIRECTORY(driver.loc_);
+ default:
+ return AgentParser::make_STRING("directory", driver.loc_);
+ }
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 276 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_CLIENTS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("clients", driver.loc_);
+ }
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 285 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_USER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("user", driver.loc_);
+ }
+}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 294 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_USER_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("user-file", driver.loc_);
+ }
+}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 303 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_PASSWORD(driver.loc_);
+ default:
+ return AgentParser::make_STRING("password", driver.loc_);
+ }
+}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 312 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_PASSWORD_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("password-file", driver.loc_);
+ }
+}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 321 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_TRUST_ANCHOR(driver.loc_);
+ default:
+ return AgentParser::make_STRING("trust-anchor", driver.loc_);
+ }
+}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 330 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_CERT_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("cert-file", driver.loc_);
+ }
+}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 339 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_KEY_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("key-file", driver.loc_);
+ }
+}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 348 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_CERT_REQUIRED(driver.loc_);
+ default:
+ return AgentParser::make_STRING("cert-required", driver.loc_);
+ }
+}
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 357 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_CONTROL_SOCKETS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("control-sockets", driver.loc_);
+ }
+}
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 366 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKETS:
+ return AgentParser::make_DHCP4_SERVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("dhcp4", driver.loc_);
+ }
+}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 375 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKETS:
+ return AgentParser::make_DHCP6_SERVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("dhcp6", driver.loc_);
+ }
+}
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 384 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKETS:
+ return AgentParser::make_D2_SERVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("d2", driver.loc_);
+ }
+}
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 393 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return AgentParser::make_SOCKET_NAME(driver.loc_);
+ default:
+ return AgentParser::make_STRING("socket-name", driver.loc_);
+ }
+}
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 402 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return AgentParser::make_SOCKET_TYPE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("socket-type", driver.loc_);
+ }
+}
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 411 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return AgentParser::make_UNIX(driver.loc_);
+ default:
+ return AgentParser::make_STRING("unix", driver.loc_);
+ }
+}
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 420 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_HOOKS_LIBRARIES(driver.loc_);
+ default:
+ return AgentParser::make_STRING("hooks-libraries", driver.loc_);
+ }
+}
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 429 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return AgentParser::make_LIBRARY(driver.loc_);
+ default:
+ return AgentParser::make_STRING("library", driver.loc_);
+ }
+}
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 438 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return AgentParser::make_PARAMETERS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("parameters", driver.loc_);
+ }
+}
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 447 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_LOGGERS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("loggers", driver.loc_);
+ }
+}
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 456 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_NAME(driver.loc_);
+ default:
+ return AgentParser::make_STRING("name", driver.loc_);
+ }
+}
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 465 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_OUTPUT_OPTIONS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("output_options", driver.loc_);
+ }
+}
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 474 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_OUTPUT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("output", driver.loc_);
+ }
+}
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 483 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_FLUSH(driver.loc_);
+ default:
+ return AgentParser::make_STRING("flush", driver.loc_);
+ }
+}
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 492 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_MAXSIZE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("maxsize", driver.loc_);
+ }
+}
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 501 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_MAXVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("maxver", driver.loc_);
+ }
+}
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 510 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_PATTERN(driver.loc_);
+ default:
+ return AgentParser::make_STRING("pattern", driver.loc_);
+ }
+}
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 519 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_DEBUGLEVEL(driver.loc_);
+ default:
+ return AgentParser::make_STRING("debuglevel", driver.loc_);
+ }
+}
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 528 "agent_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_SEVERITY(driver.loc_);
+ default:
+ return AgentParser::make_STRING("severity", driver.loc_);
+ }
+}
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 537 "agent_lexer.ll"
+{
+ /* A string has been matched. It contains the actual string and single quotes.
+ We need to get those quotes out of the way and just use its content, e.g.
+ for 'foo' we should get foo */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ raw.resize(len);
+ std::string decoded;
+ decoded.reserve(len);
+ for (size_t pos = 0; pos < len; ++pos) {
+ int b = 0;
+ char c = raw[pos];
+ switch (c) {
+ case '"':
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad quote in \"" + raw + "\"");
+ break;
+ case '\\':
+ ++pos;
+ if (pos >= len) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Overflow escape in \"" + raw + "\"");
+ }
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ decoded.push_back(c);
+ break;
+ case 'b':
+ decoded.push_back('\b');
+ break;
+ case 'f':
+ decoded.push_back('\f');
+ break;
+ case 'n':
+ decoded.push_back('\n');
+ break;
+ case 'r':
+ decoded.push_back('\r');
+ break;
+ case 't':
+ decoded.push_back('\t');
+ break;
+ case 'u':
+ /* support only \u0000 to \u00ff */
+ ++pos;
+ if (pos + 4 > len) {
+ /* impossible condition */
+ driver.error(driver.loc_,
+ "Overflow unicode escape in \"" + raw + "\"");
+ }
+ if ((raw[pos] != '0') || (raw[pos + 1] != '0')) {
+ driver.error(driver.loc_,
+ "Unsupported unicode escape in \"" + raw + "\"",
+ pos + 1);
+ }
+ pos += 2;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b = (c - '0') << 4;
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b = (c - 'A' + 10) << 4;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b = (c - 'a' + 10) << 4;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ pos++;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b |= c - '0';
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b |= c - 'A' + 10;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b |= c - 'a' + 10;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ decoded.push_back(static_cast<char>(b & 0xff));
+ break;
+ default:
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad escape in \"" + raw + "\"");
+ }
+ break;
+ default:
+ if ((c >= 0) && (c < 0x20)) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Invalid control in \"" + raw + "\"");
+ }
+ decoded.push_back(c);
+ }
+ }
+
+ return AgentParser::make_STRING(decoded, driver.loc_);
+}
+ YY_BREAK
+case 52:
+/* rule 52 can match eol */
+YY_RULE_SETUP
+#line 638 "agent_lexer.ll"
+{
+ /* Bad string with a forbidden control character inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ for (; pos < len; ++pos) {
+ char c = raw[pos];
+ if ((c >= 0) && (c < 0x20)) {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Invalid control in " + std::string(yytext),
+ pos + 1);
+}
+ YY_BREAK
+case 53:
+/* rule 53 can match eol */
+YY_RULE_SETUP
+#line 654 "agent_lexer.ll"
+{
+ /* Bad string with a bad escape inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ bool found = false;
+ for (; pos < len; ++pos) {
+ if (found) {
+ break;
+ }
+ char c = raw[pos];
+ if (c == '\\') {
+ ++pos;
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ break;
+ case 'u':
+ if ((pos + 4 > len) ||
+ !std::isxdigit(raw[pos + 1]) ||
+ !std::isxdigit(raw[pos + 2]) ||
+ !std::isxdigit(raw[pos + 3]) ||
+ !std::isxdigit(raw[pos + 4])) {
+ found = true;
+ }
+ break;
+ default:
+ found = true;
+ break;
+ }
+ }
+ }
+ /* The rule stops on the first " including on \" so add ... in this case */
+ std::string trailer = "";
+ if (raw[len - 1] == '\\') {
+ trailer = "...";
+ }
+ driver.error(driver.loc_,
+ "Bad escape in " + std::string(yytext) + trailer,
+ pos);
+}
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 703 "agent_lexer.ll"
+{
+ /* Bad string with an open escape at the end */
+ std::string raw(yytext+1);
+ driver.error(driver.loc_,
+ "Overflow escape in " + std::string(yytext),
+ raw.size() + 1);
+}
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 711 "agent_lexer.ll"
+{
+ /* Bad string with an open unicode escape at the end */
+ std::string raw(yytext+1);
+ size_t pos = raw.size() - 1;
+ for (; pos > 0; --pos) {
+ char c = raw[pos];
+ if (c == 'u') {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Overflow unicode escape in " + std::string(yytext),
+ pos + 1);
+}
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 726 "agent_lexer.ll"
+{ return AgentParser::make_LSQUARE_BRACKET(driver.loc_); }
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 727 "agent_lexer.ll"
+{ return AgentParser::make_RSQUARE_BRACKET(driver.loc_); }
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 728 "agent_lexer.ll"
+{ return AgentParser::make_LCURLY_BRACKET(driver.loc_); }
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 729 "agent_lexer.ll"
+{ return AgentParser::make_RCURLY_BRACKET(driver.loc_); }
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 730 "agent_lexer.ll"
+{ return AgentParser::make_COMMA(driver.loc_); }
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 731 "agent_lexer.ll"
+{ return AgentParser::make_COLON(driver.loc_); }
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 733 "agent_lexer.ll"
+{
+ /* An integer was found. */
+ std::string tmp(yytext);
+ int64_t integer = 0;
+ try {
+ /* In substring we want to use negative values (e.g. -1).
+ In enterprise-id we need to use values up to 0xffffffff.
+ To cover both of those use cases, we need at least
+ int64_t. */
+ integer = boost::lexical_cast<int64_t>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to an integer.");
+ }
+
+ /* The parser needs the string form as double conversion is no lossless */
+ return AgentParser::make_INTEGER(integer, driver.loc_);
+}
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 751 "agent_lexer.ll"
+{
+ /* A floating point was found. */
+ std::string tmp(yytext);
+ double fp = 0.0;
+ try {
+ fp = boost::lexical_cast<double>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to a floating point.");
+ }
+
+ return AgentParser::make_FLOAT(fp, driver.loc_);
+}
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 764 "agent_lexer.ll"
+{
+ string tmp(yytext);
+ return AgentParser::make_BOOLEAN(tmp == "true", driver.loc_);
+}
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 769 "agent_lexer.ll"
+{
+ return AgentParser::make_NULL_TYPE(driver.loc_);
+}
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 773 "agent_lexer.ll"
+driver.error (driver.loc_, "JSON true reserved keyword is lower case only");
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 775 "agent_lexer.ll"
+driver.error (driver.loc_, "JSON false reserved keyword is lower case only");
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 777 "agent_lexer.ll"
+driver.error (driver.loc_, "JSON null reserved keyword is lower case only");
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 779 "agent_lexer.ll"
+driver.error (driver.loc_, "Invalid character: " + std::string(yytext));
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+#line 781 "agent_lexer.ll"
+{
+ if (driver.states_.empty()) {
+ return AgentParser::make_END(driver.loc_);
+ }
+ driver.loc_ = driver.locs_.back();
+ driver.locs_.pop_back();
+ driver.file_ = driver.files_.back();
+ driver.files_.pop_back();
+ if (driver.sfile_) {
+ fclose(driver.sfile_);
+ driver.sfile_ = 0;
+ }
+ if (!driver.sfiles_.empty()) {
+ driver.sfile_ = driver.sfiles_.back();
+ driver.sfiles_.pop_back();
+ }
+ agent__delete_buffer(YY_CURRENT_BUFFER);
+ agent__switch_to_buffer(driver.states_.back());
+ driver.states_.pop_back();
+
+ BEGIN(DIR_EXIT);
+}
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 804 "agent_lexer.ll"
+ECHO;
+ YY_BREAK
+#line 2904 "agent_lexer.cc"
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+/* %if-c-only */
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+/* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of yylex */
+/* %ok-for-header */
+
+/* %if-c++-only */
+/* %not-for-header */
+/* %ok-for-header */
+
+/* %endif */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+/* %if-c-only */
+static int yy_get_next_buffer (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ char *source = (yytext_ptr);
+ int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1);
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc( (void *) b->yy_ch_buf,
+ (yy_size_t) (b->yy_buf_size + 2) );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = NULL;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ /* "- 2" to take care of EOB's */
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+/* %if-c-only */
+/* %not-for-header */
+ static yy_state_type yy_get_previous_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ yy_state_type yy_current_state;
+ char *yy_cp;
+
+/* %% [15.0] code to get the start state into yy_current_state goes here */
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+/* %% [16.0] code to find the next state goes here */
+ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 408 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+/* %if-c-only */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ int yy_is_jam;
+ /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */
+ char *yy_cp = (yy_c_buf_p);
+
+ YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 408 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ yy_is_jam = (yy_current_state == 407);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+/* %if-c-only */
+
+/* %endif */
+#endif
+
+/* %if-c-only */
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (int) ((yy_c_buf_p) - (yytext_ptr));
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return 0;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+/* %% [19.0] update BOL and yylineno */
+
+ return c;
+}
+/* %if-c-only */
+#endif /* ifndef YY_NO_INPUT */
+/* %endif */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+/* %if-c-only */
+ void yyrestart (FILE * input_file )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+ }
+
+ yy_init_buffer( YY_CURRENT_BUFFER, input_file );
+ yy_load_buffer_state( );
+}
+
+/* %if-c++-only */
+/* %endif */
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+/* %if-c-only */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/* %if-c-only */
+static void yy_load_buffer_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+/* %if-c-only */
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+/* %if-c-only */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+}
+
+/* %if-c++-only */
+/* %endif */
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+/* %if-c-only */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree( (void *) b->yy_ch_buf );
+
+ yyfree( (void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+/* %if-c-only */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer( b );
+
+/* %if-c-only */
+ b->yy_input_file = file;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+/* %if-c-only */
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+/* %if-c-only */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/* %if-c-or-c++ */
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+/* %if-c-only */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+/* %endif */
+
+/* %if-c-or-c++ */
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+/* %if-c-only */
+void yypop_buffer_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+/* %endif */
+
+/* %if-c-or-c++ */
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+/* %if-c-only */
+static void yyensure_buffer_stack (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ yy_size_t num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return NULL;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr )
+{
+
+ return yy_scan_bytes( yystr, (int) strlen(yystr) );
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = (yy_size_t) (_yybytes_len + 2);
+ buf = (char *) yyalloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+/* %endif */
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+/* %if-c-only */
+static void yynoreturn yy_fatal_error (const char* msg )
+{
+ fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/* %if-c-only */
+/* %if-reentrant */
+/* %endif */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/* %if-reentrant */
+/* %endif */
+
+/** Set the current line number.
+ * @param _line_number line number
+ *
+ */
+void yyset_lineno (int _line_number )
+{
+
+ yylineno = _line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * _in_str )
+{
+ yyin = _in_str ;
+}
+
+void yyset_out (FILE * _out_str )
+{
+ yyout = _out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int _bdebug )
+{
+ yy_flex_debug = _bdebug ;
+}
+
+/* %endif */
+
+/* %if-reentrant */
+/* %if-bison-bridge */
+/* %endif */
+/* %endif if-c-only */
+
+/* %if-c-only */
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = NULL;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = NULL;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = NULL;
+ yyout = NULL;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+/* %endif */
+
+/* %if-c-only SNIP! this currently causes conflicts with the c++ scanner */
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+/* %if-reentrant */
+/* %endif */
+ return 0;
+}
+/* %endif */
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n )
+{
+
+ int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s )
+{
+ int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return malloc(size);
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return realloc(ptr, size);
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+/* %if-tables-serialization definitions */
+/* %define-yytables The name for this specific scanner's tables. */
+#define YYTABLES_NAME "yytables"
+/* %endif */
+
+/* %ok-for-header */
+
+#line 804 "agent_lexer.ll"
+
+
+using namespace isc::dhcp;
+
+void
+ParserContext::scanStringBegin(const std::string& str, ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = "<string>";
+ sfile_ = 0;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+ buffer = agent__scan_bytes(str.c_str(), str.size());
+ if (!buffer) {
+ fatal("cannot scan string");
+ /* fatal() throws an exception so this can't be reached */
+ }
+}
+
+void
+ParserContext::scanFileBegin(FILE * f,
+ const std::string& filename,
+ ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = filename;
+ sfile_ = f;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+
+ /* See agent_lexer.cc header for available definitions */
+ buffer = agent__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal("cannot scan file " + filename);
+ }
+ agent__switch_to_buffer(buffer);
+}
+
+void
+ParserContext::scanEnd() {
+ if (sfile_)
+ fclose(sfile_);
+ sfile_ = 0;
+ static_cast<void>(agent_lex_destroy());
+ /* Close files */
+ while (!sfiles_.empty()) {
+ FILE* f = sfiles_.back();
+ if (f) {
+ fclose(f);
+ }
+ sfiles_.pop_back();
+ }
+ /* Delete states */
+ while (!states_.empty()) {
+ agent__delete_buffer(states_.back());
+ states_.pop_back();
+ }
+}
+
+void
+ParserContext::includeFile(const std::string& filename) {
+ if (states_.size() > 10) {
+ fatal("Too many nested include.");
+ }
+
+ FILE* f = fopen(filename.c_str(), "r");
+ if (!f) {
+ fatal("Can't open include file " + filename);
+ }
+ if (sfile_) {
+ sfiles_.push_back(sfile_);
+ }
+ sfile_ = f;
+ states_.push_back(YY_CURRENT_BUFFER);
+ YY_BUFFER_STATE buffer;
+ buffer = agent__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal( "Can't scan include file " + filename);
+ }
+ agent__switch_to_buffer(buffer);
+ files_.push_back(file_);
+ file_ = filename;
+ locs_.push_back(loc_);
+ loc_.initialize(&file_);
+
+ BEGIN(INITIAL);
+}
+
+namespace {
+/** To avoid unused function error */
+class Dummy {
+ /* cppcheck-suppress unusedPrivateFunction */
+ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); }
+};
+}
+#endif /* !__clang_analyzer__ */
+
diff --git a/src/bin/agent/agent_lexer.ll b/src/bin/agent/agent_lexer.ll
new file mode 100644
index 0000000..9b0ce1b
--- /dev/null
+++ b/src/bin/agent/agent_lexer.ll
@@ -0,0 +1,904 @@
+/* Copyright (C) 2017-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/. */
+
+%{ /* -*- C++ -*- */
+
+/* Generated files do not make clang static analyser so happy */
+#ifndef __clang_analyzer__
+
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <string>
+#include <agent/parser_context.h>
+#include <asiolink/io_address.h>
+#include <boost/lexical_cast.hpp>
+#include <exceptions/exceptions.h>
+#include <cc/dhcp_config_error.h>
+
+/* Please avoid C++ style comments (// ... eol) as they break flex 2.6.2 */
+
+/* Work around an incompatibility in flex (at least versions
+ 2.5.31 through 2.5.33): it generates code that does
+ not conform to C89. See Debian bug 333231
+ <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */
+# undef yywrap
+# define yywrap() 1
+
+namespace {
+
+bool start_token_flag = false;
+
+isc::agent::ParserContext::ParserType start_token_value;
+unsigned int comment_start_line = 0;
+
+using namespace isc;
+using isc::agent::AgentParser;
+
+};
+
+/* To avoid the call to exit... oops! */
+#define YY_FATAL_ERROR(msg) isc::agent::ParserContext::fatal(msg)
+%}
+
+/* noyywrap disables automatic rewinding for the next file to parse. Since we
+ always parse only a single string, there's no need to do any wraps. And
+ using yywrap requires linking with -lfl, which provides the default yywrap
+ implementation that always returns 1 anyway. */
+%option noyywrap
+
+/* nounput simplifies the lexer, by removing support for putting a character
+ back into the input stream. We never use such capability anyway. */
+%option nounput
+
+/* batch means that we'll never use the generated lexer interactively. */
+%option batch
+
+/* avoid to get static global variables to remain with C++. */
+/* in last resort %option reentrant */
+
+/* Enables debug mode. To see the debug messages, one needs to also set
+ yy_flex_debug to 1, then the debug messages will be printed on stderr. */
+%option debug
+
+/* I have no idea what this option does, except it was specified in the bison
+ examples and Postgres folks added it to remove gcc 4.3 warnings. Let's
+ be on the safe side and keep it. */
+%option noinput
+
+%x COMMENT
+%x DIR_ENTER DIR_INCLUDE DIR_EXIT
+
+/* These are not token expressions yet, just convenience expressions that
+ can be used during actual token definitions. Note some can match
+ incorrect inputs (e.g., IP addresses) which must be checked. */
+int \-?[0-9]+
+blank [ \t\r]
+
+UnicodeEscapeSequence u[0-9A-Fa-f]{4}
+JSONEscapeCharacter ["\\/bfnrt]
+JSONEscapeSequence {JSONEscapeCharacter}|{UnicodeEscapeSequence}
+JSONStandardCharacter [^\x00-\x1f"\\]
+JSONStringCharacter {JSONStandardCharacter}|\\{JSONEscapeSequence}
+JSONString \"{JSONStringCharacter}*\"
+
+/* for errors */
+
+BadUnicodeEscapeSequence u[0-9A-Fa-f]{0,3}[^0-9A-Fa-f"]
+BadJSONEscapeSequence [^"\\/bfnrtu]|{BadUnicodeEscapeSequence}
+ControlCharacter [\x00-\x1f]
+ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
+
+%{
+/* This code run each time a pattern is matched. It updates the location
+ by moving it ahead by yyleng bytes. yyleng specifies the length of the
+ currently matched token. */
+#define YY_USER_ACTION driver.loc_.columns(yyleng);
+%}
+
+%%
+
+%{
+ /* This part of the code is copied over to the verbatim to the top
+ of the generated yylex function. Explanation:
+ http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html */
+
+ /* Code run each time yylex is called. */
+ driver.loc_.step();
+
+ /* We currently have 3 points of entries defined:
+ START_JSON - which expects any valid JSON
+ START_AGENT - which expects full configuration (with outer map and Control-agent
+ object in it.
+ START_SUB_AGENT - which expects only content of the Control-agent, this is
+ primarily useful for testing. */
+ if (start_token_flag) {
+ start_token_flag = false;
+ switch (start_token_value) {
+ case ParserContext::PARSER_JSON:
+ default:
+ return isc::agent::AgentParser::make_START_JSON(driver.loc_);
+ case ParserContext::PARSER_AGENT:
+ return isc::agent::AgentParser::make_START_AGENT(driver.loc_);
+ case ParserContext::PARSER_SUB_AGENT:
+ return isc::agent::AgentParser::make_START_SUB_AGENT(driver.loc_);
+ }
+ }
+%}
+
+#.* ;
+
+"//"(.*) ;
+
+"/*" {
+ BEGIN(COMMENT);
+ comment_start_line = driver.loc_.end.line;;
+}
+
+<COMMENT>"*/" BEGIN(INITIAL);
+<COMMENT>. ;
+<COMMENT><<EOF>> {
+ isc_throw(ParseError, "Comment not closed. (/* in line " << comment_start_line);
+}
+
+"<?" BEGIN(DIR_ENTER);
+<DIR_ENTER>"include" BEGIN(DIR_INCLUDE);
+<DIR_INCLUDE>\"([^\"\n])+\" {
+ /* Include directive. */
+
+ /* Extract the filename. */
+ std::string tmp(yytext+1);
+ tmp.resize(tmp.size() - 1);
+
+ driver.includeFile(tmp);
+}
+<DIR_ENTER,DIR_INCLUDE,DIR_EXIT><<EOF>> {
+ isc_throw(ParseError, "Directive not closed.");
+}
+<DIR_EXIT>"?>" BEGIN(INITIAL);
+
+
+<*>{blank}+ {
+ /* Ok, we found a with space. Let's ignore it and update loc variable. */
+ driver.loc_.step();
+}
+
+<*>[\n]+ {
+ /* Newline found. Let's update the location and continue. */
+ driver.loc_.lines(yyleng);
+ driver.loc_.step();
+}
+
+
+\"Control-agent\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONFIG:
+ return AgentParser::make_CONTROL_AGENT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("Control-agent", driver.loc_);
+ }
+}
+
+\"http-host\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_HTTP_HOST(driver.loc_);
+ default:
+ return AgentParser::make_STRING("http-host", driver.loc_);
+ }
+}
+
+\"http-port\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_HTTP_PORT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("http-port", driver.loc_);
+ }
+}
+
+\"user-context\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ case ParserContext::AUTHENTICATION:
+ case ParserContext::CLIENTS:
+ case ParserContext::SERVER:
+ case ParserContext::LOGGERS:
+ return AgentParser::make_USER_CONTEXT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("user-context", driver.loc_);
+ }
+}
+
+\"comment\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ case ParserContext::AUTHENTICATION:
+ case ParserContext::CLIENTS:
+ case ParserContext::SERVER:
+ case ParserContext::LOGGERS:
+ return AgentParser::make_COMMENT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("comment", driver.loc_);
+ }
+}
+
+\"authentication\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_AUTHENTICATION(driver.loc_);
+ default:
+ return AgentParser::make_STRING("authentication", driver.loc_);
+ }
+}
+
+\"type\" {
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_TYPE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("type", driver.loc_);
+ }
+}
+
+\"basic\" {
+ switch(driver.ctx_) {
+ case ParserContext::AUTH_TYPE:
+ return AgentParser::make_BASIC(driver.loc_);
+ default:
+ return AgentParser::make_STRING("basic", driver.loc_);
+ }
+}
+
+\"realm\" {
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_REALM(driver.loc_);
+ default:
+ return AgentParser::make_STRING("realm", driver.loc_);
+ }
+}
+
+\"directory\" {
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_DIRECTORY(driver.loc_);
+ default:
+ return AgentParser::make_STRING("directory", driver.loc_);
+ }
+}
+
+\"clients\" {
+ switch(driver.ctx_) {
+ case ParserContext::AUTHENTICATION:
+ return AgentParser::make_CLIENTS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("clients", driver.loc_);
+ }
+}
+
+\"user\" {
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_USER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("user", driver.loc_);
+ }
+}
+
+\"user-file\" {
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_USER_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("user-file", driver.loc_);
+ }
+}
+
+\"password\" {
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_PASSWORD(driver.loc_);
+ default:
+ return AgentParser::make_STRING("password", driver.loc_);
+ }
+}
+
+\"password-file\" {
+ switch(driver.ctx_) {
+ case ParserContext::CLIENTS:
+ return AgentParser::make_PASSWORD_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("password-file", driver.loc_);
+ }
+}
+
+\"trust-anchor\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_TRUST_ANCHOR(driver.loc_);
+ default:
+ return AgentParser::make_STRING("trust-anchor", driver.loc_);
+ }
+}
+
+\"cert-file\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_CERT_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("cert-file", driver.loc_);
+ }
+}
+
+\"key-file\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_KEY_FILE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("key-file", driver.loc_);
+ }
+}
+
+\"cert-required\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_CERT_REQUIRED(driver.loc_);
+ default:
+ return AgentParser::make_STRING("cert-required", driver.loc_);
+ }
+}
+
+\"control-sockets\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_CONTROL_SOCKETS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("control-sockets", driver.loc_);
+ }
+}
+
+\"dhcp4\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKETS:
+ return AgentParser::make_DHCP4_SERVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("dhcp4", driver.loc_);
+ }
+}
+
+\"dhcp6\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKETS:
+ return AgentParser::make_DHCP6_SERVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("dhcp6", driver.loc_);
+ }
+}
+
+\"d2\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKETS:
+ return AgentParser::make_D2_SERVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("d2", driver.loc_);
+ }
+}
+
+\"socket-name\" {
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return AgentParser::make_SOCKET_NAME(driver.loc_);
+ default:
+ return AgentParser::make_STRING("socket-name", driver.loc_);
+ }
+}
+
+\"socket-type\" {
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return AgentParser::make_SOCKET_TYPE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("socket-type", driver.loc_);
+ }
+}
+
+\"unix\" {
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return AgentParser::make_UNIX(driver.loc_);
+ default:
+ return AgentParser::make_STRING("unix", driver.loc_);
+ }
+}
+
+\"hooks-libraries\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_HOOKS_LIBRARIES(driver.loc_);
+ default:
+ return AgentParser::make_STRING("hooks-libraries", driver.loc_);
+ }
+}
+
+\"library\" {
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return AgentParser::make_LIBRARY(driver.loc_);
+ default:
+ return AgentParser::make_STRING("library", driver.loc_);
+ }
+}
+
+\"parameters\" {
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return AgentParser::make_PARAMETERS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("parameters", driver.loc_);
+ }
+}
+
+\"loggers\" {
+ switch(driver.ctx_) {
+ case ParserContext::AGENT:
+ return AgentParser::make_LOGGERS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("loggers", driver.loc_);
+ }
+}
+
+\"name\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_NAME(driver.loc_);
+ default:
+ return AgentParser::make_STRING("name", driver.loc_);
+ }
+}
+
+\"output_options\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_OUTPUT_OPTIONS(driver.loc_);
+ default:
+ return AgentParser::make_STRING("output_options", driver.loc_);
+ }
+}
+
+\"output\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_OUTPUT(driver.loc_);
+ default:
+ return AgentParser::make_STRING("output", driver.loc_);
+ }
+}
+
+\"flush\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_FLUSH(driver.loc_);
+ default:
+ return AgentParser::make_STRING("flush", driver.loc_);
+ }
+}
+
+\"maxsize\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_MAXSIZE(driver.loc_);
+ default:
+ return AgentParser::make_STRING("maxsize", driver.loc_);
+ }
+}
+
+\"maxver\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_MAXVER(driver.loc_);
+ default:
+ return AgentParser::make_STRING("maxver", driver.loc_);
+ }
+}
+
+\"pattern\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return AgentParser::make_PATTERN(driver.loc_);
+ default:
+ return AgentParser::make_STRING("pattern", driver.loc_);
+ }
+}
+
+\"debuglevel\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_DEBUGLEVEL(driver.loc_);
+ default:
+ return AgentParser::make_STRING("debuglevel", driver.loc_);
+ }
+}
+
+\"severity\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return AgentParser::make_SEVERITY(driver.loc_);
+ default:
+ return AgentParser::make_STRING("severity", driver.loc_);
+ }
+}
+
+{JSONString} {
+ /* A string has been matched. It contains the actual string and single quotes.
+ We need to get those quotes out of the way and just use its content, e.g.
+ for 'foo' we should get foo */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ raw.resize(len);
+ std::string decoded;
+ decoded.reserve(len);
+ for (size_t pos = 0; pos < len; ++pos) {
+ int b = 0;
+ char c = raw[pos];
+ switch (c) {
+ case '"':
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad quote in \"" + raw + "\"");
+ break;
+ case '\\':
+ ++pos;
+ if (pos >= len) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Overflow escape in \"" + raw + "\"");
+ }
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ decoded.push_back(c);
+ break;
+ case 'b':
+ decoded.push_back('\b');
+ break;
+ case 'f':
+ decoded.push_back('\f');
+ break;
+ case 'n':
+ decoded.push_back('\n');
+ break;
+ case 'r':
+ decoded.push_back('\r');
+ break;
+ case 't':
+ decoded.push_back('\t');
+ break;
+ case 'u':
+ /* support only \u0000 to \u00ff */
+ ++pos;
+ if (pos + 4 > len) {
+ /* impossible condition */
+ driver.error(driver.loc_,
+ "Overflow unicode escape in \"" + raw + "\"");
+ }
+ if ((raw[pos] != '0') || (raw[pos + 1] != '0')) {
+ driver.error(driver.loc_,
+ "Unsupported unicode escape in \"" + raw + "\"",
+ pos + 1);
+ }
+ pos += 2;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b = (c - '0') << 4;
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b = (c - 'A' + 10) << 4;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b = (c - 'a' + 10) << 4;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ pos++;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b |= c - '0';
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b |= c - 'A' + 10;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b |= c - 'a' + 10;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ decoded.push_back(static_cast<char>(b & 0xff));
+ break;
+ default:
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad escape in \"" + raw + "\"");
+ }
+ break;
+ default:
+ if ((c >= 0) && (c < 0x20)) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Invalid control in \"" + raw + "\"");
+ }
+ decoded.push_back(c);
+ }
+ }
+
+ return AgentParser::make_STRING(decoded, driver.loc_);
+}
+
+\"{JSONStringCharacter}*{ControlCharacter}{ControlCharacterFill}*\" {
+ /* Bad string with a forbidden control character inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ for (; pos < len; ++pos) {
+ char c = raw[pos];
+ if ((c >= 0) && (c < 0x20)) {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Invalid control in " + std::string(yytext),
+ pos + 1);
+}
+
+\"{JSONStringCharacter}*\\{BadJSONEscapeSequence}[^"]*\" {
+ /* Bad string with a bad escape inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ bool found = false;
+ for (; pos < len; ++pos) {
+ if (found) {
+ break;
+ }
+ char c = raw[pos];
+ if (c == '\\') {
+ ++pos;
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ break;
+ case 'u':
+ if ((pos + 4 > len) ||
+ !std::isxdigit(raw[pos + 1]) ||
+ !std::isxdigit(raw[pos + 2]) ||
+ !std::isxdigit(raw[pos + 3]) ||
+ !std::isxdigit(raw[pos + 4])) {
+ found = true;
+ }
+ break;
+ default:
+ found = true;
+ break;
+ }
+ }
+ }
+ /* The rule stops on the first " including on \" so add ... in this case */
+ std::string trailer = "";
+ if (raw[len - 1] == '\\') {
+ trailer = "...";
+ }
+ driver.error(driver.loc_,
+ "Bad escape in " + std::string(yytext) + trailer,
+ pos);
+}
+
+\"{JSONStringCharacter}*\\\" {
+ /* Bad string with an open escape at the end */
+ std::string raw(yytext+1);
+ driver.error(driver.loc_,
+ "Overflow escape in " + std::string(yytext),
+ raw.size() + 1);
+}
+
+\"{JSONStringCharacter}*\\u[0-9A-Fa-f]{0,3}\" {
+ /* Bad string with an open unicode escape at the end */
+ std::string raw(yytext+1);
+ size_t pos = raw.size() - 1;
+ for (; pos > 0; --pos) {
+ char c = raw[pos];
+ if (c == 'u') {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Overflow unicode escape in " + std::string(yytext),
+ pos + 1);
+}
+
+"[" { return AgentParser::make_LSQUARE_BRACKET(driver.loc_); }
+"]" { return AgentParser::make_RSQUARE_BRACKET(driver.loc_); }
+"{" { return AgentParser::make_LCURLY_BRACKET(driver.loc_); }
+"}" { return AgentParser::make_RCURLY_BRACKET(driver.loc_); }
+"," { return AgentParser::make_COMMA(driver.loc_); }
+":" { return AgentParser::make_COLON(driver.loc_); }
+
+{int} {
+ /* An integer was found. */
+ std::string tmp(yytext);
+ int64_t integer = 0;
+ try {
+ /* In substring we want to use negative values (e.g. -1).
+ In enterprise-id we need to use values up to 0xffffffff.
+ To cover both of those use cases, we need at least
+ int64_t. */
+ integer = boost::lexical_cast<int64_t>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to an integer.");
+ }
+
+ /* The parser needs the string form as double conversion is no lossless */
+ return AgentParser::make_INTEGER(integer, driver.loc_);
+}
+
+[-+]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)? {
+ /* A floating point was found. */
+ std::string tmp(yytext);
+ double fp = 0.0;
+ try {
+ fp = boost::lexical_cast<double>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to a floating point.");
+ }
+
+ return AgentParser::make_FLOAT(fp, driver.loc_);
+}
+
+true|false {
+ string tmp(yytext);
+ return AgentParser::make_BOOLEAN(tmp == "true", driver.loc_);
+}
+
+null {
+ return AgentParser::make_NULL_TYPE(driver.loc_);
+}
+
+(?i:true) driver.error (driver.loc_, "JSON true reserved keyword is lower case only");
+
+(?i:false) driver.error (driver.loc_, "JSON false reserved keyword is lower case only");
+
+(?i:null) driver.error (driver.loc_, "JSON null reserved keyword is lower case only");
+
+<*>. driver.error (driver.loc_, "Invalid character: " + std::string(yytext));
+
+<<EOF>> {
+ if (driver.states_.empty()) {
+ return AgentParser::make_END(driver.loc_);
+ }
+ driver.loc_ = driver.locs_.back();
+ driver.locs_.pop_back();
+ driver.file_ = driver.files_.back();
+ driver.files_.pop_back();
+ if (driver.sfile_) {
+ fclose(driver.sfile_);
+ driver.sfile_ = 0;
+ }
+ if (!driver.sfiles_.empty()) {
+ driver.sfile_ = driver.sfiles_.back();
+ driver.sfiles_.pop_back();
+ }
+ agent__delete_buffer(YY_CURRENT_BUFFER);
+ agent__switch_to_buffer(driver.states_.back());
+ driver.states_.pop_back();
+
+ BEGIN(DIR_EXIT);
+}
+
+%%
+
+using namespace isc::dhcp;
+
+void
+ParserContext::scanStringBegin(const std::string& str, ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = "<string>";
+ sfile_ = 0;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+ buffer = agent__scan_bytes(str.c_str(), str.size());
+ if (!buffer) {
+ fatal("cannot scan string");
+ /* fatal() throws an exception so this can't be reached */
+ }
+}
+
+void
+ParserContext::scanFileBegin(FILE * f,
+ const std::string& filename,
+ ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = filename;
+ sfile_ = f;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+
+ /* See agent_lexer.cc header for available definitions */
+ buffer = agent__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal("cannot scan file " + filename);
+ }
+ agent__switch_to_buffer(buffer);
+}
+
+void
+ParserContext::scanEnd() {
+ if (sfile_)
+ fclose(sfile_);
+ sfile_ = 0;
+ static_cast<void>(agent_lex_destroy());
+ /* Close files */
+ while (!sfiles_.empty()) {
+ FILE* f = sfiles_.back();
+ if (f) {
+ fclose(f);
+ }
+ sfiles_.pop_back();
+ }
+ /* Delete states */
+ while (!states_.empty()) {
+ agent__delete_buffer(states_.back());
+ states_.pop_back();
+ }
+}
+
+void
+ParserContext::includeFile(const std::string& filename) {
+ if (states_.size() > 10) {
+ fatal("Too many nested include.");
+ }
+
+ FILE* f = fopen(filename.c_str(), "r");
+ if (!f) {
+ fatal("Can't open include file " + filename);
+ }
+ if (sfile_) {
+ sfiles_.push_back(sfile_);
+ }
+ sfile_ = f;
+ states_.push_back(YY_CURRENT_BUFFER);
+ YY_BUFFER_STATE buffer;
+ buffer = agent__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal( "Can't scan include file " + filename);
+ }
+ agent__switch_to_buffer(buffer);
+ files_.push_back(file_);
+ file_ = filename;
+ locs_.push_back(loc_);
+ loc_.initialize(&file_);
+
+ BEGIN(INITIAL);
+}
+
+namespace {
+/** To avoid unused function error */
+class Dummy {
+ /* cppcheck-suppress unusedPrivateFunction */
+ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); }
+};
+}
+#endif /* !__clang_analyzer__ */
diff --git a/src/bin/agent/agent_parser.cc b/src/bin/agent/agent_parser.cc
new file mode 100644
index 0000000..29b82be
--- /dev/null
+++ b/src/bin/agent/agent_parser.cc
@@ -0,0 +1,2552 @@
+// A Bison parser, made by GNU Bison 3.8.2.
+
+// Skeleton implementation for Bison LALR(1) parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton. Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+// especially those whose name start with YY_ or yy_. They are
+// private implementation details that can be changed or removed.
+
+
+// Take the name prefix into account.
+#define yylex agent_lex
+
+
+
+#include "agent_parser.h"
+
+
+// Unqualified %code blocks.
+#line 33 "agent_parser.yy"
+
+#include <agent/parser_context.h>
+
+#line 52 "agent_parser.cc"
+
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> // FIXME: INFRINGES ON USER NAME SPACE.
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+
+// Whether we are compiled with exception support.
+#ifndef YY_EXCEPTIONS
+# if defined __GNUC__ && !defined __EXCEPTIONS
+# define YY_EXCEPTIONS 0
+# else
+# define YY_EXCEPTIONS 1
+# endif
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K].location)
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+# ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).begin = YYRHSLOC (Rhs, 1).begin; \
+ (Current).end = YYRHSLOC (Rhs, N).end; \
+ } \
+ else \
+ { \
+ (Current).begin = (Current).end = YYRHSLOC (Rhs, 0).end; \
+ } \
+ while (false)
+# endif
+
+
+// Enable debugging if requested.
+#if AGENT_DEBUG
+
+// A pseudo ostream that takes yydebug_ into account.
+# define YYCDEBUG if (yydebug_) (*yycdebug_)
+
+# define YY_SYMBOL_PRINT(Title, Symbol) \
+ do { \
+ if (yydebug_) \
+ { \
+ *yycdebug_ << Title << ' '; \
+ yy_print_ (*yycdebug_, Symbol); \
+ *yycdebug_ << '\n'; \
+ } \
+ } while (false)
+
+# define YY_REDUCE_PRINT(Rule) \
+ do { \
+ if (yydebug_) \
+ yy_reduce_print_ (Rule); \
+ } while (false)
+
+# define YY_STACK_PRINT() \
+ do { \
+ if (yydebug_) \
+ yy_stack_print_ (); \
+ } while (false)
+
+#else // !AGENT_DEBUG
+
+# define YYCDEBUG if (false) std::cerr
+# define YY_SYMBOL_PRINT(Title, Symbol) YY_USE (Symbol)
+# define YY_REDUCE_PRINT(Rule) static_cast<void> (0)
+# define YY_STACK_PRINT() static_cast<void> (0)
+
+#endif // !AGENT_DEBUG
+
+#define yyerrok (yyerrstatus_ = 0)
+#define yyclearin (yyla.clear ())
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+#define YYRECOVERING() (!!yyerrstatus_)
+
+#line 14 "agent_parser.yy"
+namespace isc { namespace agent {
+#line 145 "agent_parser.cc"
+
+ /// Build a parser object.
+ AgentParser::AgentParser (isc::agent::ParserContext& ctx_yyarg)
+#if AGENT_DEBUG
+ : yydebug_ (false),
+ yycdebug_ (&std::cerr),
+#else
+ :
+#endif
+ ctx (ctx_yyarg)
+ {}
+
+ AgentParser::~AgentParser ()
+ {}
+
+ AgentParser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW
+ {}
+
+ /*---------.
+ | symbol. |
+ `---------*/
+
+
+
+ // by_state.
+ AgentParser::by_state::by_state () YY_NOEXCEPT
+ : state (empty_state)
+ {}
+
+ AgentParser::by_state::by_state (const by_state& that) YY_NOEXCEPT
+ : state (that.state)
+ {}
+
+ void
+ AgentParser::by_state::clear () YY_NOEXCEPT
+ {
+ state = empty_state;
+ }
+
+ void
+ AgentParser::by_state::move (by_state& that)
+ {
+ state = that.state;
+ that.clear ();
+ }
+
+ AgentParser::by_state::by_state (state_type s) YY_NOEXCEPT
+ : state (s)
+ {}
+
+ AgentParser::symbol_kind_type
+ AgentParser::by_state::kind () const YY_NOEXCEPT
+ {
+ if (state == empty_state)
+ return symbol_kind::S_YYEMPTY;
+ else
+ return YY_CAST (symbol_kind_type, yystos_[+state]);
+ }
+
+ AgentParser::stack_symbol_type::stack_symbol_type ()
+ {}
+
+ AgentParser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that)
+ : super_type (YY_MOVE (that.state), YY_MOVE (that.location))
+ {
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.YY_MOVE_OR_COPY< ElementPtr > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.YY_MOVE_OR_COPY< bool > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.YY_MOVE_OR_COPY< double > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.YY_MOVE_OR_COPY< int64_t > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.YY_MOVE_OR_COPY< std::string > (YY_MOVE (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+#if 201103L <= YY_CPLUSPLUS
+ // that is emptied.
+ that.state = empty_state;
+#endif
+ }
+
+ AgentParser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that)
+ : super_type (s, YY_MOVE (that.location))
+ {
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.move< ElementPtr > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (YY_MOVE (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+ // that is emptied.
+ that.kind_ = symbol_kind::S_YYEMPTY;
+ }
+
+#if YY_CPLUSPLUS < 201103L
+ AgentParser::stack_symbol_type&
+ AgentParser::stack_symbol_type::operator= (const stack_symbol_type& that)
+ {
+ state = that.state;
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.copy< ElementPtr > (that.value);
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.copy< bool > (that.value);
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.copy< double > (that.value);
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.copy< int64_t > (that.value);
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.copy< std::string > (that.value);
+ break;
+
+ default:
+ break;
+ }
+
+ location = that.location;
+ return *this;
+ }
+
+ AgentParser::stack_symbol_type&
+ AgentParser::stack_symbol_type::operator= (stack_symbol_type& that)
+ {
+ state = that.state;
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.move< ElementPtr > (that.value);
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (that.value);
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (that.value);
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (that.value);
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (that.value);
+ break;
+
+ default:
+ break;
+ }
+
+ location = that.location;
+ // that is emptied.
+ that.state = empty_state;
+ return *this;
+ }
+#endif
+
+ template <typename Base>
+ void
+ AgentParser::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const
+ {
+ if (yymsg)
+ YY_SYMBOL_PRINT (yymsg, yysym);
+ }
+
+#if AGENT_DEBUG
+ template <typename Base>
+ void
+ AgentParser::yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const
+ {
+ std::ostream& yyoutput = yyo;
+ YY_USE (yyoutput);
+ if (yysym.empty ())
+ yyo << "empty symbol";
+ else
+ {
+ symbol_kind_type yykind = yysym.kind ();
+ yyo << (yykind < YYNTOKENS ? "token" : "nterm")
+ << ' ' << yysym.name () << " ("
+ << yysym.location << ": ";
+ switch (yykind)
+ {
+ case symbol_kind::S_STRING: // "constant string"
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < std::string > (); }
+#line 388 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < int64_t > (); }
+#line 394 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < double > (); }
+#line 400 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < bool > (); }
+#line 406 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_value: // value
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 412 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_map_value: // map_value
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 418 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_socket_type_value: // socket_type_value
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 424 "agent_parser.cc"
+ break;
+
+ case symbol_kind::S_auth_type_value: // auth_type_value
+#line 116 "agent_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 430 "agent_parser.cc"
+ break;
+
+ default:
+ break;
+ }
+ yyo << ')';
+ }
+ }
+#endif
+
+ void
+ AgentParser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym)
+ {
+ if (m)
+ YY_SYMBOL_PRINT (m, sym);
+ yystack_.push (YY_MOVE (sym));
+ }
+
+ void
+ AgentParser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym)
+ {
+#if 201103L <= YY_CPLUSPLUS
+ yypush_ (m, stack_symbol_type (s, std::move (sym)));
+#else
+ stack_symbol_type ss (s, sym);
+ yypush_ (m, ss);
+#endif
+ }
+
+ void
+ AgentParser::yypop_ (int n) YY_NOEXCEPT
+ {
+ yystack_.pop (n);
+ }
+
+#if AGENT_DEBUG
+ std::ostream&
+ AgentParser::debug_stream () const
+ {
+ return *yycdebug_;
+ }
+
+ void
+ AgentParser::set_debug_stream (std::ostream& o)
+ {
+ yycdebug_ = &o;
+ }
+
+
+ AgentParser::debug_level_type
+ AgentParser::debug_level () const
+ {
+ return yydebug_;
+ }
+
+ void
+ AgentParser::set_debug_level (debug_level_type l)
+ {
+ yydebug_ = l;
+ }
+#endif // AGENT_DEBUG
+
+ AgentParser::state_type
+ AgentParser::yy_lr_goto_state_ (state_type yystate, int yysym)
+ {
+ int yyr = yypgoto_[yysym - YYNTOKENS] + yystate;
+ if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate)
+ return yytable_[yyr];
+ else
+ return yydefgoto_[yysym - YYNTOKENS];
+ }
+
+ bool
+ AgentParser::yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT
+ {
+ return yyvalue == yypact_ninf_;
+ }
+
+ bool
+ AgentParser::yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT
+ {
+ return yyvalue == yytable_ninf_;
+ }
+
+ int
+ AgentParser::operator() ()
+ {
+ return parse ();
+ }
+
+ int
+ AgentParser::parse ()
+ {
+ int yyn;
+ /// Length of the RHS of the rule being reduced.
+ int yylen = 0;
+
+ // Error handling.
+ int yynerrs_ = 0;
+ int yyerrstatus_ = 0;
+
+ /// The lookahead symbol.
+ symbol_type yyla;
+
+ /// The locations where the error started and ended.
+ stack_symbol_type yyerror_range[3];
+
+ /// The return value of parse ().
+ int yyresult;
+
+#if YY_EXCEPTIONS
+ try
+#endif // YY_EXCEPTIONS
+ {
+ YYCDEBUG << "Starting parse\n";
+
+
+ /* Initialize the stack. The initial state will be set in
+ yynewstate, since the latter expects the semantical and the
+ location values to have been already stored, initialize these
+ stacks with a primary value. */
+ yystack_.clear ();
+ yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla));
+
+ /*-----------------------------------------------.
+ | yynewstate -- push a new symbol on the stack. |
+ `-----------------------------------------------*/
+ yynewstate:
+ YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n';
+ YY_STACK_PRINT ();
+
+ // Accept?
+ if (yystack_[0].state == yyfinal_)
+ YYACCEPT;
+
+ goto yybackup;
+
+
+ /*-----------.
+ | yybackup. |
+ `-----------*/
+ yybackup:
+ // Try to take a decision without lookahead.
+ yyn = yypact_[+yystack_[0].state];
+ if (yy_pact_value_is_default_ (yyn))
+ goto yydefault;
+
+ // Read a lookahead token.
+ if (yyla.empty ())
+ {
+ YYCDEBUG << "Reading a token\n";
+#if YY_EXCEPTIONS
+ try
+#endif // YY_EXCEPTIONS
+ {
+ symbol_type yylookahead (yylex (ctx));
+ yyla.move (yylookahead);
+ }
+#if YY_EXCEPTIONS
+ catch (const syntax_error& yyexc)
+ {
+ YYCDEBUG << "Caught exception: " << yyexc.what() << '\n';
+ error (yyexc);
+ goto yyerrlab1;
+ }
+#endif // YY_EXCEPTIONS
+ }
+ YY_SYMBOL_PRINT ("Next token is", yyla);
+
+ if (yyla.kind () == symbol_kind::S_YYerror)
+ {
+ // The scanner already issued an error message, process directly
+ // to error recovery. But do not keep the error token as
+ // lookahead, it is too special and may lead us to an endless
+ // loop in error recovery. */
+ yyla.kind_ = symbol_kind::S_YYUNDEF;
+ goto yyerrlab1;
+ }
+
+ /* If the proper action on seeing token YYLA.TYPE is to reduce or
+ to detect an error, take that action. */
+ yyn += yyla.kind ();
+ if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ())
+ {
+ goto yydefault;
+ }
+
+ // Reduce or error.
+ yyn = yytable_[yyn];
+ if (yyn <= 0)
+ {
+ if (yy_table_value_is_error_ (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ // Count tokens shifted since error; after three, turn off error status.
+ if (yyerrstatus_)
+ --yyerrstatus_;
+
+ // Shift the lookahead token.
+ yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla));
+ goto yynewstate;
+
+
+ /*-----------------------------------------------------------.
+ | yydefault -- do the default action for the current state. |
+ `-----------------------------------------------------------*/
+ yydefault:
+ yyn = yydefact_[+yystack_[0].state];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+ /*-----------------------------.
+ | yyreduce -- do a reduction. |
+ `-----------------------------*/
+ yyreduce:
+ yylen = yyr2_[yyn];
+ {
+ stack_symbol_type yylhs;
+ yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]);
+ /* Variants are always initialized to an empty instance of the
+ correct type. The default '$$ = $1' action is NOT applied
+ when using variants. */
+ switch (yyr1_[yyn])
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ yylhs.value.emplace< ElementPtr > ();
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ yylhs.value.emplace< bool > ();
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ yylhs.value.emplace< double > ();
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ yylhs.value.emplace< int64_t > ();
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ yylhs.value.emplace< std::string > ();
+ break;
+
+ default:
+ break;
+ }
+
+
+ // Default location.
+ {
+ stack_type::slice range (yystack_, yylen);
+ YYLLOC_DEFAULT (yylhs.location, range, yylen);
+ yyerror_range[1].location = yylhs.location;
+ }
+
+ // Perform the reduction.
+ YY_REDUCE_PRINT (yyn);
+#if YY_EXCEPTIONS
+ try
+#endif // YY_EXCEPTIONS
+ {
+ switch (yyn)
+ {
+ case 2: // $@1: %empty
+#line 127 "agent_parser.yy"
+ { ctx.ctx_ = ctx.NO_KEYWORDS; }
+#line 706 "agent_parser.cc"
+ break;
+
+ case 4: // $@2: %empty
+#line 128 "agent_parser.yy"
+ { ctx.ctx_ = ctx.CONFIG; }
+#line 712 "agent_parser.cc"
+ break;
+
+ case 6: // $@3: %empty
+#line 129 "agent_parser.yy"
+ { ctx.ctx_ = ctx.AGENT; }
+#line 718 "agent_parser.cc"
+ break;
+
+ case 8: // $@4: %empty
+#line 137 "agent_parser.yy"
+ {
+ // Parse the Control-agent map
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(m);
+}
+#line 728 "agent_parser.cc"
+ break;
+
+ case 9: // sub_agent: "{" $@4 global_params "}"
+#line 141 "agent_parser.yy"
+ {
+ // parsing completed
+}
+#line 736 "agent_parser.cc"
+ break;
+
+ case 10: // json: value
+#line 148 "agent_parser.yy"
+ {
+ // Push back the JSON value on the stack
+ ctx.stack_.push_back(yystack_[0].value.as < ElementPtr > ());
+}
+#line 745 "agent_parser.cc"
+ break;
+
+ case 11: // value: "integer"
+#line 154 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); }
+#line 751 "agent_parser.cc"
+ break;
+
+ case 12: // value: "floating point"
+#line 155 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new DoubleElement(yystack_[0].value.as < double > (), ctx.loc2pos(yystack_[0].location))); }
+#line 757 "agent_parser.cc"
+ break;
+
+ case 13: // value: "boolean"
+#line 156 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location))); }
+#line 763 "agent_parser.cc"
+ break;
+
+ case 14: // value: "constant string"
+#line 157 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); }
+#line 769 "agent_parser.cc"
+ break;
+
+ case 15: // value: "null"
+#line 158 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new NullElement(ctx.loc2pos(yystack_[0].location))); }
+#line 775 "agent_parser.cc"
+ break;
+
+ case 16: // value: map
+#line 159 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
+#line 781 "agent_parser.cc"
+ break;
+
+ case 17: // value: list_generic
+#line 160 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
+#line 787 "agent_parser.cc"
+ break;
+
+ case 18: // $@5: %empty
+#line 164 "agent_parser.yy"
+ {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(m);
+}
+#line 798 "agent_parser.cc"
+ break;
+
+ case 19: // map: "{" $@5 map_content "}"
+#line 169 "agent_parser.yy"
+ {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+}
+#line 808 "agent_parser.cc"
+ break;
+
+ case 20: // map_value: map
+#line 175 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
+#line 814 "agent_parser.cc"
+ break;
+
+ case 23: // not_empty_map: "constant string" ":" value
+#line 189 "agent_parser.yy"
+ {
+ // map containing a single entry
+ ctx.unique(yystack_[2].value.as < std::string > (), ctx.loc2pos(yystack_[2].location));
+ ctx.stack_.back()->set(yystack_[2].value.as < std::string > (), yystack_[0].value.as < ElementPtr > ());
+ }
+#line 824 "agent_parser.cc"
+ break;
+
+ case 24: // not_empty_map: not_empty_map "," "constant string" ":" value
+#line 194 "agent_parser.yy"
+ {
+ // map consisting of a shorter map followed by
+ // comma and string:value
+ ctx.unique(yystack_[2].value.as < std::string > (), ctx.loc2pos(yystack_[2].location));
+ ctx.stack_.back()->set(yystack_[2].value.as < std::string > (), yystack_[0].value.as < ElementPtr > ());
+ }
+#line 835 "agent_parser.cc"
+ break;
+
+ case 25: // not_empty_map: not_empty_map ","
+#line 200 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 843 "agent_parser.cc"
+ break;
+
+ case 26: // $@6: %empty
+#line 205 "agent_parser.yy"
+ {
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(l);
+}
+#line 852 "agent_parser.cc"
+ break;
+
+ case 27: // list_generic: "[" $@6 list_content "]"
+#line 208 "agent_parser.yy"
+ {
+}
+#line 859 "agent_parser.cc"
+ break;
+
+ case 30: // not_empty_list: value
+#line 215 "agent_parser.yy"
+ {
+ // List consisting of a single element.
+ ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ());
+ }
+#line 868 "agent_parser.cc"
+ break;
+
+ case 31: // not_empty_list: not_empty_list "," value
+#line 219 "agent_parser.yy"
+ {
+ // List ending with , and a value.
+ ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ());
+ }
+#line 877 "agent_parser.cc"
+ break;
+
+ case 32: // not_empty_list: not_empty_list ","
+#line 223 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 885 "agent_parser.cc"
+ break;
+
+ case 33: // unknown_map_entry: "constant string" ":"
+#line 235 "agent_parser.yy"
+ {
+ const std::string& where = ctx.contextName();
+ const std::string& keyword = yystack_[1].value.as < std::string > ();
+ error(yystack_[1].location,
+ "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
+}
+#line 896 "agent_parser.cc"
+ break;
+
+ case 34: // $@7: %empty
+#line 243 "agent_parser.yy"
+ {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(m);
+}
+#line 907 "agent_parser.cc"
+ break;
+
+ case 35: // agent_syntax_map: "{" $@7 global_object "}"
+#line 248 "agent_parser.yy"
+ {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+}
+#line 917 "agent_parser.cc"
+ break;
+
+ case 36: // $@8: %empty
+#line 255 "agent_parser.yy"
+ {
+ // Let's create a MapElement that will represent it, add it to the
+ // top level map (that's already on the stack) and put the new map
+ // on the stack as well, so child elements will be able to add
+ // themselves to it.
+ ctx.unique("Control-agent", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("Control-agent", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.AGENT);
+}
+#line 933 "agent_parser.cc"
+ break;
+
+ case 37: // global_object: "Control-agent" $@8 ":" "{" global_params "}"
+#line 265 "agent_parser.yy"
+ {
+ // Ok, we're done with parsing control-agent. Let's take the map
+ // off the stack.
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 944 "agent_parser.cc"
+ break;
+
+ case 39: // global_object_comma: global_object ","
+#line 274 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+}
+#line 952 "agent_parser.cc"
+ break;
+
+ case 42: // global_params: global_params ","
+#line 280 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 960 "agent_parser.cc"
+ break;
+
+ case 56: // $@9: %empty
+#line 302 "agent_parser.yy"
+ {
+ ctx.unique("http-host", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 969 "agent_parser.cc"
+ break;
+
+ case 57: // http_host: "http-host" $@9 ":" "constant string"
+#line 305 "agent_parser.yy"
+ {
+ ElementPtr host(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("http-host", host);
+ ctx.leave();
+}
+#line 979 "agent_parser.cc"
+ break;
+
+ case 58: // http_port: "http-port" ":" "integer"
+#line 311 "agent_parser.yy"
+ {
+ ctx.unique("http-port", ctx.loc2pos(yystack_[2].location));
+ ElementPtr prf(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("http-port", prf);
+}
+#line 989 "agent_parser.cc"
+ break;
+
+ case 59: // $@10: %empty
+#line 317 "agent_parser.yy"
+ {
+ ctx.unique("trust-anchor", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 998 "agent_parser.cc"
+ break;
+
+ case 60: // trust_anchor: "trust-anchor" $@10 ":" "constant string"
+#line 320 "agent_parser.yy"
+ {
+ ElementPtr ca(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("trust-anchor", ca);
+ ctx.leave();
+}
+#line 1008 "agent_parser.cc"
+ break;
+
+ case 61: // $@11: %empty
+#line 326 "agent_parser.yy"
+ {
+ ctx.unique("cert-file", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1017 "agent_parser.cc"
+ break;
+
+ case 62: // cert_file: "cert-file" $@11 ":" "constant string"
+#line 329 "agent_parser.yy"
+ {
+ ElementPtr cert(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("cert-file", cert);
+ ctx.leave();
+}
+#line 1027 "agent_parser.cc"
+ break;
+
+ case 63: // $@12: %empty
+#line 335 "agent_parser.yy"
+ {
+ ctx.unique("key-file", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1036 "agent_parser.cc"
+ break;
+
+ case 64: // key_file: "key-file" $@12 ":" "constant string"
+#line 338 "agent_parser.yy"
+ {
+ ElementPtr key(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("key-file", key);
+ ctx.leave();
+}
+#line 1046 "agent_parser.cc"
+ break;
+
+ case 65: // cert_required: "cert-required" ":" "boolean"
+#line 344 "agent_parser.yy"
+ {
+ ctx.unique("cert-required", ctx.loc2pos(yystack_[2].location));
+ ElementPtr req(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("cert-required", req);
+}
+#line 1056 "agent_parser.cc"
+ break;
+
+ case 66: // $@13: %empty
+#line 350 "agent_parser.yy"
+ {
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1064 "agent_parser.cc"
+ break;
+
+ case 67: // user_context: "user-context" $@13 ":" map_value
+#line 352 "agent_parser.yy"
+ {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context = yystack_[0].value.as < ElementPtr > ();
+ ConstElementPtr old = parent->get("user-context");
+
+ // Handle already existing user context
+ if (old) {
+ // Check if it was a comment or a duplicate
+ if ((old->size() != 1) || !old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context entries (previous at "
+ << old->getPosition().str() << ")";
+ error(yystack_[3].location, msg.str());
+ }
+ // Merge the comment
+ user_context->set("comment", old->get("comment"));
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+}
+#line 1091 "agent_parser.cc"
+ break;
+
+ case 68: // $@14: %empty
+#line 375 "agent_parser.yy"
+ {
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1099 "agent_parser.cc"
+ break;
+
+ case 69: // comment: "comment" $@14 ":" "constant string"
+#line 377 "agent_parser.yy"
+ {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context(new MapElement(ctx.loc2pos(yystack_[3].location)));
+ ElementPtr comment(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ user_context->set("comment", comment);
+
+ // Handle already existing user context
+ ConstElementPtr old = parent->get("user-context");
+ if (old) {
+ // Check for duplicate comment
+ if (old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context/comment entries (previous at "
+ << old->getPosition().str() << ")";
+ error(yystack_[3].location, msg.str());
+ }
+ // Merge the user context in the comment
+ merge(user_context, old);
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+}
+#line 1128 "agent_parser.cc"
+ break;
+
+ case 70: // $@15: %empty
+#line 403 "agent_parser.yy"
+ {
+ ctx.unique("hooks-libraries", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("hooks-libraries", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.HOOKS_LIBRARIES);
+}
+#line 1140 "agent_parser.cc"
+ break;
+
+ case 71: // hooks_libraries: "hooks-libraries" $@15 ":" "[" hooks_libraries_list "]"
+#line 409 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1149 "agent_parser.cc"
+ break;
+
+ case 76: // not_empty_hooks_libraries_list: not_empty_hooks_libraries_list ","
+#line 420 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1157 "agent_parser.cc"
+ break;
+
+ case 77: // $@16: %empty
+#line 425 "agent_parser.yy"
+ {
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+}
+#line 1167 "agent_parser.cc"
+ break;
+
+ case 78: // hooks_library: "{" $@16 hooks_params "}"
+#line 429 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1175 "agent_parser.cc"
+ break;
+
+ case 81: // hooks_params: hooks_params ","
+#line 435 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1183 "agent_parser.cc"
+ break;
+
+ case 85: // $@17: %empty
+#line 445 "agent_parser.yy"
+ {
+ ctx.unique("library", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1192 "agent_parser.cc"
+ break;
+
+ case 86: // library: "library" $@17 ":" "constant string"
+#line 448 "agent_parser.yy"
+ {
+ ElementPtr lib(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("library", lib);
+ ctx.leave();
+}
+#line 1202 "agent_parser.cc"
+ break;
+
+ case 87: // $@18: %empty
+#line 454 "agent_parser.yy"
+ {
+ ctx.unique("parameters", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1211 "agent_parser.cc"
+ break;
+
+ case 88: // parameters: "parameters" $@18 ":" map_value
+#line 457 "agent_parser.yy"
+ {
+ ctx.stack_.back()->set("parameters", yystack_[0].value.as < ElementPtr > ());
+ ctx.leave();
+}
+#line 1220 "agent_parser.cc"
+ break;
+
+ case 89: // $@19: %empty
+#line 465 "agent_parser.yy"
+ {
+ ctx.unique("control-sockets", ctx.loc2pos(yystack_[2].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[2].location)));
+ ctx.stack_.back()->set("control-sockets", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.CONTROL_SOCKETS);
+}
+#line 1232 "agent_parser.cc"
+ break;
+
+ case 90: // control_sockets: "control-sockets" ":" "{" $@19 control_sockets_params "}"
+#line 471 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1241 "agent_parser.cc"
+ break;
+
+ case 93: // control_sockets_params: control_sockets_params ","
+#line 481 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1249 "agent_parser.cc"
+ break;
+
+ case 98: // $@20: %empty
+#line 495 "agent_parser.yy"
+ {
+ ctx.unique("dhcp4", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("dhcp4", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1261 "agent_parser.cc"
+ break;
+
+ case 99: // dhcp4_server_socket: "dhcp4" $@20 ":" "{" control_socket_params "}"
+#line 501 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1270 "agent_parser.cc"
+ break;
+
+ case 100: // $@21: %empty
+#line 507 "agent_parser.yy"
+ {
+ ctx.unique("dhcp6", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("dhcp6", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1282 "agent_parser.cc"
+ break;
+
+ case 101: // dhcp6_server_socket: "dhcp6" $@21 ":" "{" control_socket_params "}"
+#line 513 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1291 "agent_parser.cc"
+ break;
+
+ case 102: // $@22: %empty
+#line 519 "agent_parser.yy"
+ {
+ ctx.unique("d2", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("d2", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1303 "agent_parser.cc"
+ break;
+
+ case 103: // d2_server_socket: "d2" $@22 ":" "{" control_socket_params "}"
+#line 525 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1312 "agent_parser.cc"
+ break;
+
+ case 106: // control_socket_params: control_socket_params ","
+#line 533 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1320 "agent_parser.cc"
+ break;
+
+ case 112: // $@23: %empty
+#line 547 "agent_parser.yy"
+ {
+ ctx.unique("socket-name", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1329 "agent_parser.cc"
+ break;
+
+ case 113: // socket_name: "socket-name" $@23 ":" "constant string"
+#line 550 "agent_parser.yy"
+ {
+ ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("socket-name", name);
+ ctx.leave();
+}
+#line 1339 "agent_parser.cc"
+ break;
+
+ case 114: // $@24: %empty
+#line 557 "agent_parser.yy"
+ {
+ ctx.unique("socket-type", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.SOCKET_TYPE);
+}
+#line 1348 "agent_parser.cc"
+ break;
+
+ case 115: // socket_type: "socket-type" $@24 ":" socket_type_value
+#line 560 "agent_parser.yy"
+ {
+ ctx.stack_.back()->set("socket-type", yystack_[0].value.as < ElementPtr > ());
+ ctx.leave();
+}
+#line 1357 "agent_parser.cc"
+ break;
+
+ case 116: // socket_type_value: "unix"
+#line 566 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("unix", ctx.loc2pos(yystack_[0].location))); }
+#line 1363 "agent_parser.cc"
+ break;
+
+ case 117: // $@25: %empty
+#line 573 "agent_parser.yy"
+ {
+ ctx.unique("authentication", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("authentication", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.AUTHENTICATION);
+}
+#line 1375 "agent_parser.cc"
+ break;
+
+ case 118: // authentication: "authentication" $@25 ":" "{" auth_params "}"
+#line 579 "agent_parser.yy"
+ {
+ // The type parameter is required
+ ctx.require("type", ctx.loc2pos(yystack_[2].location), ctx.loc2pos(yystack_[0].location));
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1386 "agent_parser.cc"
+ break;
+
+ case 121: // auth_params: auth_params ","
+#line 588 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1394 "agent_parser.cc"
+ break;
+
+ case 129: // $@26: %empty
+#line 602 "agent_parser.yy"
+ {
+ ctx.unique("type", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.AUTH_TYPE);
+}
+#line 1403 "agent_parser.cc"
+ break;
+
+ case 130: // auth_type: "type" $@26 ":" auth_type_value
+#line 605 "agent_parser.yy"
+ {
+ ctx.stack_.back()->set("type", yystack_[0].value.as < ElementPtr > ());
+ ctx.leave();
+}
+#line 1412 "agent_parser.cc"
+ break;
+
+ case 131: // auth_type_value: "basic"
+#line 610 "agent_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("basic", ctx.loc2pos(yystack_[0].location))); }
+#line 1418 "agent_parser.cc"
+ break;
+
+ case 132: // $@27: %empty
+#line 613 "agent_parser.yy"
+ {
+ ctx.unique("realm", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1427 "agent_parser.cc"
+ break;
+
+ case 133: // realm: "realm" $@27 ":" "constant string"
+#line 616 "agent_parser.yy"
+ {
+ ElementPtr realm(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("realm", realm);
+ ctx.leave();
+}
+#line 1437 "agent_parser.cc"
+ break;
+
+ case 134: // $@28: %empty
+#line 622 "agent_parser.yy"
+ {
+ ctx.unique("directory", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1446 "agent_parser.cc"
+ break;
+
+ case 135: // directory: "directory" $@28 ":" "constant string"
+#line 625 "agent_parser.yy"
+ {
+ ElementPtr directory(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("directory", directory);
+ ctx.leave();
+}
+#line 1456 "agent_parser.cc"
+ break;
+
+ case 136: // $@29: %empty
+#line 631 "agent_parser.yy"
+ {
+ ctx.unique("clients", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("clients", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.CLIENTS);
+}
+#line 1468 "agent_parser.cc"
+ break;
+
+ case 137: // clients: "clients" $@29 ":" "[" clients_list "]"
+#line 637 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1477 "agent_parser.cc"
+ break;
+
+ case 142: // not_empty_clients_list: not_empty_clients_list ","
+#line 648 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1485 "agent_parser.cc"
+ break;
+
+ case 143: // $@30: %empty
+#line 653 "agent_parser.yy"
+ {
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+}
+#line 1495 "agent_parser.cc"
+ break;
+
+ case 144: // basic_auth: "{" $@30 clients_params "}"
+#line 657 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1503 "agent_parser.cc"
+ break;
+
+ case 147: // clients_params: clients_params ","
+#line 663 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1511 "agent_parser.cc"
+ break;
+
+ case 155: // $@31: %empty
+#line 677 "agent_parser.yy"
+ {
+ ctx.unique("user", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1520 "agent_parser.cc"
+ break;
+
+ case 156: // user: "user" $@31 ":" "constant string"
+#line 680 "agent_parser.yy"
+ {
+ ElementPtr user(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("user", user);
+ ctx.leave();
+}
+#line 1530 "agent_parser.cc"
+ break;
+
+ case 157: // $@32: %empty
+#line 686 "agent_parser.yy"
+ {
+ ctx.unique("user-file", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1539 "agent_parser.cc"
+ break;
+
+ case 158: // user_file: "user-file" $@32 ":" "constant string"
+#line 689 "agent_parser.yy"
+ {
+ ElementPtr user(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("user-file", user);
+ ctx.leave();
+}
+#line 1549 "agent_parser.cc"
+ break;
+
+ case 159: // $@33: %empty
+#line 695 "agent_parser.yy"
+ {
+ ctx.unique("password", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1558 "agent_parser.cc"
+ break;
+
+ case 160: // password: "password" $@33 ":" "constant string"
+#line 698 "agent_parser.yy"
+ {
+ ElementPtr password(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("password", password);
+ ctx.leave();
+}
+#line 1568 "agent_parser.cc"
+ break;
+
+ case 161: // $@34: %empty
+#line 704 "agent_parser.yy"
+ {
+ ctx.unique("password-file", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1577 "agent_parser.cc"
+ break;
+
+ case 162: // password_file: "password-file" $@34 ":" "constant string"
+#line 707 "agent_parser.yy"
+ {
+ ElementPtr password(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("password-file", password);
+ ctx.leave();
+}
+#line 1587 "agent_parser.cc"
+ break;
+
+ case 163: // $@35: %empty
+#line 717 "agent_parser.yy"
+ {
+ ctx.unique("loggers", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("loggers", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.LOGGERS);
+}
+#line 1599 "agent_parser.cc"
+ break;
+
+ case 164: // loggers: "loggers" $@35 ":" "[" loggers_entries "]"
+#line 723 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1608 "agent_parser.cc"
+ break;
+
+ case 167: // loggers_entries: loggers_entries ","
+#line 732 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1616 "agent_parser.cc"
+ break;
+
+ case 168: // $@36: %empty
+#line 738 "agent_parser.yy"
+ {
+ ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(l);
+ ctx.stack_.push_back(l);
+}
+#line 1626 "agent_parser.cc"
+ break;
+
+ case 169: // logger_entry: "{" $@36 logger_params "}"
+#line 742 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1634 "agent_parser.cc"
+ break;
+
+ case 172: // logger_params: logger_params ","
+#line 748 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1642 "agent_parser.cc"
+ break;
+
+ case 180: // $@37: %empty
+#line 762 "agent_parser.yy"
+ {
+ ctx.unique("name", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1651 "agent_parser.cc"
+ break;
+
+ case 181: // name: "name" $@37 ":" "constant string"
+#line 765 "agent_parser.yy"
+ {
+ ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("name", name);
+ ctx.leave();
+}
+#line 1661 "agent_parser.cc"
+ break;
+
+ case 182: // debuglevel: "debuglevel" ":" "integer"
+#line 771 "agent_parser.yy"
+ {
+ ctx.unique("debuglevel", ctx.loc2pos(yystack_[2].location));
+ ElementPtr dl(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("debuglevel", dl);
+}
+#line 1671 "agent_parser.cc"
+ break;
+
+ case 183: // $@38: %empty
+#line 777 "agent_parser.yy"
+ {
+ ctx.unique("severity", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1680 "agent_parser.cc"
+ break;
+
+ case 184: // severity: "severity" $@38 ":" "constant string"
+#line 780 "agent_parser.yy"
+ {
+ ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("severity", sev);
+ ctx.leave();
+}
+#line 1690 "agent_parser.cc"
+ break;
+
+ case 185: // $@39: %empty
+#line 786 "agent_parser.yy"
+ {
+ ctx.unique("output_options", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("output_options", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.OUTPUT_OPTIONS);
+}
+#line 1702 "agent_parser.cc"
+ break;
+
+ case 186: // output_options_list: "output_options" $@39 ":" "[" output_options_list_content "]"
+#line 792 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1711 "agent_parser.cc"
+ break;
+
+ case 189: // output_options_list_content: output_options_list_content ","
+#line 799 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1719 "agent_parser.cc"
+ break;
+
+ case 190: // $@40: %empty
+#line 804 "agent_parser.yy"
+ {
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+}
+#line 1729 "agent_parser.cc"
+ break;
+
+ case 191: // output_entry: "{" $@40 output_params_list "}"
+#line 808 "agent_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1737 "agent_parser.cc"
+ break;
+
+ case 194: // output_params_list: output_params_list ","
+#line 814 "agent_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1745 "agent_parser.cc"
+ break;
+
+ case 200: // $@41: %empty
+#line 826 "agent_parser.yy"
+ {
+ ctx.unique("output", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1754 "agent_parser.cc"
+ break;
+
+ case 201: // output: "output" $@41 ":" "constant string"
+#line 829 "agent_parser.yy"
+ {
+ ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("output", sev);
+ ctx.leave();
+}
+#line 1764 "agent_parser.cc"
+ break;
+
+ case 202: // flush: "flush" ":" "boolean"
+#line 835 "agent_parser.yy"
+ {
+ ctx.unique("flush", ctx.loc2pos(yystack_[2].location));
+ ElementPtr flush(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("flush", flush);
+}
+#line 1774 "agent_parser.cc"
+ break;
+
+ case 203: // maxsize: "maxsize" ":" "integer"
+#line 841 "agent_parser.yy"
+ {
+ ctx.unique("maxsize", ctx.loc2pos(yystack_[2].location));
+ ElementPtr maxsize(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("maxsize", maxsize);
+}
+#line 1784 "agent_parser.cc"
+ break;
+
+ case 204: // maxver: "maxver" ":" "integer"
+#line 847 "agent_parser.yy"
+ {
+ ctx.unique("maxver", ctx.loc2pos(yystack_[2].location));
+ ElementPtr maxver(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("maxver", maxver);
+}
+#line 1794 "agent_parser.cc"
+ break;
+
+ case 205: // $@42: %empty
+#line 853 "agent_parser.yy"
+ {
+ ctx.unique("pattern", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1803 "agent_parser.cc"
+ break;
+
+ case 206: // pattern: "pattern" $@42 ":" "constant string"
+#line 856 "agent_parser.yy"
+ {
+ ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("pattern", sev);
+ ctx.leave();
+}
+#line 1813 "agent_parser.cc"
+ break;
+
+
+#line 1817 "agent_parser.cc"
+
+ default:
+ break;
+ }
+ }
+#if YY_EXCEPTIONS
+ catch (const syntax_error& yyexc)
+ {
+ YYCDEBUG << "Caught exception: " << yyexc.what() << '\n';
+ error (yyexc);
+ YYERROR;
+ }
+#endif // YY_EXCEPTIONS
+ YY_SYMBOL_PRINT ("-> $$ =", yylhs);
+ yypop_ (yylen);
+ yylen = 0;
+
+ // Shift the result of the reduction.
+ yypush_ (YY_NULLPTR, YY_MOVE (yylhs));
+ }
+ goto yynewstate;
+
+
+ /*--------------------------------------.
+ | yyerrlab -- here on detecting error. |
+ `--------------------------------------*/
+ yyerrlab:
+ // If not already recovering from an error, report this error.
+ if (!yyerrstatus_)
+ {
+ ++yynerrs_;
+ context yyctx (*this, yyla);
+ std::string msg = yysyntax_error_ (yyctx);
+ error (yyla.location, YY_MOVE (msg));
+ }
+
+
+ yyerror_range[1].location = yyla.location;
+ if (yyerrstatus_ == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ // Return failure if at end of input.
+ if (yyla.kind () == symbol_kind::S_YYEOF)
+ YYABORT;
+ else if (!yyla.empty ())
+ {
+ yy_destroy_ ("Error: discarding", yyla);
+ yyla.clear ();
+ }
+ }
+
+ // Else will try to reuse lookahead token after shifting the error token.
+ goto yyerrlab1;
+
+
+ /*---------------------------------------------------.
+ | yyerrorlab -- error raised explicitly by YYERROR. |
+ `---------------------------------------------------*/
+ yyerrorlab:
+ /* Pacify compilers when the user code never invokes YYERROR and
+ the label yyerrorlab therefore never appears in user code. */
+ if (false)
+ YYERROR;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ yypop_ (yylen);
+ yylen = 0;
+ YY_STACK_PRINT ();
+ goto yyerrlab1;
+
+
+ /*-------------------------------------------------------------.
+ | yyerrlab1 -- common code for both syntax error and YYERROR. |
+ `-------------------------------------------------------------*/
+ yyerrlab1:
+ yyerrstatus_ = 3; // Each real token shifted decrements this.
+ // Pop stack until we find a state that shifts the error token.
+ for (;;)
+ {
+ yyn = yypact_[+yystack_[0].state];
+ if (!yy_pact_value_is_default_ (yyn))
+ {
+ yyn += symbol_kind::S_YYerror;
+ if (0 <= yyn && yyn <= yylast_
+ && yycheck_[yyn] == symbol_kind::S_YYerror)
+ {
+ yyn = yytable_[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ // Pop the current state because it cannot handle the error token.
+ if (yystack_.size () == 1)
+ YYABORT;
+
+ yyerror_range[1].location = yystack_[0].location;
+ yy_destroy_ ("Error: popping", yystack_[0]);
+ yypop_ ();
+ YY_STACK_PRINT ();
+ }
+ {
+ stack_symbol_type error_token;
+
+ yyerror_range[2].location = yyla.location;
+ YYLLOC_DEFAULT (error_token.location, yyerror_range, 2);
+
+ // Shift the error token.
+ error_token.state = state_type (yyn);
+ yypush_ ("Shifting", YY_MOVE (error_token));
+ }
+ goto yynewstate;
+
+
+ /*-------------------------------------.
+ | yyacceptlab -- YYACCEPT comes here. |
+ `-------------------------------------*/
+ yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+
+ /*-----------------------------------.
+ | yyabortlab -- YYABORT comes here. |
+ `-----------------------------------*/
+ yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+
+ /*-----------------------------------------------------.
+ | yyreturn -- parsing is finished, return the result. |
+ `-----------------------------------------------------*/
+ yyreturn:
+ if (!yyla.empty ())
+ yy_destroy_ ("Cleanup: discarding lookahead", yyla);
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ yypop_ (yylen);
+ YY_STACK_PRINT ();
+ while (1 < yystack_.size ())
+ {
+ yy_destroy_ ("Cleanup: popping", yystack_[0]);
+ yypop_ ();
+ }
+
+ return yyresult;
+ }
+#if YY_EXCEPTIONS
+ catch (...)
+ {
+ YYCDEBUG << "Exception caught: cleaning lookahead and stack\n";
+ // Do not try to display the values of the reclaimed symbols,
+ // as their printers might throw an exception.
+ if (!yyla.empty ())
+ yy_destroy_ (YY_NULLPTR, yyla);
+
+ while (1 < yystack_.size ())
+ {
+ yy_destroy_ (YY_NULLPTR, yystack_[0]);
+ yypop_ ();
+ }
+ throw;
+ }
+#endif // YY_EXCEPTIONS
+ }
+
+ void
+ AgentParser::error (const syntax_error& yyexc)
+ {
+ error (yyexc.location, yyexc.what ());
+ }
+
+ /* Return YYSTR after stripping away unnecessary quotes and
+ backslashes, so that it's suitable for yyerror. The heuristic is
+ that double-quoting is unnecessary unless the string contains an
+ apostrophe, a comma, or backslash (other than backslash-backslash).
+ YYSTR is taken from yytname. */
+ std::string
+ AgentParser::yytnamerr_ (const char *yystr)
+ {
+ if (*yystr == '"')
+ {
+ std::string yyr;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ else
+ goto append;
+
+ append:
+ default:
+ yyr += *yyp;
+ break;
+
+ case '"':
+ return yyr;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ return yystr;
+ }
+
+ std::string
+ AgentParser::symbol_name (symbol_kind_type yysymbol)
+ {
+ return yytnamerr_ (yytname_[yysymbol]);
+ }
+
+
+
+ // AgentParser::context.
+ AgentParser::context::context (const AgentParser& yyparser, const symbol_type& yyla)
+ : yyparser_ (yyparser)
+ , yyla_ (yyla)
+ {}
+
+ int
+ AgentParser::context::expected_tokens (symbol_kind_type yyarg[], int yyargn) const
+ {
+ // Actual number of expected tokens
+ int yycount = 0;
+
+ const int yyn = yypact_[+yyparser_.yystack_[0].state];
+ if (!yy_pact_value_is_default_ (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ const int yyxbegin = yyn < 0 ? -yyn : 0;
+ // Stay within bounds of both yycheck and yytname.
+ const int yychecklim = yylast_ - yyn + 1;
+ const int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ for (int yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck_[yyx + yyn] == yyx && yyx != symbol_kind::S_YYerror
+ && !yy_table_value_is_error_ (yytable_[yyx + yyn]))
+ {
+ if (!yyarg)
+ ++yycount;
+ else if (yycount == yyargn)
+ return 0;
+ else
+ yyarg[yycount++] = YY_CAST (symbol_kind_type, yyx);
+ }
+ }
+
+ if (yyarg && yycount == 0 && 0 < yyargn)
+ yyarg[0] = symbol_kind::S_YYEMPTY;
+ return yycount;
+ }
+
+
+
+
+
+
+ int
+ AgentParser::yy_syntax_error_arguments_ (const context& yyctx,
+ symbol_kind_type yyarg[], int yyargn) const
+ {
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yyla) is
+ if this state is a consistent state with a default action.
+ Thus, detecting the absence of a lookahead is sufficient to
+ determine that there is no unexpected or expected token to
+ report. In that case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is
+ a consistent state with a default action. There might have
+ been a previous inconsistent state, consistent state with a
+ non-default action, or user semantic action that manipulated
+ yyla. (However, yyla is currently not documented for users.)
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+
+ if (!yyctx.lookahead ().empty ())
+ {
+ if (yyarg)
+ yyarg[0] = yyctx.token ();
+ int yyn = yyctx.expected_tokens (yyarg ? yyarg + 1 : yyarg, yyargn - 1);
+ return yyn + 1;
+ }
+ return 0;
+ }
+
+ // Generate an error message.
+ std::string
+ AgentParser::yysyntax_error_ (const context& yyctx) const
+ {
+ // Its maximum.
+ enum { YYARGS_MAX = 5 };
+ // Arguments of yyformat.
+ symbol_kind_type yyarg[YYARGS_MAX];
+ int yycount = yy_syntax_error_arguments_ (yyctx, yyarg, YYARGS_MAX);
+
+ char const* yyformat = YY_NULLPTR;
+ switch (yycount)
+ {
+#define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ default: // Avoid compiler warnings.
+ YYCASE_ (0, YY_("syntax error"));
+ YYCASE_ (1, YY_("syntax error, unexpected %s"));
+ YYCASE_ (2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_ (3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_ (4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_ (5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+#undef YYCASE_
+ }
+
+ std::string yyres;
+ // Argument number.
+ std::ptrdiff_t yyi = 0;
+ for (char const* yyp = yyformat; *yyp; ++yyp)
+ if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount)
+ {
+ yyres += symbol_name (yyarg[yyi++]);
+ ++yyp;
+ }
+ else
+ yyres += *yyp;
+ return yyres;
+ }
+
+
+ const short AgentParser::yypact_ninf_ = -136;
+
+ const signed char AgentParser::yytable_ninf_ = -1;
+
+ const short
+ AgentParser::yypact_[] =
+ {
+ 71, -136, -136, -136, 4, 0, 1, 16, -136, -136,
+ -136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
+ -136, -136, -136, -136, 0, -31, 19, -1, -136, 40,
+ 55, 78, 83, 107, -136, 14, -136, -136, 112, -136,
+ -136, -136, -136, -136, -136, 114, 119, -136, -136, 120,
+ -136, 42, -136, -136, -136, -136, -136, -136, -136, -136,
+ -136, -136, -136, -136, -136, -136, 0, 0, -136, 73,
+ 122, -136, -136, 126, 80, 127, 131, 132, 136, 138,
+ 139, 89, 140, 141, 142, -136, -1, -136, -136, -136,
+ 144, 143, 100, -136, 146, 102, 148, 104, 106, 108,
+ -136, -136, 152, 154, -136, 0, -1, -136, -136, -136,
+ -136, 23, -136, -136, -136, -12, 155, 156, -136, 75,
+ -136, -136, -136, -136, -136, -136, -136, 91, -136, -136,
+ -136, -136, -136, -136, -136, -136, -136, 95, -136, -136,
+ -136, -136, -136, 145, 158, -136, -136, 28, -136, -136,
+ 160, 161, 162, 163, 23, -136, 164, 165, 166, -12,
+ -136, -22, -136, 155, 49, 156, -136, 157, 121, 123,
+ 167, -136, 169, 170, 171, -136, -136, -136, -136, 97,
+ -136, -136, -136, -136, -136, -136, 175, -136, -136, -136,
+ -136, 99, -136, -136, -136, -136, -136, -136, -136, -136,
+ -136, -136, 173, 63, 63, 63, 177, 178, -5, -136,
+ 179, 180, 96, 181, 49, -136, -136, 184, 168, -136,
+ -136, -136, -136, -136, -136, 101, -136, -136, -136, 103,
+ 105, 134, 146, -136, 147, 186, -136, 149, -136, 43,
+ -136, 173, 188, 189, 63, -136, -136, -136, -136, -136,
+ -136, 187, -136, -136, -136, -136, -136, -136, -136, -136,
+ 109, -136, -136, -136, -136, -136, -136, 150, 172, -136,
+ -136, 41, -136, 191, 192, 193, 194, 43, -136, -136,
+ -136, -136, 26, 187, -136, 151, 153, 159, 174, -136,
+ -136, 196, 200, 202, -136, 111, -136, -136, -136, -136,
+ -136, -136, -136, -136, -136, -136, -136, 204, 176, 182,
+ 183, 205, 26, -136, 185, -136, -136, -136, 190, -136,
+ -136, -136
+ };
+
+ const unsigned char
+ AgentParser::yydefact_[] =
+ {
+ 0, 2, 4, 6, 0, 0, 0, 0, 1, 26,
+ 18, 15, 14, 11, 12, 13, 3, 10, 16, 17,
+ 34, 5, 8, 7, 28, 21, 0, 0, 30, 0,
+ 29, 0, 0, 22, 36, 0, 38, 56, 0, 66,
+ 68, 117, 59, 61, 63, 0, 0, 70, 163, 0,
+ 55, 0, 40, 43, 44, 45, 46, 47, 48, 53,
+ 54, 51, 50, 49, 52, 27, 32, 0, 19, 25,
+ 0, 39, 35, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 33, 42, 9, 31, 23,
+ 0, 0, 0, 58, 0, 0, 0, 0, 0, 0,
+ 65, 89, 0, 0, 41, 0, 0, 57, 20, 67,
+ 69, 0, 60, 62, 64, 0, 72, 0, 24, 0,
+ 129, 132, 134, 136, 128, 127, 126, 0, 119, 122,
+ 123, 124, 125, 98, 100, 102, 97, 0, 91, 94,
+ 95, 96, 77, 0, 73, 74, 168, 0, 165, 37,
+ 0, 0, 0, 0, 121, 118, 0, 0, 0, 93,
+ 90, 0, 71, 76, 0, 167, 164, 0, 0, 0,
+ 0, 120, 0, 0, 0, 92, 85, 87, 82, 0,
+ 79, 83, 84, 75, 180, 185, 0, 183, 179, 177,
+ 178, 0, 170, 173, 175, 176, 174, 166, 131, 130,
+ 133, 135, 138, 0, 0, 0, 0, 0, 81, 78,
+ 0, 0, 0, 0, 172, 169, 143, 0, 139, 140,
+ 112, 114, 111, 109, 110, 0, 104, 107, 108, 0,
+ 0, 0, 0, 80, 0, 0, 182, 0, 171, 0,
+ 137, 142, 0, 0, 106, 99, 101, 103, 86, 88,
+ 181, 0, 184, 155, 157, 159, 161, 154, 152, 153,
+ 0, 145, 148, 149, 150, 151, 141, 0, 0, 105,
+ 190, 0, 187, 0, 0, 0, 0, 147, 144, 113,
+ 116, 115, 0, 189, 186, 0, 0, 0, 0, 146,
+ 200, 0, 0, 0, 205, 0, 192, 195, 196, 197,
+ 198, 199, 188, 156, 158, 160, 162, 0, 0, 0,
+ 0, 0, 194, 191, 0, 202, 203, 204, 0, 193,
+ 201, 206
+ };
+
+ const short
+ AgentParser::yypgoto_[] =
+ {
+ -136, -136, -136, -136, -136, -136, -136, -136, -18, -91,
+ -136, -17, -136, -136, -136, -136, -136, -136, -27, -136,
+ -136, -136, -136, -136, 110, 124, -136, -136, -136, -136,
+ -136, -136, -136, -136, -136, -136, -26, -136, -25, -136,
+ -136, -136, -136, -136, 54, -136, -136, 10, -136, -136,
+ -136, -136, -136, -136, -136, 60, -136, -136, -136, -136,
+ -136, -136, -135, -24, -136, -136, -136, -136, -136, -136,
+ -136, -136, 67, -136, -136, -136, -136, -136, -136, -136,
+ -136, -136, -136, -136, -19, -136, -136, -54, -136, -136,
+ -136, -136, -136, -136, -136, -136, -136, -136, -136, 59,
+ -136, -136, 11, -136, -136, -136, -136, -136, -136, -136,
+ -136, -56, -136, -136, -84, -136, -136, -136, -136, -136,
+ -136, -136
+ };
+
+ const short
+ AgentParser::yydefgoto_[] =
+ {
+ 0, 4, 5, 6, 7, 23, 27, 16, 17, 18,
+ 25, 109, 32, 33, 19, 24, 29, 30, 222, 21,
+ 26, 35, 70, 36, 51, 52, 53, 73, 54, 55,
+ 78, 56, 79, 57, 80, 58, 223, 75, 224, 76,
+ 61, 83, 143, 144, 145, 161, 179, 180, 181, 206,
+ 182, 207, 62, 115, 137, 138, 139, 156, 140, 157,
+ 141, 158, 225, 226, 227, 242, 228, 243, 281, 63,
+ 77, 127, 128, 129, 150, 199, 130, 151, 131, 152,
+ 132, 153, 217, 218, 219, 239, 260, 261, 262, 273,
+ 263, 274, 264, 275, 265, 276, 64, 84, 147, 148,
+ 164, 191, 192, 193, 210, 194, 195, 213, 196, 211,
+ 271, 272, 282, 295, 296, 297, 307, 298, 299, 300,
+ 301, 311
+ };
+
+ const short
+ AgentParser::yytable_[] =
+ {
+ 50, 59, 60, 108, 8, 9, 28, 10, 20, 11,
+ 37, 38, 39, 40, 41, 176, 177, 71, 133, 134,
+ 135, 31, 72, 22, 42, 43, 44, 45, 46, 34,
+ 49, 165, 176, 177, 166, 47, 39, 40, 48, 120,
+ 49, 121, 122, 123, 283, 86, 65, 284, 88, 89,
+ 87, 49, 12, 13, 14, 15, 39, 40, 66, 50,
+ 59, 60, 39, 40, 253, 254, 255, 256, 290, 229,
+ 230, 291, 292, 293, 294, 49, 39, 40, 86, 50,
+ 59, 60, 67, 149, 124, 125, 126, 118, 136, 184,
+ 185, 68, 186, 187, 154, 49, 220, 221, 159, 155,
+ 208, 49, 214, 160, 244, 209, 244, 215, 244, 245,
+ 69, 246, 277, 247, 312, 49, 74, 278, 81, 313,
+ 1, 2, 3, 82, 85, 90, 91, 124, 125, 126,
+ 92, 94, 136, 93, 178, 95, 96, 188, 189, 190,
+ 97, 108, 98, 99, 100, 102, 103, 101, 105, 236,
+ 106, 162, 107, 10, 110, 111, 112, 116, 113, 117,
+ 114, 163, 142, 146, 167, 168, 169, 170, 172, 173,
+ 174, 241, 202, 200, 198, 201, 203, 204, 205, 212,
+ 216, 231, 232, 234, 235, 237, 248, 188, 189, 190,
+ 240, 251, 267, 268, 270, 285, 286, 287, 288, 250,
+ 308, 252, 279, 303, 309, 304, 310, 280, 314, 318,
+ 104, 305, 257, 258, 259, 249, 119, 183, 233, 175,
+ 269, 171, 266, 289, 197, 238, 306, 302, 319, 0,
+ 0, 315, 0, 0, 0, 316, 317, 320, 0, 0,
+ 0, 0, 321, 0, 0, 0, 0, 0, 0, 0,
+ 257, 258, 259
+ };
+
+ const short
+ AgentParser::yycheck_[] =
+ {
+ 27, 27, 27, 94, 0, 5, 24, 7, 7, 9,
+ 11, 12, 13, 14, 15, 37, 38, 3, 30, 31,
+ 32, 52, 8, 7, 25, 26, 27, 28, 29, 10,
+ 52, 3, 37, 38, 6, 36, 13, 14, 39, 16,
+ 52, 18, 19, 20, 3, 3, 6, 6, 66, 67,
+ 8, 52, 52, 53, 54, 55, 13, 14, 3, 86,
+ 86, 86, 13, 14, 21, 22, 23, 24, 42, 204,
+ 205, 45, 46, 47, 48, 52, 13, 14, 3, 106,
+ 106, 106, 4, 8, 111, 111, 111, 105, 115, 40,
+ 41, 8, 43, 44, 3, 52, 33, 34, 3, 8,
+ 3, 52, 3, 8, 3, 8, 3, 8, 3, 8,
+ 3, 8, 3, 8, 3, 52, 4, 8, 4, 8,
+ 49, 50, 51, 4, 4, 52, 4, 154, 154, 154,
+ 4, 4, 159, 53, 161, 4, 4, 164, 164, 164,
+ 4, 232, 4, 4, 55, 4, 4, 7, 4, 53,
+ 7, 6, 52, 7, 52, 7, 52, 5, 52, 5,
+ 52, 3, 7, 7, 4, 4, 4, 4, 4, 4,
+ 4, 3, 5, 52, 17, 52, 7, 7, 7, 4,
+ 7, 4, 4, 4, 4, 4, 52, 214, 214, 214,
+ 6, 5, 4, 4, 7, 4, 4, 4, 4, 52,
+ 4, 52, 52, 52, 4, 52, 4, 35, 4, 4,
+ 86, 52, 239, 239, 239, 232, 106, 163, 208, 159,
+ 244, 154, 241, 277, 165, 214, 52, 283, 312, -1,
+ -1, 55, -1, -1, -1, 53, 53, 52, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1, -1, -1,
+ 277, 277, 277
+ };
+
+ const unsigned char
+ AgentParser::yystos_[] =
+ {
+ 0, 49, 50, 51, 57, 58, 59, 60, 0, 5,
+ 7, 9, 52, 53, 54, 55, 63, 64, 65, 70,
+ 7, 75, 7, 61, 71, 66, 76, 62, 64, 72,
+ 73, 52, 68, 69, 10, 77, 79, 11, 12, 13,
+ 14, 15, 25, 26, 27, 28, 29, 36, 39, 52,
+ 74, 80, 81, 82, 84, 85, 87, 89, 91, 92,
+ 94, 96, 108, 125, 152, 6, 3, 4, 8, 3,
+ 78, 3, 8, 83, 4, 93, 95, 126, 86, 88,
+ 90, 4, 4, 97, 153, 4, 3, 8, 64, 64,
+ 52, 4, 4, 53, 4, 4, 4, 4, 4, 4,
+ 55, 7, 4, 4, 81, 4, 7, 52, 65, 67,
+ 52, 7, 52, 52, 52, 109, 5, 5, 64, 80,
+ 16, 18, 19, 20, 74, 92, 94, 127, 128, 129,
+ 132, 134, 136, 30, 31, 32, 74, 110, 111, 112,
+ 114, 116, 7, 98, 99, 100, 7, 154, 155, 8,
+ 130, 133, 135, 137, 3, 8, 113, 115, 117, 3,
+ 8, 101, 6, 3, 156, 3, 6, 4, 4, 4,
+ 4, 128, 4, 4, 4, 111, 37, 38, 74, 102,
+ 103, 104, 106, 100, 40, 41, 43, 44, 74, 92,
+ 94, 157, 158, 159, 161, 162, 164, 155, 17, 131,
+ 52, 52, 5, 7, 7, 7, 105, 107, 3, 8,
+ 160, 165, 4, 163, 3, 8, 7, 138, 139, 140,
+ 33, 34, 74, 92, 94, 118, 119, 120, 122, 118,
+ 118, 4, 4, 103, 4, 4, 53, 4, 158, 141,
+ 6, 3, 121, 123, 3, 8, 8, 8, 52, 67,
+ 52, 5, 52, 21, 22, 23, 24, 74, 92, 94,
+ 142, 143, 144, 146, 148, 150, 140, 4, 4, 119,
+ 7, 166, 167, 145, 147, 149, 151, 3, 8, 52,
+ 35, 124, 168, 3, 6, 4, 4, 4, 4, 143,
+ 42, 45, 46, 47, 48, 169, 170, 171, 173, 174,
+ 175, 176, 167, 52, 52, 52, 52, 172, 4, 4,
+ 4, 177, 3, 8, 4, 55, 53, 53, 4, 170,
+ 52, 52
+ };
+
+ const unsigned char
+ AgentParser::yyr1_[] =
+ {
+ 0, 56, 58, 57, 59, 57, 60, 57, 62, 61,
+ 63, 64, 64, 64, 64, 64, 64, 64, 66, 65,
+ 67, 68, 68, 69, 69, 69, 71, 70, 72, 72,
+ 73, 73, 73, 74, 76, 75, 78, 77, 77, 79,
+ 80, 80, 80, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 83, 82, 84, 86,
+ 85, 88, 87, 90, 89, 91, 93, 92, 95, 94,
+ 97, 96, 98, 98, 99, 99, 99, 101, 100, 102,
+ 102, 102, 102, 103, 103, 105, 104, 107, 106, 109,
+ 108, 110, 110, 110, 111, 111, 111, 111, 113, 112,
+ 115, 114, 117, 116, 118, 118, 118, 119, 119, 119,
+ 119, 119, 121, 120, 123, 122, 124, 126, 125, 127,
+ 127, 127, 128, 128, 128, 128, 128, 128, 128, 130,
+ 129, 131, 133, 132, 135, 134, 137, 136, 138, 138,
+ 139, 139, 139, 141, 140, 142, 142, 142, 143, 143,
+ 143, 143, 143, 143, 143, 145, 144, 147, 146, 149,
+ 148, 151, 150, 153, 152, 154, 154, 154, 156, 155,
+ 157, 157, 157, 158, 158, 158, 158, 158, 158, 158,
+ 160, 159, 161, 163, 162, 165, 164, 166, 166, 166,
+ 168, 167, 169, 169, 169, 170, 170, 170, 170, 170,
+ 172, 171, 173, 174, 175, 177, 176
+ };
+
+ const signed char
+ AgentParser::yyr2_[] =
+ {
+ 0, 2, 0, 3, 0, 3, 0, 3, 0, 4,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 4,
+ 1, 0, 1, 3, 5, 2, 0, 4, 0, 1,
+ 1, 3, 2, 2, 0, 4, 0, 6, 1, 2,
+ 1, 3, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 4, 3, 0,
+ 4, 0, 4, 0, 4, 3, 0, 4, 0, 4,
+ 0, 6, 0, 1, 1, 3, 2, 0, 4, 1,
+ 3, 2, 1, 1, 1, 0, 4, 0, 4, 0,
+ 6, 1, 3, 2, 1, 1, 1, 1, 0, 6,
+ 0, 6, 0, 6, 1, 3, 2, 1, 1, 1,
+ 1, 1, 0, 4, 0, 4, 1, 0, 6, 1,
+ 3, 2, 1, 1, 1, 1, 1, 1, 1, 0,
+ 4, 1, 0, 4, 0, 4, 0, 6, 0, 1,
+ 1, 3, 2, 0, 4, 1, 3, 2, 1, 1,
+ 1, 1, 1, 1, 1, 0, 4, 0, 4, 0,
+ 4, 0, 4, 0, 6, 1, 3, 2, 0, 4,
+ 1, 3, 2, 1, 1, 1, 1, 1, 1, 1,
+ 0, 4, 3, 0, 4, 0, 6, 1, 3, 2,
+ 0, 4, 1, 3, 2, 1, 1, 1, 1, 1,
+ 0, 4, 3, 3, 3, 0, 4
+ };
+
+
+#if AGENT_DEBUG || 1
+ // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ // First, the terminals, then, starting at \a YYNTOKENS, nonterminals.
+ const char*
+ const AgentParser::yytname_[] =
+ {
+ "\"end of file\"", "error", "\"invalid token\"", "\",\"", "\":\"",
+ "\"[\"", "\"]\"", "\"{\"", "\"}\"", "\"null\"", "\"Control-agent\"",
+ "\"http-host\"", "\"http-port\"", "\"user-context\"", "\"comment\"",
+ "\"authentication\"", "\"type\"", "\"basic\"", "\"realm\"",
+ "\"directory\"", "\"clients\"", "\"user\"", "\"user-file\"",
+ "\"password\"", "\"password-file\"", "\"trust-anchor\"", "\"cert-file\"",
+ "\"key-file\"", "\"cert-required\"", "\"control-sockets\"", "\"dhcp4\"",
+ "\"dhcp6\"", "\"d2\"", "\"socket-name\"", "\"socket-type\"", "\"unix\"",
+ "\"hooks-libraries\"", "\"library\"", "\"parameters\"", "\"loggers\"",
+ "\"name\"", "\"output_options\"", "\"output\"", "\"debuglevel\"",
+ "\"severity\"", "\"flush\"", "\"maxsize\"", "\"maxver\"", "\"pattern\"",
+ "START_JSON", "START_AGENT", "START_SUB_AGENT", "\"constant string\"",
+ "\"integer\"", "\"floating point\"", "\"boolean\"", "$accept", "start",
+ "$@1", "$@2", "$@3", "sub_agent", "$@4", "json", "value", "map", "$@5",
+ "map_value", "map_content", "not_empty_map", "list_generic", "$@6",
+ "list_content", "not_empty_list", "unknown_map_entry",
+ "agent_syntax_map", "$@7", "global_object", "$@8", "global_object_comma",
+ "global_params", "global_param", "http_host", "$@9", "http_port",
+ "trust_anchor", "$@10", "cert_file", "$@11", "key_file", "$@12",
+ "cert_required", "user_context", "$@13", "comment", "$@14",
+ "hooks_libraries", "$@15", "hooks_libraries_list",
+ "not_empty_hooks_libraries_list", "hooks_library", "$@16",
+ "hooks_params", "hooks_param", "library", "$@17", "parameters", "$@18",
+ "control_sockets", "$@19", "control_sockets_params", "control_socket",
+ "dhcp4_server_socket", "$@20", "dhcp6_server_socket", "$@21",
+ "d2_server_socket", "$@22", "control_socket_params",
+ "control_socket_param", "socket_name", "$@23", "socket_type", "$@24",
+ "socket_type_value", "authentication", "$@25", "auth_params",
+ "auth_param", "auth_type", "$@26", "auth_type_value", "realm", "$@27",
+ "directory", "$@28", "clients", "$@29", "clients_list",
+ "not_empty_clients_list", "basic_auth", "$@30", "clients_params",
+ "clients_param", "user", "$@31", "user_file", "$@32", "password", "$@33",
+ "password_file", "$@34", "loggers", "$@35", "loggers_entries",
+ "logger_entry", "$@36", "logger_params", "logger_param", "name", "$@37",
+ "debuglevel", "severity", "$@38", "output_options_list", "$@39",
+ "output_options_list_content", "output_entry", "$@40",
+ "output_params_list", "output_params", "output", "$@41", "flush",
+ "maxsize", "maxver", "pattern", "$@42", YY_NULLPTR
+ };
+#endif
+
+
+#if AGENT_DEBUG
+ const short
+ AgentParser::yyrline_[] =
+ {
+ 0, 127, 127, 127, 128, 128, 129, 129, 137, 137,
+ 148, 154, 155, 156, 157, 158, 159, 160, 164, 164,
+ 175, 180, 181, 189, 194, 200, 205, 205, 211, 212,
+ 215, 219, 223, 235, 243, 243, 255, 255, 271, 274,
+ 278, 279, 280, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 302, 302, 311, 317,
+ 317, 326, 326, 335, 335, 344, 350, 350, 375, 375,
+ 403, 403, 414, 415, 418, 419, 420, 425, 425, 433,
+ 434, 435, 438, 441, 442, 445, 445, 454, 454, 465,
+ 465, 479, 480, 481, 488, 489, 490, 491, 495, 495,
+ 507, 507, 519, 519, 531, 532, 533, 539, 540, 541,
+ 542, 543, 547, 547, 557, 557, 566, 573, 573, 586,
+ 587, 588, 593, 594, 595, 596, 597, 598, 599, 602,
+ 602, 610, 613, 613, 622, 622, 631, 631, 642, 643,
+ 646, 647, 648, 653, 653, 661, 662, 663, 668, 669,
+ 670, 671, 672, 673, 674, 677, 677, 686, 686, 695,
+ 695, 704, 704, 717, 717, 730, 731, 732, 738, 738,
+ 746, 747, 748, 753, 754, 755, 756, 757, 758, 759,
+ 762, 762, 771, 777, 777, 786, 786, 797, 798, 799,
+ 804, 804, 812, 813, 814, 819, 820, 821, 822, 823,
+ 826, 826, 835, 841, 847, 853, 853
+ };
+
+ void
+ AgentParser::yy_stack_print_ () const
+ {
+ *yycdebug_ << "Stack now";
+ for (stack_type::const_iterator
+ i = yystack_.begin (),
+ i_end = yystack_.end ();
+ i != i_end; ++i)
+ *yycdebug_ << ' ' << int (i->state);
+ *yycdebug_ << '\n';
+ }
+
+ void
+ AgentParser::yy_reduce_print_ (int yyrule) const
+ {
+ int yylno = yyrline_[yyrule];
+ int yynrhs = yyr2_[yyrule];
+ // Print the symbols being reduced, and their result.
+ *yycdebug_ << "Reducing stack by rule " << yyrule - 1
+ << " (line " << yylno << "):\n";
+ // The symbols being reduced.
+ for (int yyi = 0; yyi < yynrhs; yyi++)
+ YY_SYMBOL_PRINT (" $" << yyi + 1 << " =",
+ yystack_[(yynrhs) - (yyi + 1)]);
+ }
+#endif // AGENT_DEBUG
+
+
+#line 14 "agent_parser.yy"
+} } // isc::agent
+#line 2543 "agent_parser.cc"
+
+#line 862 "agent_parser.yy"
+
+
+void
+isc::agent::AgentParser::error(const location_type& loc,
+ const std::string& what)
+{
+ ctx.error(loc, what);
+}
diff --git a/src/bin/agent/agent_parser.h b/src/bin/agent/agent_parser.h
new file mode 100644
index 0000000..55e0573
--- /dev/null
+++ b/src/bin/agent/agent_parser.h
@@ -0,0 +1,2534 @@
+// A Bison parser, made by GNU Bison 3.8.2.
+
+// Skeleton interface for Bison LALR(1) parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton. Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+
+/**
+ ** \file agent_parser.h
+ ** Define the isc::agent::parser class.
+ */
+
+// C++ LALR(1) parser skeleton written by Akim Demaille.
+
+// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+// especially those whose name start with YY_ or yy_. They are
+// private implementation details that can be changed or removed.
+
+#ifndef YY_AGENT_AGENT_PARSER_H_INCLUDED
+# define YY_AGENT_AGENT_PARSER_H_INCLUDED
+// "%code requires" blocks.
+#line 17 "agent_parser.yy"
+
+#include <string>
+#include <cc/data.h>
+#include <boost/lexical_cast.hpp>
+#include <agent/parser_context_decl.h>
+
+using namespace isc::agent;
+using namespace isc::data;
+using namespace std;
+
+#line 60 "agent_parser.h"
+
+# include <cassert>
+# include <cstdlib> // std::abort
+# include <iostream>
+# include <stdexcept>
+# include <string>
+# include <vector>
+
+#if defined __cplusplus
+# define YY_CPLUSPLUS __cplusplus
+#else
+# define YY_CPLUSPLUS 199711L
+#endif
+
+// Support move semantics when possible.
+#if 201103L <= YY_CPLUSPLUS
+# define YY_MOVE std::move
+# define YY_MOVE_OR_COPY move
+# define YY_MOVE_REF(Type) Type&&
+# define YY_RVREF(Type) Type&&
+# define YY_COPY(Type) Type
+#else
+# define YY_MOVE
+# define YY_MOVE_OR_COPY copy
+# define YY_MOVE_REF(Type) Type&
+# define YY_RVREF(Type) const Type&
+# define YY_COPY(Type) const Type&
+#endif
+
+// Support noexcept when possible.
+#if 201103L <= YY_CPLUSPLUS
+# define YY_NOEXCEPT noexcept
+# define YY_NOTHROW
+#else
+# define YY_NOEXCEPT
+# define YY_NOTHROW throw ()
+#endif
+
+// Support constexpr when possible.
+#if 201703 <= YY_CPLUSPLUS
+# define YY_CONSTEXPR constexpr
+#else
+# define YY_CONSTEXPR
+#endif
+# include "location.hh"
+#include <typeinfo>
+#ifndef AGENT__ASSERT
+# include <cassert>
+# define AGENT__ASSERT assert
+#endif
+
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+# define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+# define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YY_USE(E) ((void) (E))
+#else
+# define YY_USE(E) /* empty */
+#endif
+
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
+# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
+# else
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# endif
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END \
+ _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+# ifndef YY_CAST
+# ifdef __cplusplus
+# define YY_CAST(Type, Val) static_cast<Type> (Val)
+# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+# else
+# define YY_CAST(Type, Val) ((Type) (Val))
+# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+# endif
+# endif
+# ifndef YY_NULLPTR
+# if defined __cplusplus
+# if 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# else
+# define YY_NULLPTR ((void*)0)
+# endif
+# endif
+
+/* Debug traces. */
+#ifndef AGENT_DEBUG
+# if defined YYDEBUG
+#if YYDEBUG
+# define AGENT_DEBUG 1
+# else
+# define AGENT_DEBUG 0
+# endif
+# else /* ! defined YYDEBUG */
+# define AGENT_DEBUG 1
+# endif /* ! defined YYDEBUG */
+#endif /* ! defined AGENT_DEBUG */
+
+#line 14 "agent_parser.yy"
+namespace isc { namespace agent {
+#line 209 "agent_parser.h"
+
+
+
+
+ /// A Bison parser.
+ class AgentParser
+ {
+ public:
+#ifdef AGENT_STYPE
+# ifdef __GNUC__
+# pragma GCC message "bison: do not #define AGENT_STYPE in C++, use %define api.value.type"
+# endif
+ typedef AGENT_STYPE value_type;
+#else
+ /// A buffer to store and retrieve objects.
+ ///
+ /// Sort of a variant, but does not keep track of the nature
+ /// of the stored data, since that knowledge is available
+ /// via the current parser state.
+ class value_type
+ {
+ public:
+ /// Type of *this.
+ typedef value_type self_type;
+
+ /// Empty construction.
+ value_type () YY_NOEXCEPT
+ : yyraw_ ()
+ , yytypeid_ (YY_NULLPTR)
+ {}
+
+ /// Construct and fill.
+ template <typename T>
+ value_type (YY_RVREF (T) t)
+ : yytypeid_ (&typeid (T))
+ {
+ AGENT__ASSERT (sizeof (T) <= size);
+ new (yyas_<T> ()) T (YY_MOVE (t));
+ }
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Non copyable.
+ value_type (const self_type&) = delete;
+ /// Non copyable.
+ self_type& operator= (const self_type&) = delete;
+#endif
+
+ /// Destruction, allowed only if empty.
+ ~value_type () YY_NOEXCEPT
+ {
+ AGENT__ASSERT (!yytypeid_);
+ }
+
+# if 201103L <= YY_CPLUSPLUS
+ /// Instantiate a \a T in here from \a t.
+ template <typename T, typename... U>
+ T&
+ emplace (U&&... u)
+ {
+ AGENT__ASSERT (!yytypeid_);
+ AGENT__ASSERT (sizeof (T) <= size);
+ yytypeid_ = & typeid (T);
+ return *new (yyas_<T> ()) T (std::forward <U>(u)...);
+ }
+# else
+ /// Instantiate an empty \a T in here.
+ template <typename T>
+ T&
+ emplace ()
+ {
+ AGENT__ASSERT (!yytypeid_);
+ AGENT__ASSERT (sizeof (T) <= size);
+ yytypeid_ = & typeid (T);
+ return *new (yyas_<T> ()) T ();
+ }
+
+ /// Instantiate a \a T in here from \a t.
+ template <typename T>
+ T&
+ emplace (const T& t)
+ {
+ AGENT__ASSERT (!yytypeid_);
+ AGENT__ASSERT (sizeof (T) <= size);
+ yytypeid_ = & typeid (T);
+ return *new (yyas_<T> ()) T (t);
+ }
+# endif
+
+ /// Instantiate an empty \a T in here.
+ /// Obsolete, use emplace.
+ template <typename T>
+ T&
+ build ()
+ {
+ return emplace<T> ();
+ }
+
+ /// Instantiate a \a T in here from \a t.
+ /// Obsolete, use emplace.
+ template <typename T>
+ T&
+ build (const T& t)
+ {
+ return emplace<T> (t);
+ }
+
+ /// Accessor to a built \a T.
+ template <typename T>
+ T&
+ as () YY_NOEXCEPT
+ {
+ AGENT__ASSERT (yytypeid_);
+ AGENT__ASSERT (*yytypeid_ == typeid (T));
+ AGENT__ASSERT (sizeof (T) <= size);
+ return *yyas_<T> ();
+ }
+
+ /// Const accessor to a built \a T (for %printer).
+ template <typename T>
+ const T&
+ as () const YY_NOEXCEPT
+ {
+ AGENT__ASSERT (yytypeid_);
+ AGENT__ASSERT (*yytypeid_ == typeid (T));
+ AGENT__ASSERT (sizeof (T) <= size);
+ return *yyas_<T> ();
+ }
+
+ /// Swap the content with \a that, of same type.
+ ///
+ /// Both variants must be built beforehand, because swapping the actual
+ /// data requires reading it (with as()), and this is not possible on
+ /// unconstructed variants: it would require some dynamic testing, which
+ /// should not be the variant's responsibility.
+ /// Swapping between built and (possibly) non-built is done with
+ /// self_type::move ().
+ template <typename T>
+ void
+ swap (self_type& that) YY_NOEXCEPT
+ {
+ AGENT__ASSERT (yytypeid_);
+ AGENT__ASSERT (*yytypeid_ == *that.yytypeid_);
+ std::swap (as<T> (), that.as<T> ());
+ }
+
+ /// Move the content of \a that to this.
+ ///
+ /// Destroys \a that.
+ template <typename T>
+ void
+ move (self_type& that)
+ {
+# if 201103L <= YY_CPLUSPLUS
+ emplace<T> (std::move (that.as<T> ()));
+# else
+ emplace<T> ();
+ swap<T> (that);
+# endif
+ that.destroy<T> ();
+ }
+
+# if 201103L <= YY_CPLUSPLUS
+ /// Move the content of \a that to this.
+ template <typename T>
+ void
+ move (self_type&& that)
+ {
+ emplace<T> (std::move (that.as<T> ()));
+ that.destroy<T> ();
+ }
+#endif
+
+ /// Copy the content of \a that to this.
+ template <typename T>
+ void
+ copy (const self_type& that)
+ {
+ emplace<T> (that.as<T> ());
+ }
+
+ /// Destroy the stored \a T.
+ template <typename T>
+ void
+ destroy ()
+ {
+ as<T> ().~T ();
+ yytypeid_ = YY_NULLPTR;
+ }
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Non copyable.
+ value_type (const self_type&);
+ /// Non copyable.
+ self_type& operator= (const self_type&);
+#endif
+
+ /// Accessor to raw memory as \a T.
+ template <typename T>
+ T*
+ yyas_ () YY_NOEXCEPT
+ {
+ void *yyp = yyraw_;
+ return static_cast<T*> (yyp);
+ }
+
+ /// Const accessor to raw memory as \a T.
+ template <typename T>
+ const T*
+ yyas_ () const YY_NOEXCEPT
+ {
+ const void *yyp = yyraw_;
+ return static_cast<const T*> (yyp);
+ }
+
+ /// An auxiliary type to compute the largest semantic type.
+ union union_type
+ {
+ // value
+ // map_value
+ // socket_type_value
+ // auth_type_value
+ char dummy1[sizeof (ElementPtr)];
+
+ // "boolean"
+ char dummy2[sizeof (bool)];
+
+ // "floating point"
+ char dummy3[sizeof (double)];
+
+ // "integer"
+ char dummy4[sizeof (int64_t)];
+
+ // "constant string"
+ char dummy5[sizeof (std::string)];
+ };
+
+ /// The size of the largest semantic type.
+ enum { size = sizeof (union_type) };
+
+ /// A buffer to store semantic values.
+ union
+ {
+ /// Strongest alignment constraints.
+ long double yyalign_me_;
+ /// A buffer large enough to store any of the semantic values.
+ char yyraw_[size];
+ };
+
+ /// Whether the content is built: if defined, the name of the stored type.
+ const std::type_info *yytypeid_;
+ };
+
+#endif
+ /// Backward compatibility (Bison 3.8).
+ typedef value_type semantic_type;
+
+ /// Symbol locations.
+ typedef location location_type;
+
+ /// Syntax errors thrown from user actions.
+ struct syntax_error : std::runtime_error
+ {
+ syntax_error (const location_type& l, const std::string& m)
+ : std::runtime_error (m)
+ , location (l)
+ {}
+
+ syntax_error (const syntax_error& s)
+ : std::runtime_error (s.what ())
+ , location (s.location)
+ {}
+
+ ~syntax_error () YY_NOEXCEPT YY_NOTHROW;
+
+ location_type location;
+ };
+
+ /// Token kinds.
+ struct token
+ {
+ enum token_kind_type
+ {
+ TOKEN_AGENT_EMPTY = -2,
+ TOKEN_END = 0, // "end of file"
+ TOKEN_AGENT_error = 256, // error
+ TOKEN_AGENT_UNDEF = 257, // "invalid token"
+ TOKEN_COMMA = 258, // ","
+ TOKEN_COLON = 259, // ":"
+ TOKEN_LSQUARE_BRACKET = 260, // "["
+ TOKEN_RSQUARE_BRACKET = 261, // "]"
+ TOKEN_LCURLY_BRACKET = 262, // "{"
+ TOKEN_RCURLY_BRACKET = 263, // "}"
+ TOKEN_NULL_TYPE = 264, // "null"
+ TOKEN_CONTROL_AGENT = 265, // "Control-agent"
+ TOKEN_HTTP_HOST = 266, // "http-host"
+ TOKEN_HTTP_PORT = 267, // "http-port"
+ TOKEN_USER_CONTEXT = 268, // "user-context"
+ TOKEN_COMMENT = 269, // "comment"
+ TOKEN_AUTHENTICATION = 270, // "authentication"
+ TOKEN_TYPE = 271, // "type"
+ TOKEN_BASIC = 272, // "basic"
+ TOKEN_REALM = 273, // "realm"
+ TOKEN_DIRECTORY = 274, // "directory"
+ TOKEN_CLIENTS = 275, // "clients"
+ TOKEN_USER = 276, // "user"
+ TOKEN_USER_FILE = 277, // "user-file"
+ TOKEN_PASSWORD = 278, // "password"
+ TOKEN_PASSWORD_FILE = 279, // "password-file"
+ TOKEN_TRUST_ANCHOR = 280, // "trust-anchor"
+ TOKEN_CERT_FILE = 281, // "cert-file"
+ TOKEN_KEY_FILE = 282, // "key-file"
+ TOKEN_CERT_REQUIRED = 283, // "cert-required"
+ TOKEN_CONTROL_SOCKETS = 284, // "control-sockets"
+ TOKEN_DHCP4_SERVER = 285, // "dhcp4"
+ TOKEN_DHCP6_SERVER = 286, // "dhcp6"
+ TOKEN_D2_SERVER = 287, // "d2"
+ TOKEN_SOCKET_NAME = 288, // "socket-name"
+ TOKEN_SOCKET_TYPE = 289, // "socket-type"
+ TOKEN_UNIX = 290, // "unix"
+ TOKEN_HOOKS_LIBRARIES = 291, // "hooks-libraries"
+ TOKEN_LIBRARY = 292, // "library"
+ TOKEN_PARAMETERS = 293, // "parameters"
+ TOKEN_LOGGERS = 294, // "loggers"
+ TOKEN_NAME = 295, // "name"
+ TOKEN_OUTPUT_OPTIONS = 296, // "output_options"
+ TOKEN_OUTPUT = 297, // "output"
+ TOKEN_DEBUGLEVEL = 298, // "debuglevel"
+ TOKEN_SEVERITY = 299, // "severity"
+ TOKEN_FLUSH = 300, // "flush"
+ TOKEN_MAXSIZE = 301, // "maxsize"
+ TOKEN_MAXVER = 302, // "maxver"
+ TOKEN_PATTERN = 303, // "pattern"
+ TOKEN_START_JSON = 304, // START_JSON
+ TOKEN_START_AGENT = 305, // START_AGENT
+ TOKEN_START_SUB_AGENT = 306, // START_SUB_AGENT
+ TOKEN_STRING = 307, // "constant string"
+ TOKEN_INTEGER = 308, // "integer"
+ TOKEN_FLOAT = 309, // "floating point"
+ TOKEN_BOOLEAN = 310 // "boolean"
+ };
+ /// Backward compatibility alias (Bison 3.6).
+ typedef token_kind_type yytokentype;
+ };
+
+ /// Token kind, as returned by yylex.
+ typedef token::token_kind_type token_kind_type;
+
+ /// Backward compatibility alias (Bison 3.6).
+ typedef token_kind_type token_type;
+
+ /// Symbol kinds.
+ struct symbol_kind
+ {
+ enum symbol_kind_type
+ {
+ YYNTOKENS = 56, ///< Number of tokens.
+ S_YYEMPTY = -2,
+ S_YYEOF = 0, // "end of file"
+ S_YYerror = 1, // error
+ S_YYUNDEF = 2, // "invalid token"
+ S_COMMA = 3, // ","
+ S_COLON = 4, // ":"
+ S_LSQUARE_BRACKET = 5, // "["
+ S_RSQUARE_BRACKET = 6, // "]"
+ S_LCURLY_BRACKET = 7, // "{"
+ S_RCURLY_BRACKET = 8, // "}"
+ S_NULL_TYPE = 9, // "null"
+ S_CONTROL_AGENT = 10, // "Control-agent"
+ S_HTTP_HOST = 11, // "http-host"
+ S_HTTP_PORT = 12, // "http-port"
+ S_USER_CONTEXT = 13, // "user-context"
+ S_COMMENT = 14, // "comment"
+ S_AUTHENTICATION = 15, // "authentication"
+ S_TYPE = 16, // "type"
+ S_BASIC = 17, // "basic"
+ S_REALM = 18, // "realm"
+ S_DIRECTORY = 19, // "directory"
+ S_CLIENTS = 20, // "clients"
+ S_USER = 21, // "user"
+ S_USER_FILE = 22, // "user-file"
+ S_PASSWORD = 23, // "password"
+ S_PASSWORD_FILE = 24, // "password-file"
+ S_TRUST_ANCHOR = 25, // "trust-anchor"
+ S_CERT_FILE = 26, // "cert-file"
+ S_KEY_FILE = 27, // "key-file"
+ S_CERT_REQUIRED = 28, // "cert-required"
+ S_CONTROL_SOCKETS = 29, // "control-sockets"
+ S_DHCP4_SERVER = 30, // "dhcp4"
+ S_DHCP6_SERVER = 31, // "dhcp6"
+ S_D2_SERVER = 32, // "d2"
+ S_SOCKET_NAME = 33, // "socket-name"
+ S_SOCKET_TYPE = 34, // "socket-type"
+ S_UNIX = 35, // "unix"
+ S_HOOKS_LIBRARIES = 36, // "hooks-libraries"
+ S_LIBRARY = 37, // "library"
+ S_PARAMETERS = 38, // "parameters"
+ S_LOGGERS = 39, // "loggers"
+ S_NAME = 40, // "name"
+ S_OUTPUT_OPTIONS = 41, // "output_options"
+ S_OUTPUT = 42, // "output"
+ S_DEBUGLEVEL = 43, // "debuglevel"
+ S_SEVERITY = 44, // "severity"
+ S_FLUSH = 45, // "flush"
+ S_MAXSIZE = 46, // "maxsize"
+ S_MAXVER = 47, // "maxver"
+ S_PATTERN = 48, // "pattern"
+ S_START_JSON = 49, // START_JSON
+ S_START_AGENT = 50, // START_AGENT
+ S_START_SUB_AGENT = 51, // START_SUB_AGENT
+ S_STRING = 52, // "constant string"
+ S_INTEGER = 53, // "integer"
+ S_FLOAT = 54, // "floating point"
+ S_BOOLEAN = 55, // "boolean"
+ S_YYACCEPT = 56, // $accept
+ S_start = 57, // start
+ S_58_1 = 58, // $@1
+ S_59_2 = 59, // $@2
+ S_60_3 = 60, // $@3
+ S_sub_agent = 61, // sub_agent
+ S_62_4 = 62, // $@4
+ S_json = 63, // json
+ S_value = 64, // value
+ S_map = 65, // map
+ S_66_5 = 66, // $@5
+ S_map_value = 67, // map_value
+ S_map_content = 68, // map_content
+ S_not_empty_map = 69, // not_empty_map
+ S_list_generic = 70, // list_generic
+ S_71_6 = 71, // $@6
+ S_list_content = 72, // list_content
+ S_not_empty_list = 73, // not_empty_list
+ S_unknown_map_entry = 74, // unknown_map_entry
+ S_agent_syntax_map = 75, // agent_syntax_map
+ S_76_7 = 76, // $@7
+ S_global_object = 77, // global_object
+ S_78_8 = 78, // $@8
+ S_global_object_comma = 79, // global_object_comma
+ S_global_params = 80, // global_params
+ S_global_param = 81, // global_param
+ S_http_host = 82, // http_host
+ S_83_9 = 83, // $@9
+ S_http_port = 84, // http_port
+ S_trust_anchor = 85, // trust_anchor
+ S_86_10 = 86, // $@10
+ S_cert_file = 87, // cert_file
+ S_88_11 = 88, // $@11
+ S_key_file = 89, // key_file
+ S_90_12 = 90, // $@12
+ S_cert_required = 91, // cert_required
+ S_user_context = 92, // user_context
+ S_93_13 = 93, // $@13
+ S_comment = 94, // comment
+ S_95_14 = 95, // $@14
+ S_hooks_libraries = 96, // hooks_libraries
+ S_97_15 = 97, // $@15
+ S_hooks_libraries_list = 98, // hooks_libraries_list
+ S_not_empty_hooks_libraries_list = 99, // not_empty_hooks_libraries_list
+ S_hooks_library = 100, // hooks_library
+ S_101_16 = 101, // $@16
+ S_hooks_params = 102, // hooks_params
+ S_hooks_param = 103, // hooks_param
+ S_library = 104, // library
+ S_105_17 = 105, // $@17
+ S_parameters = 106, // parameters
+ S_107_18 = 107, // $@18
+ S_control_sockets = 108, // control_sockets
+ S_109_19 = 109, // $@19
+ S_control_sockets_params = 110, // control_sockets_params
+ S_control_socket = 111, // control_socket
+ S_dhcp4_server_socket = 112, // dhcp4_server_socket
+ S_113_20 = 113, // $@20
+ S_dhcp6_server_socket = 114, // dhcp6_server_socket
+ S_115_21 = 115, // $@21
+ S_d2_server_socket = 116, // d2_server_socket
+ S_117_22 = 117, // $@22
+ S_control_socket_params = 118, // control_socket_params
+ S_control_socket_param = 119, // control_socket_param
+ S_socket_name = 120, // socket_name
+ S_121_23 = 121, // $@23
+ S_socket_type = 122, // socket_type
+ S_123_24 = 123, // $@24
+ S_socket_type_value = 124, // socket_type_value
+ S_authentication = 125, // authentication
+ S_126_25 = 126, // $@25
+ S_auth_params = 127, // auth_params
+ S_auth_param = 128, // auth_param
+ S_auth_type = 129, // auth_type
+ S_130_26 = 130, // $@26
+ S_auth_type_value = 131, // auth_type_value
+ S_realm = 132, // realm
+ S_133_27 = 133, // $@27
+ S_directory = 134, // directory
+ S_135_28 = 135, // $@28
+ S_clients = 136, // clients
+ S_137_29 = 137, // $@29
+ S_clients_list = 138, // clients_list
+ S_not_empty_clients_list = 139, // not_empty_clients_list
+ S_basic_auth = 140, // basic_auth
+ S_141_30 = 141, // $@30
+ S_clients_params = 142, // clients_params
+ S_clients_param = 143, // clients_param
+ S_user = 144, // user
+ S_145_31 = 145, // $@31
+ S_user_file = 146, // user_file
+ S_147_32 = 147, // $@32
+ S_password = 148, // password
+ S_149_33 = 149, // $@33
+ S_password_file = 150, // password_file
+ S_151_34 = 151, // $@34
+ S_loggers = 152, // loggers
+ S_153_35 = 153, // $@35
+ S_loggers_entries = 154, // loggers_entries
+ S_logger_entry = 155, // logger_entry
+ S_156_36 = 156, // $@36
+ S_logger_params = 157, // logger_params
+ S_logger_param = 158, // logger_param
+ S_name = 159, // name
+ S_160_37 = 160, // $@37
+ S_debuglevel = 161, // debuglevel
+ S_severity = 162, // severity
+ S_163_38 = 163, // $@38
+ S_output_options_list = 164, // output_options_list
+ S_165_39 = 165, // $@39
+ S_output_options_list_content = 166, // output_options_list_content
+ S_output_entry = 167, // output_entry
+ S_168_40 = 168, // $@40
+ S_output_params_list = 169, // output_params_list
+ S_output_params = 170, // output_params
+ S_output = 171, // output
+ S_172_41 = 172, // $@41
+ S_flush = 173, // flush
+ S_maxsize = 174, // maxsize
+ S_maxver = 175, // maxver
+ S_pattern = 176, // pattern
+ S_177_42 = 177 // $@42
+ };
+ };
+
+ /// (Internal) symbol kind.
+ typedef symbol_kind::symbol_kind_type symbol_kind_type;
+
+ /// The number of tokens.
+ static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS;
+
+ /// A complete symbol.
+ ///
+ /// Expects its Base type to provide access to the symbol kind
+ /// via kind ().
+ ///
+ /// Provide access to semantic value and location.
+ template <typename Base>
+ struct basic_symbol : Base
+ {
+ /// Alias to Base.
+ typedef Base super_type;
+
+ /// Default constructor.
+ basic_symbol () YY_NOEXCEPT
+ : value ()
+ , location ()
+ {}
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Move constructor.
+ basic_symbol (basic_symbol&& that)
+ : Base (std::move (that))
+ , value ()
+ , location (std::move (that.location))
+ {
+ switch (this->kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.move< ElementPtr > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (std::move (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+ }
+#endif
+
+ /// Copy constructor.
+ basic_symbol (const basic_symbol& that);
+
+ /// Constructors for typed symbols.
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, location_type&& l)
+ : Base (t)
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const location_type& l)
+ : Base (t)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, ElementPtr&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const ElementPtr& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, bool&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const bool& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, double&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const double& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, int64_t&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const int64_t& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, std::string&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const std::string& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+ /// Destroy the symbol.
+ ~basic_symbol ()
+ {
+ clear ();
+ }
+
+
+
+ /// Destroy contents, and record that is empty.
+ void clear () YY_NOEXCEPT
+ {
+ // User destructor.
+ symbol_kind_type yykind = this->kind ();
+ basic_symbol<Base>& yysym = *this;
+ (void) yysym;
+ switch (yykind)
+ {
+ default:
+ break;
+ }
+
+ // Value type destructor.
+switch (yykind)
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.template destroy< ElementPtr > ();
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.template destroy< bool > ();
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.template destroy< double > ();
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.template destroy< int64_t > ();
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.template destroy< std::string > ();
+ break;
+
+ default:
+ break;
+ }
+
+ Base::clear ();
+ }
+
+ /// The user-facing name of this symbol.
+ std::string name () const YY_NOEXCEPT
+ {
+ return AgentParser::symbol_name (this->kind ());
+ }
+
+ /// Backward compatibility (Bison 3.6).
+ symbol_kind_type type_get () const YY_NOEXCEPT;
+
+ /// Whether empty.
+ bool empty () const YY_NOEXCEPT;
+
+ /// Destructive move, \a s is emptied into this.
+ void move (basic_symbol& s);
+
+ /// The semantic value.
+ value_type value;
+
+ /// The location.
+ location_type location;
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Assignment operator.
+ basic_symbol& operator= (const basic_symbol& that);
+#endif
+ };
+
+ /// Type access provider for token (enum) based symbols.
+ struct by_kind
+ {
+ /// The symbol kind as needed by the constructor.
+ typedef token_kind_type kind_type;
+
+ /// Default constructor.
+ by_kind () YY_NOEXCEPT;
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Move constructor.
+ by_kind (by_kind&& that) YY_NOEXCEPT;
+#endif
+
+ /// Copy constructor.
+ by_kind (const by_kind& that) YY_NOEXCEPT;
+
+ /// Constructor from (external) token numbers.
+ by_kind (kind_type t) YY_NOEXCEPT;
+
+
+
+ /// Record that this symbol is empty.
+ void clear () YY_NOEXCEPT;
+
+ /// Steal the symbol kind from \a that.
+ void move (by_kind& that);
+
+ /// The (internal) type number (corresponding to \a type).
+ /// \a empty when empty.
+ symbol_kind_type kind () const YY_NOEXCEPT;
+
+ /// Backward compatibility (Bison 3.6).
+ symbol_kind_type type_get () const YY_NOEXCEPT;
+
+ /// The symbol kind.
+ /// \a S_YYEMPTY when empty.
+ symbol_kind_type kind_;
+ };
+
+ /// Backward compatibility for a private implementation detail (Bison 3.6).
+ typedef by_kind by_type;
+
+ /// "External" symbols: returned by the scanner.
+ struct symbol_type : basic_symbol<by_kind>
+ {
+ /// Superclass.
+ typedef basic_symbol<by_kind> super_type;
+
+ /// Empty symbol.
+ symbol_type () YY_NOEXCEPT {}
+
+ /// Constructor for valueless symbols, and symbols from each type.
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, location_type l)
+ : super_type (token_kind_type (tok), std::move (l))
+#else
+ symbol_type (int tok, const location_type& l)
+ : super_type (token_kind_type (tok), l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ AGENT__ASSERT (tok == token::TOKEN_END
+ || (token::TOKEN_AGENT_error <= tok && tok <= token::TOKEN_START_SUB_AGENT));
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, bool v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const bool& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ AGENT__ASSERT (tok == token::TOKEN_BOOLEAN);
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, double v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const double& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ AGENT__ASSERT (tok == token::TOKEN_FLOAT);
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, int64_t v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const int64_t& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ AGENT__ASSERT (tok == token::TOKEN_INTEGER);
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, std::string v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const std::string& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ AGENT__ASSERT (tok == token::TOKEN_STRING);
+#endif
+ }
+ };
+
+ /// Build a parser object.
+ AgentParser (isc::agent::ParserContext& ctx_yyarg);
+ virtual ~AgentParser ();
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Non copyable.
+ AgentParser (const AgentParser&) = delete;
+ /// Non copyable.
+ AgentParser& operator= (const AgentParser&) = delete;
+#endif
+
+ /// Parse. An alias for parse ().
+ /// \returns 0 iff parsing succeeded.
+ int operator() ();
+
+ /// Parse.
+ /// \returns 0 iff parsing succeeded.
+ virtual int parse ();
+
+#if AGENT_DEBUG
+ /// The current debugging stream.
+ std::ostream& debug_stream () const YY_ATTRIBUTE_PURE;
+ /// Set the current debugging stream.
+ void set_debug_stream (std::ostream &);
+
+ /// Type for debugging levels.
+ typedef int debug_level_type;
+ /// The current debugging level.
+ debug_level_type debug_level () const YY_ATTRIBUTE_PURE;
+ /// Set the current debugging level.
+ void set_debug_level (debug_level_type l);
+#endif
+
+ /// Report a syntax error.
+ /// \param loc where the syntax error is found.
+ /// \param msg a description of the syntax error.
+ virtual void error (const location_type& loc, const std::string& msg);
+
+ /// Report a syntax error.
+ void error (const syntax_error& err);
+
+ /// The user-facing name of the symbol whose (internal) number is
+ /// YYSYMBOL. No bounds checking.
+ static std::string symbol_name (symbol_kind_type yysymbol);
+
+ // Implementation of make_symbol for each token kind.
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_END (location_type l)
+ {
+ return symbol_type (token::TOKEN_END, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_END (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_END, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_AGENT_error (location_type l)
+ {
+ return symbol_type (token::TOKEN_AGENT_error, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_AGENT_error (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_AGENT_error, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_AGENT_UNDEF (location_type l)
+ {
+ return symbol_type (token::TOKEN_AGENT_UNDEF, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_AGENT_UNDEF (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_AGENT_UNDEF, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_COMMA (location_type l)
+ {
+ return symbol_type (token::TOKEN_COMMA, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_COMMA (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_COMMA, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_COLON (location_type l)
+ {
+ return symbol_type (token::TOKEN_COLON, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_COLON (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_COLON, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LSQUARE_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_LSQUARE_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LSQUARE_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LSQUARE_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_RSQUARE_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_RSQUARE_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_RSQUARE_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_RSQUARE_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LCURLY_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_LCURLY_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LCURLY_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LCURLY_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_RCURLY_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_RCURLY_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_RCURLY_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_RCURLY_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NULL_TYPE (location_type l)
+ {
+ return symbol_type (token::TOKEN_NULL_TYPE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NULL_TYPE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NULL_TYPE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CONTROL_AGENT (location_type l)
+ {
+ return symbol_type (token::TOKEN_CONTROL_AGENT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CONTROL_AGENT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CONTROL_AGENT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_HTTP_HOST (location_type l)
+ {
+ return symbol_type (token::TOKEN_HTTP_HOST, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_HTTP_HOST (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_HTTP_HOST, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_HTTP_PORT (location_type l)
+ {
+ return symbol_type (token::TOKEN_HTTP_PORT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_HTTP_PORT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_HTTP_PORT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_USER_CONTEXT (location_type l)
+ {
+ return symbol_type (token::TOKEN_USER_CONTEXT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_USER_CONTEXT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_USER_CONTEXT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_COMMENT (location_type l)
+ {
+ return symbol_type (token::TOKEN_COMMENT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_COMMENT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_COMMENT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_AUTHENTICATION (location_type l)
+ {
+ return symbol_type (token::TOKEN_AUTHENTICATION, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_AUTHENTICATION (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_AUTHENTICATION, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_TYPE (location_type l)
+ {
+ return symbol_type (token::TOKEN_TYPE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_TYPE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_TYPE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_BASIC (location_type l)
+ {
+ return symbol_type (token::TOKEN_BASIC, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_BASIC (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_BASIC, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_REALM (location_type l)
+ {
+ return symbol_type (token::TOKEN_REALM, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_REALM (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_REALM, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DIRECTORY (location_type l)
+ {
+ return symbol_type (token::TOKEN_DIRECTORY, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DIRECTORY (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DIRECTORY, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CLIENTS (location_type l)
+ {
+ return symbol_type (token::TOKEN_CLIENTS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CLIENTS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CLIENTS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_USER (location_type l)
+ {
+ return symbol_type (token::TOKEN_USER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_USER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_USER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_USER_FILE (location_type l)
+ {
+ return symbol_type (token::TOKEN_USER_FILE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_USER_FILE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_USER_FILE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_PASSWORD (location_type l)
+ {
+ return symbol_type (token::TOKEN_PASSWORD, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_PASSWORD (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_PASSWORD, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_PASSWORD_FILE (location_type l)
+ {
+ return symbol_type (token::TOKEN_PASSWORD_FILE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_PASSWORD_FILE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_PASSWORD_FILE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_TRUST_ANCHOR (location_type l)
+ {
+ return symbol_type (token::TOKEN_TRUST_ANCHOR, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_TRUST_ANCHOR (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_TRUST_ANCHOR, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CERT_FILE (location_type l)
+ {
+ return symbol_type (token::TOKEN_CERT_FILE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CERT_FILE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CERT_FILE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_KEY_FILE (location_type l)
+ {
+ return symbol_type (token::TOKEN_KEY_FILE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_KEY_FILE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_KEY_FILE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CERT_REQUIRED (location_type l)
+ {
+ return symbol_type (token::TOKEN_CERT_REQUIRED, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CERT_REQUIRED (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CERT_REQUIRED, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CONTROL_SOCKETS (location_type l)
+ {
+ return symbol_type (token::TOKEN_CONTROL_SOCKETS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CONTROL_SOCKETS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CONTROL_SOCKETS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DHCP4_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_DHCP4_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DHCP4_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DHCP4_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DHCP6_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_DHCP6_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DHCP6_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DHCP6_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_D2_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_D2_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_D2_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_D2_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SOCKET_NAME (location_type l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_NAME, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SOCKET_NAME (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_NAME, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SOCKET_TYPE (location_type l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_TYPE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SOCKET_TYPE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_TYPE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_UNIX (location_type l)
+ {
+ return symbol_type (token::TOKEN_UNIX, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_UNIX (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_UNIX, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_HOOKS_LIBRARIES (location_type l)
+ {
+ return symbol_type (token::TOKEN_HOOKS_LIBRARIES, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_HOOKS_LIBRARIES (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_HOOKS_LIBRARIES, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LIBRARY (location_type l)
+ {
+ return symbol_type (token::TOKEN_LIBRARY, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LIBRARY (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LIBRARY, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_PARAMETERS (location_type l)
+ {
+ return symbol_type (token::TOKEN_PARAMETERS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_PARAMETERS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_PARAMETERS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LOGGERS (location_type l)
+ {
+ return symbol_type (token::TOKEN_LOGGERS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LOGGERS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LOGGERS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NAME (location_type l)
+ {
+ return symbol_type (token::TOKEN_NAME, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NAME (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NAME, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_OUTPUT_OPTIONS (location_type l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT_OPTIONS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_OUTPUT_OPTIONS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT_OPTIONS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_OUTPUT (location_type l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_OUTPUT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DEBUGLEVEL (location_type l)
+ {
+ return symbol_type (token::TOKEN_DEBUGLEVEL, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DEBUGLEVEL (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DEBUGLEVEL, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SEVERITY (location_type l)
+ {
+ return symbol_type (token::TOKEN_SEVERITY, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SEVERITY (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SEVERITY, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_FLUSH (location_type l)
+ {
+ return symbol_type (token::TOKEN_FLUSH, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_FLUSH (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_FLUSH, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_MAXSIZE (location_type l)
+ {
+ return symbol_type (token::TOKEN_MAXSIZE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_MAXSIZE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_MAXSIZE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_MAXVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_MAXVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_MAXVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_MAXVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_PATTERN (location_type l)
+ {
+ return symbol_type (token::TOKEN_PATTERN, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_PATTERN (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_PATTERN, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_START_JSON (location_type l)
+ {
+ return symbol_type (token::TOKEN_START_JSON, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_START_JSON (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_START_JSON, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_START_AGENT (location_type l)
+ {
+ return symbol_type (token::TOKEN_START_AGENT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_START_AGENT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_START_AGENT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_START_SUB_AGENT (location_type l)
+ {
+ return symbol_type (token::TOKEN_START_SUB_AGENT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_START_SUB_AGENT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_START_SUB_AGENT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_STRING (std::string v, location_type l)
+ {
+ return symbol_type (token::TOKEN_STRING, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_STRING (const std::string& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_STRING, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_INTEGER (int64_t v, location_type l)
+ {
+ return symbol_type (token::TOKEN_INTEGER, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_INTEGER (const int64_t& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_INTEGER, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_FLOAT (double v, location_type l)
+ {
+ return symbol_type (token::TOKEN_FLOAT, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_FLOAT (const double& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_FLOAT, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_BOOLEAN (bool v, location_type l)
+ {
+ return symbol_type (token::TOKEN_BOOLEAN, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_BOOLEAN (const bool& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_BOOLEAN, v, l);
+ }
+#endif
+
+
+ class context
+ {
+ public:
+ context (const AgentParser& yyparser, const symbol_type& yyla);
+ const symbol_type& lookahead () const YY_NOEXCEPT { return yyla_; }
+ symbol_kind_type token () const YY_NOEXCEPT { return yyla_.kind (); }
+ const location_type& location () const YY_NOEXCEPT { return yyla_.location; }
+
+ /// Put in YYARG at most YYARGN of the expected tokens, and return the
+ /// number of tokens stored in YYARG. If YYARG is null, return the
+ /// number of expected tokens (guaranteed to be less than YYNTOKENS).
+ int expected_tokens (symbol_kind_type yyarg[], int yyargn) const;
+
+ private:
+ const AgentParser& yyparser_;
+ const symbol_type& yyla_;
+ };
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Non copyable.
+ AgentParser (const AgentParser&);
+ /// Non copyable.
+ AgentParser& operator= (const AgentParser&);
+#endif
+
+
+ /// Stored state numbers (used for stacks).
+ typedef short state_type;
+
+ /// The arguments of the error message.
+ int yy_syntax_error_arguments_ (const context& yyctx,
+ symbol_kind_type yyarg[], int yyargn) const;
+
+ /// Generate an error message.
+ /// \param yyctx the context in which the error occurred.
+ virtual std::string yysyntax_error_ (const context& yyctx) const;
+ /// Compute post-reduction state.
+ /// \param yystate the current state
+ /// \param yysym the nonterminal to push on the stack
+ static state_type yy_lr_goto_state_ (state_type yystate, int yysym);
+
+ /// Whether the given \c yypact_ value indicates a defaulted state.
+ /// \param yyvalue the value to check
+ static bool yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT;
+
+ /// Whether the given \c yytable_ value indicates a syntax error.
+ /// \param yyvalue the value to check
+ static bool yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT;
+
+ static const short yypact_ninf_;
+ static const signed char yytable_ninf_;
+
+ /// Convert a scanner token kind \a t to a symbol kind.
+ /// In theory \a t should be a token_kind_type, but character literals
+ /// are valid, yet not members of the token_kind_type enum.
+ static symbol_kind_type yytranslate_ (int t) YY_NOEXCEPT;
+
+ /// Convert the symbol name \a n to a form suitable for a diagnostic.
+ static std::string yytnamerr_ (const char *yystr);
+
+ /// For a symbol, its name in clear.
+ static const char* const yytname_[];
+
+
+ // Tables.
+ // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ // STATE-NUM.
+ static const short yypact_[];
+
+ // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ // Performed when YYTABLE does not specify something else to do. Zero
+ // means the default is an error.
+ static const unsigned char yydefact_[];
+
+ // YYPGOTO[NTERM-NUM].
+ static const short yypgoto_[];
+
+ // YYDEFGOTO[NTERM-NUM].
+ static const short yydefgoto_[];
+
+ // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ // positive, shift that token. If negative, reduce the rule whose
+ // number is the opposite. If YYTABLE_NINF, syntax error.
+ static const short yytable_[];
+
+ static const short yycheck_[];
+
+ // YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
+ // state STATE-NUM.
+ static const unsigned char yystos_[];
+
+ // YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.
+ static const unsigned char yyr1_[];
+
+ // YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.
+ static const signed char yyr2_[];
+
+
+#if AGENT_DEBUG
+ // YYRLINE[YYN] -- Source line where rule number YYN was defined.
+ static const short yyrline_[];
+ /// Report on the debug stream that the rule \a r is going to be reduced.
+ virtual void yy_reduce_print_ (int r) const;
+ /// Print the state stack on the debug stream.
+ virtual void yy_stack_print_ () const;
+
+ /// Debugging level.
+ int yydebug_;
+ /// Debug stream.
+ std::ostream* yycdebug_;
+
+ /// \brief Display a symbol kind, value and location.
+ /// \param yyo The output stream.
+ /// \param yysym The symbol.
+ template <typename Base>
+ void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const;
+#endif
+
+ /// \brief Reclaim the memory associated to a symbol.
+ /// \param yymsg Why this token is reclaimed.
+ /// If null, print nothing.
+ /// \param yysym The symbol.
+ template <typename Base>
+ void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const;
+
+ private:
+ /// Type access provider for state based symbols.
+ struct by_state
+ {
+ /// Default constructor.
+ by_state () YY_NOEXCEPT;
+
+ /// The symbol kind as needed by the constructor.
+ typedef state_type kind_type;
+
+ /// Constructor.
+ by_state (kind_type s) YY_NOEXCEPT;
+
+ /// Copy constructor.
+ by_state (const by_state& that) YY_NOEXCEPT;
+
+ /// Record that this symbol is empty.
+ void clear () YY_NOEXCEPT;
+
+ /// Steal the symbol kind from \a that.
+ void move (by_state& that);
+
+ /// The symbol kind (corresponding to \a state).
+ /// \a symbol_kind::S_YYEMPTY when empty.
+ symbol_kind_type kind () const YY_NOEXCEPT;
+
+ /// The state number used to denote an empty symbol.
+ /// We use the initial state, as it does not have a value.
+ enum { empty_state = 0 };
+
+ /// The state.
+ /// \a empty when empty.
+ state_type state;
+ };
+
+ /// "Internal" symbol: element of the stack.
+ struct stack_symbol_type : basic_symbol<by_state>
+ {
+ /// Superclass.
+ typedef basic_symbol<by_state> super_type;
+ /// Construct an empty symbol.
+ stack_symbol_type ();
+ /// Move or copy construction.
+ stack_symbol_type (YY_RVREF (stack_symbol_type) that);
+ /// Steal the contents from \a sym to build this.
+ stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym);
+#if YY_CPLUSPLUS < 201103L
+ /// Assignment, needed by push_back by some old implementations.
+ /// Moves the contents of that.
+ stack_symbol_type& operator= (stack_symbol_type& that);
+
+ /// Assignment, needed by push_back by other implementations.
+ /// Needed by some other old implementations.
+ stack_symbol_type& operator= (const stack_symbol_type& that);
+#endif
+ };
+
+ /// A stack with random access from its top.
+ template <typename T, typename S = std::vector<T> >
+ class stack
+ {
+ public:
+ // Hide our reversed order.
+ typedef typename S::iterator iterator;
+ typedef typename S::const_iterator const_iterator;
+ typedef typename S::size_type size_type;
+ typedef typename std::ptrdiff_t index_type;
+
+ stack (size_type n = 200) YY_NOEXCEPT
+ : seq_ (n)
+ {}
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Non copyable.
+ stack (const stack&) = delete;
+ /// Non copyable.
+ stack& operator= (const stack&) = delete;
+#endif
+
+ /// Random access.
+ ///
+ /// Index 0 returns the topmost element.
+ const T&
+ operator[] (index_type i) const
+ {
+ return seq_[size_type (size () - 1 - i)];
+ }
+
+ /// Random access.
+ ///
+ /// Index 0 returns the topmost element.
+ T&
+ operator[] (index_type i)
+ {
+ return seq_[size_type (size () - 1 - i)];
+ }
+
+ /// Steal the contents of \a t.
+ ///
+ /// Close to move-semantics.
+ void
+ push (YY_MOVE_REF (T) t)
+ {
+ seq_.push_back (T ());
+ operator[] (0).move (t);
+ }
+
+ /// Pop elements from the stack.
+ void
+ pop (std::ptrdiff_t n = 1) YY_NOEXCEPT
+ {
+ for (; 0 < n; --n)
+ seq_.pop_back ();
+ }
+
+ /// Pop all elements from the stack.
+ void
+ clear () YY_NOEXCEPT
+ {
+ seq_.clear ();
+ }
+
+ /// Number of elements on the stack.
+ index_type
+ size () const YY_NOEXCEPT
+ {
+ return index_type (seq_.size ());
+ }
+
+ /// Iterator on top of the stack (going downwards).
+ const_iterator
+ begin () const YY_NOEXCEPT
+ {
+ return seq_.begin ();
+ }
+
+ /// Bottom of the stack.
+ const_iterator
+ end () const YY_NOEXCEPT
+ {
+ return seq_.end ();
+ }
+
+ /// Present a slice of the top of a stack.
+ class slice
+ {
+ public:
+ slice (const stack& stack, index_type range) YY_NOEXCEPT
+ : stack_ (stack)
+ , range_ (range)
+ {}
+
+ const T&
+ operator[] (index_type i) const
+ {
+ return stack_[range_ - i];
+ }
+
+ private:
+ const stack& stack_;
+ index_type range_;
+ };
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Non copyable.
+ stack (const stack&);
+ /// Non copyable.
+ stack& operator= (const stack&);
+#endif
+ /// The wrapped container.
+ S seq_;
+ };
+
+
+ /// Stack type.
+ typedef stack<stack_symbol_type> stack_type;
+
+ /// The stack.
+ stack_type yystack_;
+
+ /// Push a new state on the stack.
+ /// \param m a debug message to display
+ /// if null, no trace is output.
+ /// \param sym the symbol
+ /// \warning the contents of \a s.value is stolen.
+ void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym);
+
+ /// Push a new look ahead token on the state on the stack.
+ /// \param m a debug message to display
+ /// if null, no trace is output.
+ /// \param s the state
+ /// \param sym the symbol (for its value and location).
+ /// \warning the contents of \a sym.value is stolen.
+ void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym);
+
+ /// Pop \a n symbols from the stack.
+ void yypop_ (int n = 1) YY_NOEXCEPT;
+
+ /// Constants.
+ enum
+ {
+ yylast_ = 252, ///< Last index in yytable_.
+ yynnts_ = 122, ///< Number of nonterminal symbols.
+ yyfinal_ = 8 ///< Termination state number.
+ };
+
+
+ // User arguments.
+ isc::agent::ParserContext& ctx;
+
+ };
+
+ inline
+ AgentParser::symbol_kind_type
+ AgentParser::yytranslate_ (int t) YY_NOEXCEPT
+ {
+ // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to
+ // TOKEN-NUM as returned by yylex.
+ static
+ const signed char
+ translate_table[] =
+ {
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55
+ };
+ // Last valid token kind.
+ const int code_max = 310;
+
+ if (t <= 0)
+ return symbol_kind::S_YYEOF;
+ else if (t <= code_max)
+ return static_cast <symbol_kind_type> (translate_table[t]);
+ else
+ return symbol_kind::S_YYUNDEF;
+ }
+
+ // basic_symbol.
+ template <typename Base>
+ AgentParser::basic_symbol<Base>::basic_symbol (const basic_symbol& that)
+ : Base (that)
+ , value ()
+ , location (that.location)
+ {
+ switch (this->kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.copy< ElementPtr > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.copy< bool > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.copy< double > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.copy< int64_t > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.copy< std::string > (YY_MOVE (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+
+
+
+ template <typename Base>
+ AgentParser::symbol_kind_type
+ AgentParser::basic_symbol<Base>::type_get () const YY_NOEXCEPT
+ {
+ return this->kind ();
+ }
+
+
+ template <typename Base>
+ bool
+ AgentParser::basic_symbol<Base>::empty () const YY_NOEXCEPT
+ {
+ return this->kind () == symbol_kind::S_YYEMPTY;
+ }
+
+ template <typename Base>
+ void
+ AgentParser::basic_symbol<Base>::move (basic_symbol& s)
+ {
+ super_type::move (s);
+ switch (this->kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ case symbol_kind::S_auth_type_value: // auth_type_value
+ value.move< ElementPtr > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (YY_MOVE (s.value));
+ break;
+
+ default:
+ break;
+ }
+
+ location = YY_MOVE (s.location);
+ }
+
+ // by_kind.
+ inline
+ AgentParser::by_kind::by_kind () YY_NOEXCEPT
+ : kind_ (symbol_kind::S_YYEMPTY)
+ {}
+
+#if 201103L <= YY_CPLUSPLUS
+ inline
+ AgentParser::by_kind::by_kind (by_kind&& that) YY_NOEXCEPT
+ : kind_ (that.kind_)
+ {
+ that.clear ();
+ }
+#endif
+
+ inline
+ AgentParser::by_kind::by_kind (const by_kind& that) YY_NOEXCEPT
+ : kind_ (that.kind_)
+ {}
+
+ inline
+ AgentParser::by_kind::by_kind (token_kind_type t) YY_NOEXCEPT
+ : kind_ (yytranslate_ (t))
+ {}
+
+
+
+ inline
+ void
+ AgentParser::by_kind::clear () YY_NOEXCEPT
+ {
+ kind_ = symbol_kind::S_YYEMPTY;
+ }
+
+ inline
+ void
+ AgentParser::by_kind::move (by_kind& that)
+ {
+ kind_ = that.kind_;
+ that.clear ();
+ }
+
+ inline
+ AgentParser::symbol_kind_type
+ AgentParser::by_kind::kind () const YY_NOEXCEPT
+ {
+ return kind_;
+ }
+
+
+ inline
+ AgentParser::symbol_kind_type
+ AgentParser::by_kind::type_get () const YY_NOEXCEPT
+ {
+ return this->kind ();
+ }
+
+
+#line 14 "agent_parser.yy"
+} } // isc::agent
+#line 2530 "agent_parser.h"
+
+
+
+
+#endif // !YY_AGENT_AGENT_PARSER_H_INCLUDED
diff --git a/src/bin/agent/agent_parser.yy b/src/bin/agent/agent_parser.yy
new file mode 100644
index 0000000..4b3d767
--- /dev/null
+++ b/src/bin/agent/agent_parser.yy
@@ -0,0 +1,869 @@
+/* Copyright (C) 2017-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/. */
+
+%skeleton "lalr1.cc" /* -*- C++ -*- */
+%require "3.3.0"
+%defines
+%define api.parser.class {AgentParser}
+%define api.prefix {agent_}
+%define api.token.constructor
+%define api.value.type variant
+%define api.namespace {isc::agent}
+%define parse.assert
+%code requires
+{
+#include <string>
+#include <cc/data.h>
+#include <boost/lexical_cast.hpp>
+#include <agent/parser_context_decl.h>
+
+using namespace isc::agent;
+using namespace isc::data;
+using namespace std;
+}
+// The parsing context.
+%param { isc::agent::ParserContext& ctx }
+%locations
+%define parse.trace
+%define parse.error verbose
+%code
+{
+#include <agent/parser_context.h>
+}
+
+
+%define api.token.prefix {TOKEN_}
+// Tokens in an order which makes sense and related to the intended use.
+// Actual regexps for tokens are defined in agent_lexer.ll.
+%token
+ END 0 "end of file"
+ COMMA ","
+ COLON ":"
+ LSQUARE_BRACKET "["
+ RSQUARE_BRACKET "]"
+ LCURLY_BRACKET "{"
+ RCURLY_BRACKET "}"
+ NULL_TYPE "null"
+
+ CONTROL_AGENT "Control-agent"
+ HTTP_HOST "http-host"
+ HTTP_PORT "http-port"
+
+ USER_CONTEXT "user-context"
+ COMMENT "comment"
+
+ AUTHENTICATION "authentication"
+ TYPE "type"
+ BASIC "basic"
+ REALM "realm"
+ DIRECTORY "directory"
+ CLIENTS "clients"
+ USER "user"
+ USER_FILE "user-file"
+ PASSWORD "password"
+ PASSWORD_FILE "password-file"
+
+ TRUST_ANCHOR "trust-anchor"
+ CERT_FILE "cert-file"
+ KEY_FILE "key-file"
+ CERT_REQUIRED "cert-required"
+
+ CONTROL_SOCKETS "control-sockets"
+ DHCP4_SERVER "dhcp4"
+ DHCP6_SERVER "dhcp6"
+ D2_SERVER "d2"
+ SOCKET_NAME "socket-name"
+ SOCKET_TYPE "socket-type"
+ UNIX "unix"
+
+ HOOKS_LIBRARIES "hooks-libraries"
+ LIBRARY "library"
+ PARAMETERS "parameters"
+
+ LOGGERS "loggers"
+ NAME "name"
+ OUTPUT_OPTIONS "output_options"
+ OUTPUT "output"
+ DEBUGLEVEL "debuglevel"
+ SEVERITY "severity"
+ FLUSH "flush"
+ MAXSIZE "maxsize"
+ MAXVER "maxver"
+ PATTERN "pattern"
+
+ // Not real tokens, just a way to signal what the parser is expected to
+ // parse. This define the starting point. It either can be full grammar
+ // (START_AGENT), part of the grammar related to control-agent (START_SUB_AGENT)
+ // or can be any valid JSON (START_JSON)
+ START_JSON
+ START_AGENT
+ START_SUB_AGENT
+;
+
+%token <std::string> STRING "constant string"
+%token <int64_t> INTEGER "integer"
+%token <double> FLOAT "floating point"
+%token <bool> BOOLEAN "boolean"
+
+%type <ElementPtr> value
+%type <ElementPtr> map_value
+%type <ElementPtr> socket_type_value
+%type <ElementPtr> auth_type_value
+
+%printer { yyoutput << $$; } <*>;
+
+%%
+
+// The whole grammar starts with a map, because the config file
+// consists of only Control-Agent entry in one big { }.
+%start start;
+
+// The starting token can be one of those listed below. Note these are
+// "fake" tokens. They're produced by the lexer before any input text
+// is parsed.
+start: START_JSON { ctx.ctx_ = ctx.NO_KEYWORDS; } json
+ | START_AGENT { ctx.ctx_ = ctx.CONFIG; } agent_syntax_map
+ | START_SUB_AGENT { ctx.ctx_ = ctx.AGENT; } sub_agent
+ ;
+
+// This rule defines a "shortcut". Instead of specifying the whole structure
+// expected by full grammar, we can tell the parser to start from content of
+// the Control-agent. This is very useful for unit-testing, so we don't need
+// to repeat the outer map and "Control-agent" map. We can simply provide
+// the contents of that map.
+sub_agent: LCURLY_BRACKET {
+ // Parse the Control-agent map
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(m);
+} global_params RCURLY_BRACKET {
+ // parsing completed
+};
+
+// --- generic JSON parser -----------------------------------------------------
+
+// json expression can be a value. What value means is defined below.
+json: value {
+ // Push back the JSON value on the stack
+ ctx.stack_.push_back($1);
+};
+
+// Rules for value. This can be one of the primary types allowed in JSON.
+value: INTEGER { $$ = ElementPtr(new IntElement($1, ctx.loc2pos(@1))); }
+ | FLOAT { $$ = ElementPtr(new DoubleElement($1, ctx.loc2pos(@1))); }
+ | BOOLEAN { $$ = ElementPtr(new BoolElement($1, ctx.loc2pos(@1))); }
+ | STRING { $$ = ElementPtr(new StringElement($1, ctx.loc2pos(@1))); }
+ | NULL_TYPE { $$ = ElementPtr(new NullElement(ctx.loc2pos(@1))); }
+ | map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
+ | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
+ ;
+
+// Rule for map. It will start with {, have some content and will end with }.
+map: LCURLY_BRACKET {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(m);
+} map_content RCURLY_BRACKET {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+};
+
+map_value: map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); };
+
+// Rule for map content. In some cases it is allowed to have an empty map,
+// so we should say that explicitly. In most cases, though, there will
+// be some actual content inside. That's defined by not_empty_map
+map_content: %empty // empty map
+ | not_empty_map
+ ;
+
+// Rule for content of the map. It can have one of two formats:
+// 1) string: value
+// 2) non_empty_map , string: value
+// The first case covers a single entry, while the second case
+// covers all longer lists recursively.
+not_empty_map: STRING COLON value {
+ // map containing a single entry
+ ctx.unique($1, ctx.loc2pos(@1));
+ ctx.stack_.back()->set($1, $3);
+ }
+ | not_empty_map COMMA STRING COLON value {
+ // map consisting of a shorter map followed by
+ // comma and string:value
+ ctx.unique($3, ctx.loc2pos(@3));
+ ctx.stack_.back()->set($3, $5);
+ }
+ | not_empty_map COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+list_generic: LSQUARE_BRACKET {
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(l);
+} list_content RSQUARE_BRACKET {
+};
+
+list_content: %empty // Empty list
+ | not_empty_list
+ ;
+
+not_empty_list: value {
+ // List consisting of a single element.
+ ctx.stack_.back()->add($1);
+ }
+ | not_empty_list COMMA value {
+ // List ending with , and a value.
+ ctx.stack_.back()->add($3);
+ }
+ | not_empty_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// --- generic JSON parser ends here -------------------------------------------
+
+// --- syntax checking parser starts here --------------------------------------
+
+// Unknown keyword in a map. This clever rule can be added to any map
+// if you want to have a nice expression printed when unknown (mistyped?)
+// parameter is found.
+unknown_map_entry: STRING COLON {
+ const std::string& where = ctx.contextName();
+ const std::string& keyword = $1;
+ error(@1,
+ "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
+};
+
+// This defines the top-level { } that holds only Control-agent object.
+agent_syntax_map: LCURLY_BRACKET {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(m);
+} global_object RCURLY_BRACKET {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+};
+
+// This represents the single top level entry, e.g. Control-agent.
+global_object: CONTROL_AGENT {
+ // Let's create a MapElement that will represent it, add it to the
+ // top level map (that's already on the stack) and put the new map
+ // on the stack as well, so child elements will be able to add
+ // themselves to it.
+ ctx.unique("Control-agent", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("Control-agent", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.AGENT);
+} COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
+ // Ok, we're done with parsing control-agent. Let's take the map
+ // off the stack.
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+ | global_object_comma
+ ;
+
+global_object_comma: global_object COMMA {
+ ctx.warnAboutExtraCommas(@2);
+};
+
+global_params: global_param
+ | global_params COMMA global_param
+ | global_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// These are the parameters that are allowed in the top-level for
+// Dhcp6.
+global_param: http_host
+ | http_port
+ | trust_anchor
+ | cert_file
+ | key_file
+ | cert_required
+ | authentication
+ | control_sockets
+ | hooks_libraries
+ | loggers
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+http_host: HTTP_HOST {
+ ctx.unique("http-host", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr host(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("http-host", host);
+ ctx.leave();
+};
+
+http_port: HTTP_PORT COLON INTEGER {
+ ctx.unique("http-port", ctx.loc2pos(@1));
+ ElementPtr prf(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("http-port", prf);
+};
+
+trust_anchor: TRUST_ANCHOR {
+ ctx.unique("trust-anchor", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr ca(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("trust-anchor", ca);
+ ctx.leave();
+};
+
+cert_file: CERT_FILE {
+ ctx.unique("cert-file", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr cert(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("cert-file", cert);
+ ctx.leave();
+};
+
+key_file: KEY_FILE {
+ ctx.unique("key-file", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr key(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("key-file", key);
+ ctx.leave();
+};
+
+cert_required: CERT_REQUIRED COLON BOOLEAN {
+ ctx.unique("cert-required", ctx.loc2pos(@1));
+ ElementPtr req(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("cert-required", req);
+};
+
+user_context: USER_CONTEXT {
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON map_value {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context = $4;
+ ConstElementPtr old = parent->get("user-context");
+
+ // Handle already existing user context
+ if (old) {
+ // Check if it was a comment or a duplicate
+ if ((old->size() != 1) || !old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context entries (previous at "
+ << old->getPosition().str() << ")";
+ error(@1, msg.str());
+ }
+ // Merge the comment
+ user_context->set("comment", old->get("comment"));
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+};
+
+comment: COMMENT {
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context(new MapElement(ctx.loc2pos(@1)));
+ ElementPtr comment(new StringElement($4, ctx.loc2pos(@4)));
+ user_context->set("comment", comment);
+
+ // Handle already existing user context
+ ConstElementPtr old = parent->get("user-context");
+ if (old) {
+ // Check for duplicate comment
+ if (old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context/comment entries (previous at "
+ << old->getPosition().str() << ")";
+ error(@1, msg.str());
+ }
+ // Merge the user context in the comment
+ merge(user_context, old);
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+};
+
+// --- hooks-libraries ---------------------------------------------------------
+hooks_libraries: HOOKS_LIBRARIES {
+ ctx.unique("hooks-libraries", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("hooks-libraries", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.HOOKS_LIBRARIES);
+} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+hooks_libraries_list: %empty
+ | not_empty_hooks_libraries_list
+ ;
+
+not_empty_hooks_libraries_list: hooks_library
+ | not_empty_hooks_libraries_list COMMA hooks_library
+ | not_empty_hooks_libraries_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+hooks_library: LCURLY_BRACKET {
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+} hooks_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+hooks_params: hooks_param
+ | hooks_params COMMA hooks_param
+ | hooks_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ | unknown_map_entry
+ ;
+
+hooks_param: library
+ | parameters
+ ;
+
+library: LIBRARY {
+ ctx.unique("library", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("library", lib);
+ ctx.leave();
+};
+
+parameters: PARAMETERS {
+ ctx.unique("parameters", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON map_value {
+ ctx.stack_.back()->set("parameters", $4);
+ ctx.leave();
+};
+
+// --- hooks-libraries end here ------------------------------------------------
+
+// --- control-sockets starts here ---------------------------------------------
+control_sockets: CONTROL_SOCKETS COLON LCURLY_BRACKET {
+ ctx.unique("control-sockets", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("control-sockets", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.CONTROL_SOCKETS);
+} control_sockets_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// This defines what kind of control-sockets parameters we allow.
+// Note that empty map is not allowed here, because at least one control socket
+// is required.
+control_sockets_params: control_socket
+ | control_sockets_params COMMA control_socket
+ | control_sockets_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// We currently support three types of sockets: DHCPv4, DHCPv6 and D2
+// (even though D2 socket support is not yet implemented).
+control_socket: dhcp4_server_socket
+ | dhcp6_server_socket
+ | d2_server_socket
+ | unknown_map_entry
+ ;
+
+// That's an entry for dhcp4 socket.
+dhcp4_server_socket: DHCP4_SERVER {
+ ctx.unique("dhcp4", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("dhcp4", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// That's an entry for dhcp6 socket.
+dhcp6_server_socket: DHCP6_SERVER {
+ ctx.unique("dhcp6", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("dhcp6", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// That's an entry for d2 socket.
+d2_server_socket: D2_SERVER {
+ ctx.unique("d2", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("d2", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// Socket parameters consist of one or more parameters.
+control_socket_params: control_socket_param
+ | control_socket_params COMMA control_socket_param
+ | control_socket_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// We currently support two socket parameters: type and name.
+control_socket_param: socket_name
+ | socket_type
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+// This rule defines socket-name parameter.
+socket_name: SOCKET_NAME {
+ ctx.unique("socket-name", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("socket-name", name);
+ ctx.leave();
+};
+
+// This rule specifies socket type.
+socket_type: SOCKET_TYPE {
+ ctx.unique("socket-type", ctx.loc2pos(@1));
+ ctx.enter(ctx.SOCKET_TYPE);
+} COLON socket_type_value {
+ ctx.stack_.back()->set("socket-type", $4);
+ ctx.leave();
+};
+
+// We currently allow only unix domain sockets
+socket_type_value : UNIX { $$ = ElementPtr(new StringElement("unix", ctx.loc2pos(@1))); }
+ ;
+
+// --- control-sockets end here ------------------------------------------------
+
+// --- authentication starts here -----------------------------------------------------
+
+authentication: AUTHENTICATION {
+ ctx.unique("authentication", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("authentication", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.AUTHENTICATION);
+} COLON LCURLY_BRACKET auth_params RCURLY_BRACKET {
+ // The type parameter is required
+ ctx.require("type", ctx.loc2pos(@4), ctx.loc2pos(@6));
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+auth_params: auth_param
+ | auth_params COMMA auth_param
+ | auth_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+auth_param: auth_type
+ | realm
+ | directory
+ | clients
+ | comment
+ | user_context
+ | unknown_map_entry
+ ;
+
+auth_type: TYPE {
+ ctx.unique("type", ctx.loc2pos(@1));
+ ctx.enter(ctx.AUTH_TYPE);
+} COLON auth_type_value {
+ ctx.stack_.back()->set("type", $4);
+ ctx.leave();
+};
+
+auth_type_value: BASIC { $$ = ElementPtr(new StringElement("basic", ctx.loc2pos(@1))); }
+ ;
+
+realm: REALM {
+ ctx.unique("realm", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr realm(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("realm", realm);
+ ctx.leave();
+};
+
+directory: DIRECTORY {
+ ctx.unique("directory", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr directory(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("directory", directory);
+ ctx.leave();
+};
+
+clients: CLIENTS {
+ ctx.unique("clients", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("clients", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.CLIENTS);
+} COLON LSQUARE_BRACKET clients_list RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+clients_list: %empty
+ | not_empty_clients_list
+ ;
+
+not_empty_clients_list: basic_auth
+ | not_empty_clients_list COMMA basic_auth
+ | not_empty_clients_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+basic_auth: LCURLY_BRACKET {
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+} clients_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+clients_params: clients_param
+ | clients_params COMMA clients_param
+ | clients_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+clients_param: user
+ | user_file
+ | password
+ | password_file
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+user: USER {
+ ctx.unique("user", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr user(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("user", user);
+ ctx.leave();
+};
+
+user_file: USER_FILE {
+ ctx.unique("user-file", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr user(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("user-file", user);
+ ctx.leave();
+};
+
+password: PASSWORD {
+ ctx.unique("password", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr password(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("password", password);
+ ctx.leave();
+};
+
+password_file: PASSWORD_FILE {
+ ctx.unique("password-file", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr password(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("password-file", password);
+ ctx.leave();
+};
+
+// --- authentication end here -----------------------------------------------------
+
+// --- Loggers starts here -----------------------------------------------------
+
+loggers: LOGGERS {
+ ctx.unique("loggers", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("loggers", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.LOGGERS);
+} COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// These are the parameters allowed in loggers: either one logger
+// entry or multiple entries separate by commas.
+loggers_entries: logger_entry
+ | loggers_entries COMMA logger_entry
+ | loggers_entries COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// This defines a single entry defined in loggers.
+logger_entry: LCURLY_BRACKET {
+ ElementPtr l(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(l);
+ ctx.stack_.push_back(l);
+} logger_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+logger_params: logger_param
+ | logger_params COMMA logger_param
+ | logger_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+logger_param: name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+name: NAME {
+ ctx.unique("name", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("name", name);
+ ctx.leave();
+};
+
+debuglevel: DEBUGLEVEL COLON INTEGER {
+ ctx.unique("debuglevel", ctx.loc2pos(@1));
+ ElementPtr dl(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("debuglevel", dl);
+};
+
+severity: SEVERITY {
+ ctx.unique("severity", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("severity", sev);
+ ctx.leave();
+};
+
+output_options_list: OUTPUT_OPTIONS {
+ ctx.unique("output_options", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("output_options", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.OUTPUT_OPTIONS);
+} COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+output_options_list_content: output_entry
+ | output_options_list_content COMMA output_entry
+ | output_options_list_content COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+output_entry: LCURLY_BRACKET {
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+} output_params_list RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+output_params_list: output_params
+ | output_params_list COMMA output_params
+ | output_params_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+output_params: output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+ ;
+
+output: OUTPUT {
+ ctx.unique("output", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("output", sev);
+ ctx.leave();
+};
+
+flush: FLUSH COLON BOOLEAN {
+ ctx.unique("flush", ctx.loc2pos(@1));
+ ElementPtr flush(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("flush", flush);
+}
+
+maxsize: MAXSIZE COLON INTEGER {
+ ctx.unique("maxsize", ctx.loc2pos(@1));
+ ElementPtr maxsize(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("maxsize", maxsize);
+}
+
+maxver: MAXVER COLON INTEGER {
+ ctx.unique("maxver", ctx.loc2pos(@1));
+ ElementPtr maxver(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("maxver", maxver);
+}
+
+pattern: PATTERN {
+ ctx.unique("pattern", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("pattern", sev);
+ ctx.leave();
+};
+
+%%
+
+void
+isc::agent::AgentParser::error(const location_type& loc,
+ const std::string& what)
+{
+ ctx.error(loc, what);
+}
diff --git a/src/bin/agent/ca_cfg_mgr.cc b/src/bin/agent/ca_cfg_mgr.cc
new file mode 100644
index 0000000..f2f8caa
--- /dev/null
+++ b/src/bin/agent/ca_cfg_mgr.cc
@@ -0,0 +1,216 @@
+// 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/.
+
+#include <config.h>
+
+#include <agent/ca_cfg_mgr.h>
+#include <agent/ca_log.h>
+#include <agent/simple_parser.h>
+#include <cc/simple_parser.h>
+#include <cc/command_interpreter.h>
+#include <http/basic_auth_config.h>
+#include <exceptions/exceptions.h>
+
+using namespace isc::config;
+using namespace isc::dhcp;
+using namespace isc::process;
+using namespace isc::data;
+
+namespace isc {
+namespace agent {
+
+CtrlAgentCfgContext::CtrlAgentCfgContext()
+ : http_host_(""), http_port_(0),
+ trust_anchor_(""), cert_file_(""), key_file_(""), cert_required_(true) {
+}
+
+CtrlAgentCfgContext::CtrlAgentCfgContext(const CtrlAgentCfgContext& orig)
+ : ConfigBase(), ctrl_sockets_(orig.ctrl_sockets_),
+ http_host_(orig.http_host_), http_port_(orig.http_port_),
+ trust_anchor_(orig.trust_anchor_), cert_file_(orig.cert_file_),
+ key_file_(orig.key_file_), cert_required_(orig.cert_required_),
+ hooks_config_(orig.hooks_config_), auth_config_(orig.auth_config_) {
+}
+
+CtrlAgentCfgMgr::CtrlAgentCfgMgr()
+ : DCfgMgrBase(ConfigPtr(new CtrlAgentCfgContext())) {
+}
+
+CtrlAgentCfgMgr::~CtrlAgentCfgMgr() {
+}
+
+std::string
+CtrlAgentCfgMgr::getConfigSummary(const uint32_t /*selection*/) {
+
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+
+ // First print the http stuff.
+ std::ostringstream s;
+ s << "listening on " << ctx->getHttpHost() << ", port "
+ << ctx->getHttpPort();
+
+ // When TLS is setup print its config.
+ if (!ctx->getTrustAnchor().empty()) {
+ s << ", trust anchor " << ctx->getTrustAnchor()
+ << ", cert file " << ctx->getCertFile()
+ << ", key file " << ctx->getKeyFile();
+ if (ctx->getCertRequired()) {
+ s << ", client certs are required";
+ } else {
+ s << ", client certs are optional";
+ }
+ }
+ s << ", control sockets: ";
+
+ // Then print the control-sockets
+ s << ctx->getControlSocketInfoSummary();
+
+ // Add something if authentication is required.
+ const isc::http::HttpAuthConfigPtr& auth = ctx->getAuthConfig();
+ if (auth && !auth->empty()) {
+ s << ", requires basic HTTP authentication";
+ }
+
+ // Finally, print the hook libraries names
+ const isc::hooks::HookLibsCollection libs = ctx->getHooksConfig().get();
+ s << ", " << libs.size() << " lib(s):";
+ for (auto lib = libs.begin(); lib != libs.end(); ++lib) {
+ s << lib->first << " ";
+ }
+
+ return (s.str());
+}
+
+ConfigPtr
+CtrlAgentCfgMgr::createNewContext() {
+ return (ConfigPtr(new CtrlAgentCfgContext()));
+}
+
+ConstElementPtr
+CtrlAgentCfgMgr::parse(ConstElementPtr config_set, bool check_only) {
+ // Do a sanity check first.
+ if (!config_set) {
+ isc_throw(DhcpConfigError, "Mandatory config parameter not provided");
+ }
+
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+
+ // Set the defaults
+ ElementPtr cfg = boost::const_pointer_cast<Element>(config_set);
+ AgentSimpleParser::setAllDefaults(cfg);
+
+ // And parse the configuration.
+ ConstElementPtr answer;
+ std::string excuse;
+ try {
+ // Do the actual parsing
+ AgentSimpleParser parser;
+ parser.checkTlsSetup(cfg);
+ parser.parse(ctx, cfg, check_only);
+ } catch (const isc::Exception& ex) {
+ excuse = ex.what();
+ answer = createAnswer(CONTROL_RESULT_ERROR, excuse);
+ } catch (...) {
+ excuse = "undefined configuration parsing error";
+ answer = createAnswer(CONTROL_RESULT_ERROR, excuse);
+ }
+
+ // At this stage the answer was created only in case of exception.
+ if (answer) {
+ if (check_only) {
+ LOG_ERROR(agent_logger, CTRL_AGENT_CONFIG_CHECK_FAIL).arg(excuse);
+ } else {
+ LOG_ERROR(agent_logger, CTRL_AGENT_CONFIG_FAIL).arg(excuse);
+ }
+ return (answer);
+ }
+
+ if (check_only) {
+ answer = createAnswer(CONTROL_RESULT_SUCCESS,
+ "Configuration check successful");
+ } else {
+ answer = createAnswer(CONTROL_RESULT_SUCCESS,
+ "Configuration applied successfully.");
+ }
+
+ return (answer);
+}
+
+std::list<std::list<std::string>>
+CtrlAgentCfgMgr::jsonPathsToRedact() const {
+ static std::list<std::list<std::string>> const list({
+ {"authentication", "clients", "[]"},
+ {"hooks-libraries", "[]", "parameters", "*"},
+ });
+ return list;
+}
+
+data::ConstElementPtr
+CtrlAgentCfgContext::getControlSocketInfo(const std::string& service) const {
+ auto si = ctrl_sockets_.find(service);
+ return ((si != ctrl_sockets_.end()) ? si->second : ConstElementPtr());
+}
+
+void
+CtrlAgentCfgContext::setControlSocketInfo(const ConstElementPtr& control_socket,
+ const std::string& service) {
+ ctrl_sockets_[service] = control_socket;
+}
+
+std::string
+CtrlAgentCfgContext::getControlSocketInfoSummary() const {
+ std::ostringstream s;
+ for (auto si = ctrl_sockets_.cbegin(); si != ctrl_sockets_.end(); ++si) {
+ if (s.tellp() != 0) {
+ s << " ";
+ }
+ s << si->first;
+ }
+
+ if (s.tellp() == 0) {
+ s << "none";
+ }
+
+ return (s.str());
+}
+
+ElementPtr
+CtrlAgentCfgContext::toElement() const {
+ ElementPtr ca = ConfigBase::toElement();
+ // Set user-context
+ contextToElement(ca);
+ // Set http-host
+ ca->set("http-host", Element::create(http_host_));
+ // Set http-port
+ ca->set("http-port", Element::create(static_cast<int64_t>(http_port_)));
+ // Set TLS setup when enabled
+ if (!trust_anchor_.empty()) {
+ ca->set("trust-anchor", Element::create(trust_anchor_));
+ ca->set("cert-file", Element::create(cert_file_));
+ ca->set("key-file", Element::create(key_file_));
+ ca->set("cert-required", Element::create(cert_required_));
+ }
+ // Set authentication
+ if (auth_config_) {
+ ca->set("authentication", auth_config_->toElement());
+ }
+ ca->set("hooks-libraries", hooks_config_.toElement());
+ // Set control-sockets
+ ElementPtr control_sockets = Element::createMap();
+ for (auto si = ctrl_sockets_.cbegin(); si != ctrl_sockets_.cend(); ++si) {
+ ConstElementPtr socket = UserContext::toElement(si->second);
+ control_sockets->set(si->first, socket);
+ }
+ ca->set("control-sockets", control_sockets);
+ // Set Control-agent
+ ElementPtr result = Element::createMap();
+ result->set("Control-agent", ca);
+
+ return (result);
+}
+
+} // namespace isc::agent
+} // namespace isc
diff --git a/src/bin/agent/ca_cfg_mgr.h b/src/bin/agent/ca_cfg_mgr.h
new file mode 100644
index 0000000..6234bd4
--- /dev/null
+++ b/src/bin/agent/ca_cfg_mgr.h
@@ -0,0 +1,316 @@
+// 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/.
+
+#ifndef CTRL_AGENT_CFG_MGR_H
+#define CTRL_AGENT_CFG_MGR_H
+
+#include <cc/data.h>
+#include <hooks/hooks_config.h>
+#include <http/auth_config.h>
+#include <process/d_cfg_mgr.h>
+#include <boost/pointer_cast.hpp>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace agent {
+
+class CtrlAgentCfgContext;
+/// @brief Pointer to a configuration context.
+typedef boost::shared_ptr<CtrlAgentCfgContext> CtrlAgentCfgContextPtr;
+
+/// @brief Control Agent Configuration Context.
+///
+/// Implement the storage container for configuration context.
+/// It provides a single enclosure for the storage of configuration parameters
+/// and any other Control Agent specific information that needs to be accessible
+/// during configuration parsing as well as to the application as a whole.
+/// It is derived from the context base class, ConfigBase.
+class CtrlAgentCfgContext : public process::ConfigBase {
+public:
+
+ /// @brief Default constructor
+ CtrlAgentCfgContext();
+
+ /// @brief Creates a clone of this context object.
+ ///
+ /// Note this method does not do deep copy the information about control sockets.
+ /// That data is stored as ConstElementPtr (a shared pointer) to the actual data.
+ ///
+ /// @return A pointer to the new clone.
+ virtual process::ConfigPtr clone() {
+ return (process::ConfigPtr(new CtrlAgentCfgContext(*this)));
+ }
+
+ /// @brief Returns information about control socket
+ ///
+ /// This method returns Element tree structure that describes the control
+ /// socket (or null pointer if the socket is not defined for a particular
+ /// server type). This information is expected to be compatible with
+ /// data passed to @ref isc::config::CommandMgr::openCommandSocket.
+ ///
+ /// @param service server being controlled
+ /// @return pointer to the Element that holds control-socket map (or NULL)
+ isc::data::ConstElementPtr
+ getControlSocketInfo(const std::string& service) const;
+
+ /// @brief Sets information about the control socket
+ ///
+ /// This method stores Element tree structure that describes the control
+ /// socket. This information is expected to be compatible with
+ /// data passed to @ref isc::config::CommandMgr::openCommandSocket.
+ ///
+ /// @param control_socket Element that holds control-socket map
+ /// @param service server being controlled
+ void setControlSocketInfo(const isc::data::ConstElementPtr& control_socket,
+ const std::string& service);
+
+ /// @brief Returns socket configuration summary in a textual format.
+ std::string getControlSocketInfoSummary() const;
+
+ /// @brief Sets http-host parameter
+ ///
+ /// @param host Hostname or IP address where the agent's HTTP service
+ /// will be available.
+ void setHttpHost(const std::string& host) {
+ http_host_ = host;
+ }
+
+ /// @brief Returns http-host parameter
+ ///
+ /// @return Hostname or IP address where the agent's HTTP service is
+ /// available.
+ std::string getHttpHost() const {
+ return (http_host_);
+ }
+
+ /// @brief Sets http port
+ ///
+ /// @param port sets the TCP port the HTTP server will listen on
+ void setHttpPort(const uint16_t port) {
+ http_port_ = port;
+ }
+
+ /// @brief Returns the TCP post the HTTP server will listen on
+ uint16_t getHttpPort() const {
+ return (http_port_);
+ }
+
+ /// @brief Sets HTTP authentication configuration.
+ ///
+ /// @note Only the basic HTTP authentication is supported.
+ ///
+ /// @param auth_config HTTP authentication configuration.
+ void setAuthConfig(const isc::http::HttpAuthConfigPtr& auth_config) {
+ auth_config_ = auth_config;
+ }
+
+ /// @brief Returns HTTP authentication configuration
+ ///
+ /// @note Only the basic HTTP authentication is supported.
+ ///
+ /// @return HTTP authentication configuration.
+ const isc::http::HttpAuthConfigPtr& getAuthConfig() const {
+ return (auth_config_);
+ }
+
+ /// @brief Sets trust-anchor parameter
+ ///
+ /// @param ca Trust anchor aka Certificate Authority (can be a file name
+ /// or a directory path).
+ void setTrustAnchor(const std::string& ca) {
+ trust_anchor_ = ca;
+ }
+
+ /// @brief Returns trust-anchor parameter
+ ///
+ /// @return Trust anchor aka Certificate Authority
+ std::string getTrustAnchor() const {
+ return (trust_anchor_);
+ }
+
+ /// @brief Sets cert-file parameter
+ ///
+ /// @param cert Server certificate file name
+ void setCertFile(const std::string& cert) {
+ cert_file_ = cert;
+ }
+
+ /// @brief Returns cert-file parameter
+ ///
+ /// @return Server certificate file name
+ std::string getCertFile() const {
+ return (cert_file_);
+ }
+
+ /// @brief Sets key-file parameter
+ ///
+ /// @param key Server private key file name
+ void setKeyFile(const std::string& key) {
+ key_file_ = key;
+ }
+
+ /// @brief Returns key-file parameter
+ ///
+ /// @return Server private key file name
+ std::string getKeyFile() const {
+ return (key_file_);
+ }
+
+ /// @brief Sets cert-required parameter
+ ///
+ /// @param required Client certificates are required when true
+ /// (the default) or optional when false
+ void setCertRequired(bool required) {
+ cert_required_ = required;
+ }
+
+ /// @brief Returns cert-required parameter
+ ///
+ /// @return True when client certificates are required, false when they
+ /// are optional, the default is to require them (true).
+ bool getCertRequired() const {
+ return (cert_required_);
+ }
+
+ /// @brief Returns non-const reference to configured hooks libraries.
+ ///
+ /// @return non-const reference to configured hooks libraries.
+ isc::hooks::HooksConfig& getHooksConfig() {
+ return (hooks_config_);
+ }
+
+ /// @brief Returns const reference to configured hooks libraries.
+ ///
+ /// @return const reference to configured hooks libraries.
+ const isc::hooks::HooksConfig& getHooksConfig() const {
+ return (hooks_config_);
+ }
+
+ /// @brief Unparse a configuration object
+ ///
+ /// Returns an element which must parse into the same object, i.e.
+ /// @code
+ /// for all valid config C parse(parse(C)->toElement()) == parse(C)
+ /// @endcode
+ ///
+ /// @return a pointer to a configuration which can be parsed into
+ /// the initial configuration object
+ virtual isc::data::ElementPtr toElement() const;
+
+private:
+
+ /// @brief Private copy constructor
+ ///
+ /// It is private to forbid anyone outside of this class to make copies.
+ /// The only legal way to copy a context is to call @ref clone().
+ ///
+ /// @param orig the original context to copy from
+ CtrlAgentCfgContext(const CtrlAgentCfgContext& orig);
+
+ /// @brief Private assignment operator to avoid potential for slicing.
+ ///
+ /// @param rhs Context to be assigned.
+ CtrlAgentCfgContext& operator=(const CtrlAgentCfgContext& rhs);
+
+ /// Socket information will be stored here (for all supported servers)
+ std::map<std::string, isc::data::ConstElementPtr> ctrl_sockets_;
+
+ /// Hostname the CA should listen on.
+ std::string http_host_;
+
+ /// TCP port the CA should listen on.
+ uint16_t http_port_;
+
+ /// Trust anchor aka Certificate Authority (can be a file name or
+ /// a directory path).
+ std::string trust_anchor_;
+
+ /// Server certificate file name.
+ std::string cert_file_;
+
+ /// Server private key file name.
+ std::string key_file_;
+
+ /// Client certificates requirement flag (default is true i.e. to
+ /// require them).
+ bool cert_required_;
+
+ /// @brief Configured hooks libraries.
+ isc::hooks::HooksConfig hooks_config_;
+
+ /// @brief Configured basic HTTP authentification clients.
+ isc::http::HttpAuthConfigPtr auth_config_;
+};
+
+/// @brief Ctrl Agent Configuration Manager.
+///
+/// Provides the mechanisms for managing the Control Agent application's
+/// configuration.
+class CtrlAgentCfgMgr : public process::DCfgMgrBase {
+public:
+
+ /// @brief Constructor.
+ CtrlAgentCfgMgr();
+
+ /// @brief Destructor
+ virtual ~CtrlAgentCfgMgr();
+
+ /// @brief Convenience method that returns the Control Agent configuration
+ /// context.
+ ///
+ /// @return returns a pointer to the configuration context.
+ CtrlAgentCfgContextPtr getCtrlAgentCfgContext() {
+ return (boost::dynamic_pointer_cast<CtrlAgentCfgContext>(getContext()));
+ }
+
+ /// @brief Returns configuration summary in the textual format.
+ ///
+ /// @param selection Bitfield which describes the parts of the configuration
+ /// to be returned. This parameter is ignored for the Control Agent.
+ ///
+ /// @return Summary of the configuration in the textual format.
+ virtual std::string getConfigSummary(const uint32_t selection) override;
+
+protected:
+
+ /// @brief Parses configuration of the Control Agent.
+ ///
+ /// @param config Pointer to a configuration specified for the agent.
+ /// @param check_only Boolean flag indicating if this method should
+ /// only verify correctness of the provided configuration.
+ /// @return Pointer to a result of configuration parsing.
+ virtual isc::data::ConstElementPtr
+ parse(isc::data::ConstElementPtr config, bool check_only) override;
+
+ /// @brief Creates a new, blank CtrlAgentCfgContext context.
+ ///
+ ///
+ /// This method is used at the beginning of configuration process to
+ /// create a fresh, empty copy of a CtrlAgentCfgContext. 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 process::ConfigPtr createNewContext() override;
+
+ /// @brief Return a list of all paths that contain passwords or secrets.
+ ///
+ /// Used in @ref isc::process::DCfgMgrBase::redactConfig.
+ ///
+ /// @return the list of lists of sequential JSON map keys needed to reach
+ /// the passwords and secrets.
+ std::list<std::list<std::string>> jsonPathsToRedact() const final override;
+};
+
+/// @brief Defines a shared pointer to CtrlAgentCfgMgr.
+typedef boost::shared_ptr<CtrlAgentCfgMgr> CtrlAgentCfgMgrPtr;
+
+} // namespace isc::agent
+} // namespace isc
+
+#endif // CTRL_AGENT_CFG_MGR_H
diff --git a/src/bin/agent/ca_command_mgr.cc b/src/bin/agent/ca_command_mgr.cc
new file mode 100644
index 0000000..848753d
--- /dev/null
+++ b/src/bin/agent/ca_command_mgr.cc
@@ -0,0 +1,278 @@
+// Copyright (C) 2017-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 <agent/ca_cfg_mgr.h>
+#include <agent/ca_command_mgr.h>
+#include <agent/ca_controller.h>
+#include <agent/ca_log.h>
+#include <agent/ca_process.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/unix_domain_socket.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <cc/json_feed.h>
+#include <config/client_connection.h>
+#include <config/timeouts.h>
+#include <boost/pointer_cast.hpp>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::process;
+
+namespace isc {
+namespace agent {
+
+CtrlAgentCommandMgr&
+CtrlAgentCommandMgr::instance() {
+ static CtrlAgentCommandMgr command_mgr;
+ return (command_mgr);
+}
+
+CtrlAgentCommandMgr::CtrlAgentCommandMgr()
+ : HookedCommandMgr() {
+}
+
+isc::data::ConstElementPtr
+CtrlAgentCommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) {
+ ConstElementPtr answer = HookedCommandMgr::processCommand(cmd);
+
+ // Responses from the Kea Control Agent must be always wrapped
+ // in a list because in general they contain responses from
+ // multiple daemons.
+ if (answer->getType() == Element::list) {
+ return (answer);
+ }
+ ElementPtr answer_list = Element::createList();
+ answer_list->add(boost::const_pointer_cast<Element>(answer));
+
+ return (answer_list);
+}
+
+ConstElementPtr
+CtrlAgentCommandMgr::handleCommand(const std::string& cmd_name,
+ const isc::data::ConstElementPtr& params,
+ const isc::data::ConstElementPtr& original_cmd) {
+
+ ConstElementPtr raddr_ptr = original_cmd->get("remote-address");
+ if (raddr_ptr && (raddr_ptr->getType() == Element::string)) {
+ remote_addr_ = raddr_ptr->stringValue();
+ } else {
+ remote_addr_ = "(unknown)";
+ }
+ LOG_INFO(agent_logger, CTRL_AGENT_COMMAND_RECEIVED)
+ .arg(cmd_name)
+ .arg(remote_addr_);
+
+ ConstElementPtr services = Element::createList();
+
+ // Retrieve 'service' parameter to determine if we should forward the
+ // command or handle it on our own.
+ if (original_cmd && original_cmd->contains("service")) {
+ services = original_cmd->get("service");
+ // If 'service' value is not a list, this is a fatal error. We don't want
+ // to try processing commands that don't adhere to the required format.
+ if (services->getType() != Element::list) {
+ return (createAnswer(CONTROL_RESULT_ERROR, "service value must be a list"));
+ }
+ }
+
+ // 'service' parameter hasn't been specified which indicates that the command
+ // is intended to be processed by the CA. The following command will try to
+ // process the command with hooks libraries (if available) or by one of the
+ // CA's native handlers.
+ if (services->empty()) {
+
+ // It is frequent user error to not include the 'service' parameter in
+ // the commands that should be forwarded to Kea servers. If the command
+ // lacks this parameter the CA will try to process it and often fail
+ // because it is not supported by the CA. In the future we may want to
+ // make this parameter mandatory. For now, we're going to improve the
+ // situation by clearly explaining to the controlling client that the
+ // command is not supported by the CA, but it is possible that he may
+ // achieve what he wants by providing the 'service' parameter.
+
+ // Our interface is very restrictive so we walk around this by const
+ // casting the returned pointer. It is certainly easier to do than
+ // changing the whole data interface.
+ ElementPtr answer = boost::const_pointer_cast<Element>
+ (HookedCommandMgr::handleCommand(cmd_name, params, original_cmd));
+
+ try {
+ // Check what error code was returned by the handler.
+ int rcode = 0;
+ ConstElementPtr text = parseAnswer(rcode, answer);
+
+ // There is a dedicated error code for unsupported command case.
+ if (rcode == CONTROL_RESULT_COMMAND_UNSUPPORTED) {
+
+ // Append the explanatory text to the text reported by the handler.
+ // Smart, eh?
+ std::ostringstream s;
+ s << text->stringValue();
+ s << " You did not include \"service\" parameter in the command,"
+ " which indicates that Kea Control Agent should process this"
+ " command rather than forward it to one or more Kea servers. If you"
+ " aimed to send this command to one of the Kea servers you"
+ " should include the \"service\" parameter in your request, e.g."
+ " \"service\": [ \"dhcp4\" ] to forward the command to the DHCPv4"
+ " server, or \"service\": [ \"dhcp4\", \"dhcp6\", \"d2\" ] to forward it to"
+ " DHCPv4, DHCPv6 and D2 servers etc.";
+
+ answer->set(CONTROL_TEXT, Element::create(s.str()));
+ }
+
+ } catch (...) {
+ // Exceptions are not really possible assuming that the BaseCommandMgr
+ // creates the response correctly.
+ }
+
+ return (answer);
+ }
+
+ ElementPtr answer_list = Element::createList();
+
+ // Before the command is forwarded we check if there are any hooks libraries
+ // which would process the command.
+ if (HookedCommandMgr::delegateCommandToHookLibrary(cmd_name, params, original_cmd,
+ answer_list)) {
+ // The command has been processed by hooks library. Return the result.
+ return (answer_list);
+ }
+
+ // We don't know whether the hooks libraries modified the value of the
+ // answer list, so let's be safe and re-create the answer_list.
+ answer_list = Element::createList();
+
+ // For each value within 'service' we have to try forwarding the command.
+ for (unsigned i = 0; i < services->size(); ++i) {
+ if (original_cmd) {
+ ConstElementPtr answer;
+ try {
+ LOG_DEBUG(agent_logger, isc::log::DBGLVL_COMMAND,
+ CTRL_AGENT_COMMAND_FORWARD_BEGIN)
+ .arg(cmd_name).arg(services->get(i)->stringValue());
+
+ answer = forwardCommand(services->get(i)->stringValue(),
+ cmd_name, original_cmd);
+
+ } catch (const CommandForwardingError& ex) {
+ LOG_DEBUG(agent_logger, isc::log::DBGLVL_COMMAND,
+ CTRL_AGENT_COMMAND_FORWARD_FAILED)
+ .arg(cmd_name).arg(ex.what());
+ answer = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ }
+
+ answer_list->add(boost::const_pointer_cast<Element>(answer));
+ }
+ }
+
+ return (answer_list);
+}
+
+ConstElementPtr
+CtrlAgentCommandMgr::forwardCommand(const std::string& service,
+ const std::string& cmd_name,
+ const isc::data::ConstElementPtr& command) {
+ // Context will hold the server configuration.
+ CtrlAgentCfgContextPtr ctx;
+
+ // There is a hierarchy of the objects through which we need to pass to get
+ // the configuration context. We may simplify this at some point but since
+ // we're in the singleton we want to make sure that we're using most current
+ // configuration.
+ boost::shared_ptr<CtrlAgentController> controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(CtrlAgentController::instance());
+ if (controller) {
+ CtrlAgentProcessPtr process = controller->getCtrlAgentProcess();
+ if (process) {
+ CtrlAgentCfgMgrPtr cfgmgr = process->getCtrlAgentCfgMgr();
+ if (cfgmgr) {
+ ctx = cfgmgr->getCtrlAgentCfgContext();
+ }
+ }
+ }
+
+ // This is highly unlikely but keep the checks just in case someone messes up
+ // in the code.
+ if (!ctx) {
+ isc_throw(CommandForwardingError, "internal server error: unable to retrieve"
+ " Control Agent configuration information");
+ }
+
+ // Now that we know what service it should be forwarded to, we should
+ // find a matching forwarding socket. If this socket is not configured,
+ // we have to communicate it to the client.
+ ConstElementPtr socket_info = ctx->getControlSocketInfo(service);
+ if (!socket_info) {
+ isc_throw(CommandForwardingError, "forwarding socket is not configured"
+ " for the server type " << service);
+ }
+
+ // If the configuration does its job properly the socket-name must be
+ // specified and must be a string value.
+ std::string socket_name = socket_info->get("socket-name")->stringValue();
+
+ // Forward command and receive reply.
+ IOServicePtr io_service(new IOService());;
+ ClientConnection conn(*io_service);
+ boost::system::error_code received_ec;
+ ConstJSONFeedPtr received_feed;
+ conn.start(ClientConnection::SocketPath(socket_name),
+ ClientConnection::ControlCommand(command->toWire()),
+ [&io_service, &received_ec, &received_feed]
+ (const boost::system::error_code& ec, ConstJSONFeedPtr feed) {
+ // Capture error code and parsed data.
+ received_ec = ec;
+ received_feed = feed;
+ // Got the IO service so stop IO service. This causes to
+ // stop IO service when all handlers have been invoked.
+ io_service->stopWork();
+ }, ClientConnection::Timeout(TIMEOUT_AGENT_FORWARD_COMMAND));
+ io_service->run();
+
+ if (received_ec) {
+ isc_throw(CommandForwardingError, "unable to forward command to the "
+ << service << " service: " << received_ec.message()
+ << ". The server is likely to be offline");
+ }
+
+ // This shouldn't happen because the fact that there was no time out indicates
+ // that the whole response has been read and it should be stored within the
+ // feed. But, let's check to prevent assertions.
+ if (!received_feed) {
+ isc_throw(CommandForwardingError, "internal server error: empty response"
+ " received from the unix domain socket");
+ }
+
+ ConstElementPtr answer;
+ try {
+ answer = received_feed->toElement();
+
+ LOG_INFO(agent_logger, CTRL_AGENT_COMMAND_FORWARDED)
+ .arg(cmd_name)
+ .arg(service)
+ .arg(remote_addr_);
+
+ } catch (const std::exception& ex) {
+ isc_throw(CommandForwardingError, "internal server error: unable to parse"
+ " server's answer to the forwarded message: " << ex.what());
+ }
+
+ return (answer);
+}
+
+
+} // end of namespace isc::agent
+} // end of namespace isc
diff --git a/src/bin/agent/ca_command_mgr.h b/src/bin/agent/ca_command_mgr.h
new file mode 100644
index 0000000..c883f77
--- /dev/null
+++ b/src/bin/agent/ca_command_mgr.h
@@ -0,0 +1,118 @@
+// Copyright (C) 2017-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 CTRL_AGENT_COMMAND_MGR_H
+#define CTRL_AGENT_COMMAND_MGR_H
+
+#include <config/hooked_command_mgr.h>
+#include <exceptions/exceptions.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace agent {
+
+/// @brief Exception thrown when an error occurred during control command
+/// forwarding.
+class CommandForwardingError : public Exception {
+public:
+ CommandForwardingError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Command Manager for Control Agent.
+///
+/// This is an implementation of the Command Manager within Control Agent.
+/// In addition to the standard capabilities of the @ref HookedCommandMgr
+/// it is also intended to forward commands to the respective Kea servers
+/// when the command is not supported directly by the Control Agent.
+///
+/// The @ref CtrlAgentCommandMgr is implemented as a singleton. The commands
+/// are registered using @c CtrlAgentCommandMgr::instance().registerCommand().
+/// The @ref CtrlAgentResponseCreator uses the sole instance of the Command
+/// Manager to handle incoming commands.
+class CtrlAgentCommandMgr : public config::HookedCommandMgr,
+ public boost::noncopyable {
+public:
+
+ /// @brief Returns sole instance of the Command Manager.
+ static CtrlAgentCommandMgr& instance();
+
+ /// @brief Triggers command processing.
+ ///
+ /// This method overrides the @c BaseCommandMgr::processCommand to ensure
+ /// that the response is always wrapped in a list. The base implementation
+ /// returns a response map. Kea Control Agent forwards commands to multiple
+ /// daemons behind it and thus it must return a list of responses from
+ /// respective daemons. If an error occurs during command processing the
+ /// error response must also be wrapped in a list because caller expects
+ /// that CA always returns a list.
+ ///
+ /// This method is an entry point for dealing with a command. Internally
+ /// it calls @c CtrlAgentCommandMgr::handleCommand.
+ ///
+ /// @param cmd Pointer to the data element representing command in JSON
+ /// format.
+ /// @return Pointer to the response.
+ virtual isc::data::ConstElementPtr
+ processCommand(const isc::data::ConstElementPtr& cmd);
+
+ /// @brief Handles the command having a given name and arguments.
+ ///
+ /// This method extends the base implementation with the ability to forward
+ /// commands to Kea servers.
+ ///
+ /// If the received command doesn't include 'service' parameter or this
+ /// parameter is blank, the command is first handled by the attached hooks
+ /// libraries, and if still unhandled, the Control Agent itself.
+ ///
+ /// If the non-blank 'service' parameter has been specified the hooks
+ /// are executed. If the hooks process the command the result is returned
+ /// to the controlling client. Otherwise, the command is forwarded to each
+ /// Kea server listed in the 'service' parameter.
+ ///
+ /// @param cmd_name Command name.
+ /// @param params Command arguments.
+ /// @param original_cmd Original command being processed.
+ ///
+ /// @return Pointer to the const data element representing a list of
+ /// responses to the command. If the command has been handled by the CA,
+ /// this list includes one response.
+ virtual isc::data::ConstElementPtr
+ handleCommand(const std::string& cmd_name,
+ const isc::data::ConstElementPtr& params,
+ const isc::data::ConstElementPtr& original_cmd);
+
+private:
+
+ /// @brief Tries to forward received control command to a specified server.
+ ///
+ /// @param service Contains name of the service where the command should be
+ /// forwarded.
+ /// @param cmd_name Command name.
+ /// @param command Pointer to the object representing the forwarded command.
+ ///
+ /// @return Response to forwarded command.
+ /// @throw CommandForwardingError when an error occurred during forwarding.
+ isc::data::ConstElementPtr
+ forwardCommand(const std::string& service, const std::string& cmd_name,
+ const isc::data::ConstElementPtr& command);
+
+ /// @brief Private constructor.
+ ///
+ /// The instance should be created using @ref CtrlAgentCommandMgr::instance,
+ /// thus the constructor is private.
+ CtrlAgentCommandMgr();
+
+ /// @brief Remote address of HTTP endpoint.
+ std::string remote_addr_;
+
+};
+
+} // end of namespace isc::agent
+} // end of namespace isc
+
+#endif
diff --git a/src/bin/agent/ca_controller.cc b/src/bin/agent/ca_controller.cc
new file mode 100644
index 0000000..f840831
--- /dev/null
+++ b/src/bin/agent/ca_controller.cc
@@ -0,0 +1,113 @@
+// Copyright (C) 2016-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 <agent/ca_controller.h>
+#include <agent/ca_process.h>
+#include <agent/ca_command_mgr.h>
+#include <agent/parser_context.h>
+#include <cfgrpt/config_report.h>
+#include <functional>
+
+using namespace isc::process;
+namespace ph = std::placeholders;
+
+namespace isc {
+namespace agent {
+
+/// @brief Defines the application name, this is passed into base class
+/// it may be used to locate configuration data and appears in log statement.
+const char* CtrlAgentController::agent_app_name_ = "Control-agent";
+
+/// @brief Defines the executable name. This is passed into the base class
+const char* CtrlAgentController::agent_bin_name_ = "kea-ctrl-agent";
+
+DControllerBasePtr&
+CtrlAgentController::instance() {
+ // If the instance hasn't been created yet, create it. Note this method
+ // must use the base class singleton instance methods.
+ if (!getController()) {
+ DControllerBasePtr controller_ptr(new CtrlAgentController());
+ setController(controller_ptr);
+ }
+
+ return (getController());
+}
+
+DProcessBase*
+CtrlAgentController::createProcess() {
+ // Instantiate and return an instance of the D2 application process. Note
+ // that the process is passed the controller's io_service.
+ return (new CtrlAgentProcess(getAppName().c_str(), getIOService()));
+}
+
+isc::data::ConstElementPtr
+CtrlAgentController::parseFile(const std::string& name) {
+ ParserContext parser;
+ return (parser.parseFile(name, ParserContext::PARSER_AGENT));
+}
+
+void
+CtrlAgentController::registerCommands() {
+ CtrlAgentCommandMgr::instance().registerCommand(BUILD_REPORT_COMMAND,
+ std::bind(&DControllerBase::buildReportHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(CONFIG_GET_COMMAND,
+ std::bind(&DControllerBase::configGetHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(CONFIG_RELOAD_COMMAND,
+ std::bind(&DControllerBase::configReloadHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(CONFIG_SET_COMMAND,
+ std::bind(&DControllerBase::configSetHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(CONFIG_TEST_COMMAND,
+ std::bind(&DControllerBase::configTestHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(CONFIG_WRITE_COMMAND,
+ std::bind(&DControllerBase::configWriteHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(SHUT_DOWN_COMMAND,
+ std::bind(&DControllerBase::shutdownHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(STATUS_GET_COMMAND,
+ std::bind(&DControllerBase::statusGetHandler, this, ph::_1, ph::_2));
+
+ CtrlAgentCommandMgr::instance().registerCommand(VERSION_GET_COMMAND,
+ std::bind(&DControllerBase::versionGetHandler, this, ph::_1, ph::_2));
+}
+
+void
+CtrlAgentController::deregisterCommands() {
+ CtrlAgentCommandMgr::instance().deregisterCommand(BUILD_REPORT_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_GET_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_RELOAD_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_SET_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_TEST_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(CONFIG_WRITE_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(SHUT_DOWN_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(STATUS_GET_COMMAND);
+ CtrlAgentCommandMgr::instance().deregisterCommand(VERSION_GET_COMMAND);
+}
+
+CtrlAgentController::CtrlAgentController()
+ : DControllerBase(agent_app_name_, agent_bin_name_) {
+}
+
+CtrlAgentController::~CtrlAgentController() {
+}
+
+CtrlAgentProcessPtr
+CtrlAgentController::getCtrlAgentProcess() {
+ return (boost::dynamic_pointer_cast<CtrlAgentProcess>(getProcess()));
+}
+
+// Refer to config_report so it will be embedded in the binary.
+const char* const* ca_config_report = isc::detail::config_report;
+
+} // namespace isc::agent
+} // namespace isc
diff --git a/src/bin/agent/ca_controller.h b/src/bin/agent/ca_controller.h
new file mode 100644
index 0000000..7d6b5ae
--- /dev/null
+++ b/src/bin/agent/ca_controller.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2016-2017 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 CTRL_AGENT_CONTROLLER_H
+#define CTRL_AGENT_CONTROLLER_H
+
+#include <agent/ca_process.h>
+#include <process/d_controller.h>
+
+namespace isc {
+namespace agent {
+
+/// @brief Process Controller for Control Agent Process.
+///
+/// This class is the Control Agent specific derivation of the DControllerBase.
+/// It creates and manages an instance of the Control Agent application process,
+/// CtrlAgentProcess.
+class CtrlAgentController : public process::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 the pointer reference to the singleton instance.
+ static process::DControllerBasePtr& instance();
+
+ /// @brief Destructor
+ virtual ~CtrlAgentController();
+
+ /// @brief Returns pointer to an instance of the underlying process object.
+ CtrlAgentProcessPtr getCtrlAgentProcess();
+
+ /// @brief Defines the application name, this is passed into base class
+ /// and appears in log statements.
+ static const char* agent_app_name_;
+
+ /// @brief Defines the executable name. This is passed into the base class
+ /// by convention this should match the executable name.
+ static const char* agent_bin_name_;
+
+ /// @brief Parses the configuration file using Agent::ParserContext (bison)
+ ///
+ /// @param name name of the text file to be parsed
+ /// @return Element tree structure representing parsed configuration
+ isc::data::ConstElementPtr
+ parseFile(const std::string& name);
+
+ /// @brief Register commands.
+ void registerCommands();
+
+ /// @brief Deregister commands.
+ void deregisterCommands();
+
+private:
+
+ /// @brief Creates an instance of the Control Agent application
+ /// process.
+ ///
+ /// This method is invoked during the process initialization step of
+ /// the controller launch.
+ ///
+ /// @return returns a DProcessBase* to the application process created.
+ /// Note the caller is responsible for destructing the process. This
+ /// is handled by the base class, which wraps this pointer with a smart
+ /// pointer.
+ virtual process::DProcessBase* createProcess();
+
+ /// @brief Constructor is declared private to maintain the integrity of
+ /// the singleton instance.
+ CtrlAgentController();
+};
+
+// @Defines a shared pointer to CtrlAgentController
+typedef boost::shared_ptr<CtrlAgentController> CtrlAgentControllerPtr;
+
+} // namespace isc::agent
+} // namespace isc
+
+#endif // CTRL_AGENT_CONTROLLER_H
diff --git a/src/bin/agent/ca_log.cc b/src/bin/agent/ca_log.cc
new file mode 100644
index 0000000..a2a01bb
--- /dev/null
+++ b/src/bin/agent/ca_log.cc
@@ -0,0 +1,17 @@
+// Copyright (C) 2016-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/.
+
+#include <config.h>
+
+#include <agent/ca_log.h>
+
+namespace isc {
+namespace agent {
+
+isc::log::Logger agent_logger("ctrl-agent");
+
+} // namespace isc::agent
+} // namespace isc
diff --git a/src/bin/agent/ca_log.h b/src/bin/agent/ca_log.h
new file mode 100644
index 0000000..f9d77eb
--- /dev/null
+++ b/src/bin/agent/ca_log.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2016-2017 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 CTRL_AGENT_LOG_H
+#define CTRL_AGENT_LOG_H
+
+#include <log/logger_support.h>
+#include <log/macros.h>
+#include <agent/ca_messages.h>
+
+namespace isc {
+namespace agent {
+
+/// @brief Control Agent logger.
+extern isc::log::Logger agent_logger;
+
+} // namespace isc::agent
+} // namespace isc
+
+#endif
diff --git a/src/bin/agent/ca_messages.cc b/src/bin/agent/ca_messages.cc
new file mode 100644
index 0000000..4c9df2b
--- /dev/null
+++ b/src/bin/agent/ca_messages.cc
@@ -0,0 +1,47 @@
+// File created from ../../../src/bin/agent/ca_messages.mes
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace agent {
+
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_FORWARDED = "CTRL_AGENT_COMMAND_FORWARDED";
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_FORWARD_BEGIN = "CTRL_AGENT_COMMAND_FORWARD_BEGIN";
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_FORWARD_FAILED = "CTRL_AGENT_COMMAND_FORWARD_FAILED";
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_RECEIVED = "CTRL_AGENT_COMMAND_RECEIVED";
+extern const isc::log::MessageID CTRL_AGENT_CONFIG_CHECK_FAIL = "CTRL_AGENT_CONFIG_CHECK_FAIL";
+extern const isc::log::MessageID CTRL_AGENT_CONFIG_FAIL = "CTRL_AGENT_CONFIG_FAIL";
+extern const isc::log::MessageID CTRL_AGENT_CONFIG_SYNTAX_WARNING = "CTRL_AGENT_CONFIG_SYNTAX_WARNING";
+extern const isc::log::MessageID CTRL_AGENT_FAILED = "CTRL_AGENT_FAILED";
+extern const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_STARTED = "CTRL_AGENT_HTTPS_SERVICE_STARTED";
+extern const isc::log::MessageID CTRL_AGENT_HTTP_SERVICE_STARTED = "CTRL_AGENT_HTTP_SERVICE_STARTED";
+extern const isc::log::MessageID CTRL_AGENT_RUN_EXIT = "CTRL_AGENT_RUN_EXIT";
+extern const isc::log::MessageID CTRL_AGENT_STARTED = "CTRL_AGENT_STARTED";
+
+} // namespace agent
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+ "CTRL_AGENT_COMMAND_FORWARDED", "command %1 successfully forwarded to the service %2 from remote address %3",
+ "CTRL_AGENT_COMMAND_FORWARD_BEGIN", "begin forwarding command %1 to service %2",
+ "CTRL_AGENT_COMMAND_FORWARD_FAILED", "failed forwarding command %1: %2",
+ "CTRL_AGENT_COMMAND_RECEIVED", "command %1 received from remote address %2",
+ "CTRL_AGENT_CONFIG_CHECK_FAIL", "Control Agent configuration check failed: %1",
+ "CTRL_AGENT_CONFIG_FAIL", "Control Agent configuration failed: %1",
+ "CTRL_AGENT_CONFIG_SYNTAX_WARNING", "Control Agent configuration syntax warning: %1",
+ "CTRL_AGENT_FAILED", "application experienced a fatal error: %1",
+ "CTRL_AGENT_HTTPS_SERVICE_STARTED", "HTTPS service bound to address %1:%2",
+ "CTRL_AGENT_HTTP_SERVICE_STARTED", "HTTP service bound to address %1:%2",
+ "CTRL_AGENT_RUN_EXIT", "application is exiting the event loop",
+ "CTRL_AGENT_STARTED", "Kea Control Agent version %1 started",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/bin/agent/ca_messages.h b/src/bin/agent/ca_messages.h
new file mode 100644
index 0000000..dda4866
--- /dev/null
+++ b/src/bin/agent/ca_messages.h
@@ -0,0 +1,27 @@
+// File created from ../../../src/bin/agent/ca_messages.mes
+
+#ifndef CA_MESSAGES_H
+#define CA_MESSAGES_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace agent {
+
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_FORWARDED;
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_FORWARD_BEGIN;
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_FORWARD_FAILED;
+extern const isc::log::MessageID CTRL_AGENT_COMMAND_RECEIVED;
+extern const isc::log::MessageID CTRL_AGENT_CONFIG_CHECK_FAIL;
+extern const isc::log::MessageID CTRL_AGENT_CONFIG_FAIL;
+extern const isc::log::MessageID CTRL_AGENT_CONFIG_SYNTAX_WARNING;
+extern const isc::log::MessageID CTRL_AGENT_FAILED;
+extern const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_STARTED;
+extern const isc::log::MessageID CTRL_AGENT_HTTP_SERVICE_STARTED;
+extern const isc::log::MessageID CTRL_AGENT_RUN_EXIT;
+extern const isc::log::MessageID CTRL_AGENT_STARTED;
+
+} // namespace agent
+} // namespace isc
+
+#endif // CA_MESSAGES_H
diff --git a/src/bin/agent/ca_messages.mes b/src/bin/agent/ca_messages.mes
new file mode 100644
index 0000000..8100fad
--- /dev/null
+++ b/src/bin/agent/ca_messages.mes
@@ -0,0 +1,61 @@
+# Copyright (C) 2016-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/.
+
+$NAMESPACE isc::agent
+
+% CTRL_AGENT_COMMAND_FORWARDED command %1 successfully forwarded to the service %2 from remote address %3
+This informational message is issued when the CA successfully forwards
+the control message to the specified Kea service and receives a response.
+
+% CTRL_AGENT_COMMAND_FORWARD_BEGIN begin forwarding command %1 to service %2
+This debug message is issued when the Control Agent starts forwarding a
+received command to one of the Kea servers.
+
+% CTRL_AGENT_COMMAND_FORWARD_FAILED failed forwarding command %1: %2
+This debug message is issued when the Control Agent failed forwarding a
+received command to one of the Kea servers. The second argument provides
+the details of the error.
+
+% CTRL_AGENT_COMMAND_RECEIVED command %1 received from remote address %2
+This informational message is issued when the CA receives a control message,
+whether it is destined to the control agent itself, or to be forwarded on.
+
+% CTRL_AGENT_CONFIG_CHECK_FAIL Control Agent configuration check failed: %1
+This error message indicates that the CA had failed configuration
+check. Details are provided. Additional details may be available
+in earlier log entries, possibly on lower levels.
+
+% CTRL_AGENT_CONFIG_FAIL Control Agent configuration failed: %1
+This error message indicates that the CA had failed configuration
+attempt. Details are provided. Additional details may be available
+in earlier log entries, possibly on lower levels.
+
+% CTRL_AGENT_CONFIG_SYNTAX_WARNING Control Agent configuration syntax warning: %1
+This warning message indicates that the CA configuration had a minor syntax
+error. The error was displayed and the configuration parsing resumed.
+
+% CTRL_AGENT_FAILED application experienced a fatal error: %1
+This is a fatal error message issued when the Control Agent application
+encounters an unrecoverable error from within the event loop.
+
+% CTRL_AGENT_HTTPS_SERVICE_STARTED HTTPS service bound to address %1:%2
+This informational message indicates that the server has started HTTPS service
+on the specified address and port. All control commands should be sent to this
+address and port over a TLS channel.
+
+% CTRL_AGENT_HTTP_SERVICE_STARTED HTTP service bound to address %1:%2
+This informational message indicates that the server has started HTTP service
+on the specified address and port. All control commands should be sent to this
+address and port.
+
+% CTRL_AGENT_RUN_EXIT application is exiting the event loop
+This is a debug message issued when the Control Agent exits its
+event loop.
+
+% CTRL_AGENT_STARTED Kea Control Agent version %1 started
+This informational message indicates that the Control Agent has
+processed all configuration information and is ready to begin processing.
+The version is also printed.
diff --git a/src/bin/agent/ca_process.cc b/src/bin/agent/ca_process.cc
new file mode 100644
index 0000000..8c47377
--- /dev/null
+++ b/src/bin/agent/ca_process.cc
@@ -0,0 +1,237 @@
+// 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/.
+
+#include <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <agent/ca_process.h>
+#include <agent/ca_controller.h>
+#include <agent/ca_response_creator_factory.h>
+#include <agent/ca_log.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+#include <cc/command_interpreter.h>
+#include <config/timeouts.h>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+using namespace isc::process;
+
+
+namespace isc {
+namespace agent {
+
+CtrlAgentProcess::CtrlAgentProcess(const char* name,
+ const asiolink::IOServicePtr& io_service)
+ : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())),
+ http_listeners_() {
+}
+
+CtrlAgentProcess::~CtrlAgentProcess() {
+}
+
+void
+CtrlAgentProcess::init() {
+}
+
+void
+CtrlAgentProcess::run() {
+ LOG_INFO(agent_logger, CTRL_AGENT_STARTED).arg(VERSION);
+
+ try {
+ // Register commands.
+ CtrlAgentControllerPtr controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(
+ CtrlAgentController::instance());
+ controller->registerCommands();
+
+ // Let's process incoming data or expiring timers in a loop until
+ // shutdown condition is detected.
+ while (!shouldShutdown()) {
+ // Remove unused listeners within the main loop because new listeners
+ // are created in within a callback method. This avoids removal the
+ // listeners within a callback.
+ garbageCollectListeners(1);
+ runIO();
+ }
+ // Done so removing all listeners.
+ garbageCollectListeners(0);
+ stopIOService();
+ } catch (const std::exception& ex) {
+ LOG_FATAL(agent_logger, CTRL_AGENT_FAILED).arg(ex.what());
+ try {
+ stopIOService();
+ } catch (...) {
+ // Ignore double errors
+ }
+ isc_throw(DProcessBaseError,
+ "Process run method failed: " << ex.what());
+ }
+
+ try {
+ // Deregister commands.
+ CtrlAgentControllerPtr controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(
+ CtrlAgentController::instance());
+ controller->deregisterCommands();
+ } catch (const std::exception&) {
+ // What to do? Simply ignore...
+ }
+
+ LOG_DEBUG(agent_logger, isc::log::DBGLVL_START_SHUT, CTRL_AGENT_RUN_EXIT);
+}
+
+size_t
+CtrlAgentProcess::runIO() {
+ size_t cnt = getIoService()->get_io_service().poll();
+ if (!cnt) {
+ cnt = getIoService()->get_io_service().run_one();
+ }
+ return (cnt);
+}
+
+isc::data::ConstElementPtr
+CtrlAgentProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
+ setShutdownFlag(true);
+ return (isc::config::createAnswer(0, "Control Agent is shutting down"));
+}
+
+isc::data::ConstElementPtr
+CtrlAgentProcess::configure(isc::data::ConstElementPtr config_set,
+ bool check_only) {
+ // System reconfiguration often poses an interesting issue whereby the
+ // configuration parsing is successful, but an attempt to use a new
+ // configuration is not. This will leave us in the inconsistent state
+ // when the configuration is in fact only partially applied and the
+ // system's ability to operate is impaired. The use of C++ lambda is
+ // a way to resolve this problem by injecting the code to the
+ // simpleParseConfig which performs an attempt to open new instance
+ // of the listener (if required). The lambda code will throw an
+ // exception if it fails and cause the simpleParseConfig to rollback
+ // configuration changes and report an error.
+ ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
+ check_only,
+ [this]() {
+ ConfigPtr base_ctx = getCfgMgr()->getContext();
+ CtrlAgentCfgContextPtr
+ ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
+
+ if (!ctx) {
+ isc_throw(Unexpected, "Internal logic error: bad context type");
+ }
+
+ /// @todo: If the parameter is a hostname, we need to resolve it.
+ IOAddress server_address("::");
+ try {
+ server_address = IOAddress(ctx->getHttpHost());
+
+ } catch (const IOError& e) {
+ isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
+ << " to IP address:" << e.what());
+ }
+
+ uint16_t server_port = ctx->getHttpPort();
+ bool use_https = false;
+
+ // Only open a new listener if the configuration has changed.
+ if (http_listeners_.empty() ||
+ (http_listeners_.back()->getLocalAddress() != server_address) ||
+ (http_listeners_.back()->getLocalPort() != server_port)) {
+ // Create a TLS context.
+ TlsContextPtr tls_context;
+ // When TLS is enabled configure it.
+ if (!ctx->getCertFile().empty()) {
+ TlsContext::configure(tls_context,
+ TlsRole::SERVER,
+ ctx->getTrustAnchor(),
+ ctx->getCertFile(),
+ ctx->getKeyFile(),
+ ctx->getCertRequired());
+ use_https = true;
+ }
+
+ // Create response creator factory first. It will be used to
+ // generate response creators. Each response creator will be
+ // used to generate answer to specific request.
+ HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
+
+ // Create http listener. It will open up a TCP socket and be
+ // prepared to accept incoming connection.
+ HttpListenerPtr http_listener
+ (new HttpListener(*getIoService(), server_address,
+ server_port, tls_context, rcf,
+ HttpListener::RequestTimeout(TIMEOUT_AGENT_RECEIVE_COMMAND),
+ HttpListener::IdleTimeout(TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT)));
+
+ // Instruct the http listener to actually open socket, install
+ // callback and start listening.
+ http_listener->start();
+
+ // The new listener is running so add it to the collection of
+ // active listeners. The next step will be to remove all other
+ // active listeners, but we do it inside the main process loop.
+ http_listeners_.push_back(http_listener);
+ }
+
+ // Ok, seems we're good to go.
+ if (use_https) {
+ LOG_INFO(agent_logger, CTRL_AGENT_HTTPS_SERVICE_STARTED)
+ .arg(server_address.toText()).arg(server_port);
+ } else {
+ LOG_INFO(agent_logger, CTRL_AGENT_HTTP_SERVICE_STARTED)
+ .arg(server_address.toText()).arg(server_port);
+ }
+ });
+
+ int rcode = 0;
+ config::parseAnswer(rcode, answer);
+ return (answer);
+}
+
+void
+CtrlAgentProcess::garbageCollectListeners(size_t leaving) {
+ // We expect only one active listener. If there are more (most likely 2),
+ // it means we have just reconfigured the server and need to shut down all
+ // listeners execept the most recently added.
+ if (http_listeners_.size() > leaving) {
+ // Stop no longer used listeners.
+ for (auto l = http_listeners_.begin();
+ l != http_listeners_.end() - leaving;
+ ++l) {
+ (*l)->stop();
+ }
+ // We have stopped listeners but there may be some pending handlers
+ // related to these listeners. Need to invoke these handlers.
+ getIoService()->get_io_service().poll();
+ // Finally, we're ready to remove no longer used listeners.
+ http_listeners_.erase(http_listeners_.begin(),
+ http_listeners_.end() - leaving);
+ }
+}
+
+
+CtrlAgentCfgMgrPtr
+CtrlAgentProcess::getCtrlAgentCfgMgr() {
+ return (boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
+}
+
+ConstHttpListenerPtr
+CtrlAgentProcess::getHttpListener() const {
+ // Return the most recent listener or null.
+ return (http_listeners_.empty() ? ConstHttpListenerPtr() :
+ http_listeners_.back());
+}
+
+bool
+CtrlAgentProcess::isListening() const {
+ // If there are is a listener, we're listening.
+ return (static_cast<bool>(getHttpListener()));
+}
+
+} // namespace isc::agent
+} // namespace isc
diff --git a/src/bin/agent/ca_process.h b/src/bin/agent/ca_process.h
new file mode 100644
index 0000000..3e6e084
--- /dev/null
+++ b/src/bin/agent/ca_process.h
@@ -0,0 +1,155 @@
+// 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/.
+
+#ifndef CTRL_AGENT_PROCESS_H
+#define CTRL_AGENT_PROCESS_H
+
+#include <agent/ca_cfg_mgr.h>
+#include <http/listener.h>
+#include <process/d_process.h>
+#include <vector>
+
+namespace isc {
+namespace agent {
+
+/// @brief Kea Control Agent Application Process
+///
+/// CtrlAgentProcess provides top level application logic for the Control
+/// Agent, a process managing Kea servers.
+///
+/// The Control Agent receives JSON control commands over HTTP and forwards
+/// the JSON commands to the respective Kea servers. The JSON command
+/// includes a name of the server to which the command pertains. After
+/// receiving a response from the Kea server it is sent back over HTTP
+/// to the control API client.
+///
+/// Some commands are handled by the Control Agent process itself, rather than
+/// forwarded to the Kea servers. An example of such command is the one that
+/// instructs the agent to start a specific service.
+class CtrlAgentProcess : public process::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.
+ CtrlAgentProcess(const char* name, const asiolink::IOServicePtr& io_service);
+
+ /// @brief Destructor
+ virtual ~CtrlAgentProcess();
+
+ /// @brief Initialize the Control Agent process.
+ ///
+ /// This is invoked by the controller after command line arguments but
+ /// prior to configuration reception. The base class provides this method
+ /// as a place to perform any derivation-specific initialization steps
+ /// that are inappropriate for the constructor but necessary prior to
+ /// launch.
+ virtual void init();
+
+ /// @brief Implements the process's event loop.
+ ///
+ /// @throw DProcessBaseError if an operational error is encountered.
+ virtual void run();
+
+ /// @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);
+
+ /// @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.
+ ///
+ /// A usual problem related to the system reconfiguration is how to preserve
+ /// configuration integrity in case of errors. In this case, when the
+ /// HTTP listener's configuration is modified there is a need to close all
+ /// existing connections and gracefully shutdown the listener's instance.
+ /// This, however, makes it possible that the control agent looses
+ /// connectivity if opening a new listener is unsuccessful. In fact, this
+ /// is quite possible scenario when the user is setting up the listener to
+ /// use a restricted port range or non-existing IP address. In this case,
+ /// the configuration parser will not signal the problem because IP address
+ /// and/or port are syntactically correct.
+ ///
+ /// This method deals with this problem by opening a new listener aside of
+ /// the currently running listener (if the new listener settings are
+ /// different than current settings). Both instances are held until the
+ /// CtrlAgentProcess::garbageCollectListeners is invoked, which
+ /// removes any listeners which are no longer used.
+ ///
+ /// @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);
+
+ /// @brief Returns a pointer to the configuration manager.
+ CtrlAgentCfgMgrPtr getCtrlAgentCfgMgr();
+
+ /// @brief Returns a const pointer to the HTTP listener used by the process.
+ ///
+ /// @return Const pointer to the currently used listener or null pointer if
+ /// we're not listening. In fact, the latter should never be the case given
+ /// that we provide default listener configuration.
+ http::ConstHttpListenerPtr getHttpListener() const;
+
+ /// @brief Checks if the process is listening to the HTTP requests.
+ ///
+ /// @return true if the process is listening.
+ bool isListening() const;
+
+private:
+
+ /// @brief Removes listeners which are no longer in use.
+ ///
+ /// This method should be called after executing
+ /// @ref CtrlAgentProcess::configure to remove listeners used previously
+ /// (no longer used because the listening address and port has changed as
+ // a result of the reconfiguration). If there are no listeners additional
+ /// to the one that is currently in use, the method has no effect.
+ /// This method is reused to remove all listeners at shutdown time.
+ ///
+ /// @param leaving The number of listener to leave (default one).
+ void garbageCollectListeners(size_t leaving = 1);
+
+ /// @brief Polls all ready handlers and then runs one handler if none
+ /// handlers have been executed as a result of polling.
+ ///
+ /// @return Number of executed handlers.
+ size_t runIO();
+
+ /// @brief Holds a list of pointers to the active listeners.
+ std::vector<http::HttpListenerPtr> http_listeners_;
+
+};
+
+/// @brief Defines a shared pointer to CtrlAgentProcess.
+typedef boost::shared_ptr<CtrlAgentProcess> CtrlAgentProcessPtr;
+
+}; // namespace isc::agent
+}; // namespace isc
+
+#endif // CTRL_AGENT_PROCESS_H
diff --git a/src/bin/agent/ca_response_creator.cc b/src/bin/agent/ca_response_creator.cc
new file mode 100644
index 0000000..5da0e4c
--- /dev/null
+++ b/src/bin/agent/ca_response_creator.cc
@@ -0,0 +1,205 @@
+// Copyright (C) 2017-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 <agent/ca_cfg_mgr.h>
+#include <agent/ca_command_mgr.h>
+#include <agent/ca_controller.h>
+#include <agent/ca_process.h>
+#include <agent/ca_response_creator.h>
+#include <cc/data.h>
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_log.h>
+#include <hooks/hooks_manager.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <boost/pointer_cast.hpp>
+#include <iostream>
+
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::http;
+
+namespace {
+
+/// Structure that holds registered hook indexes.
+struct CtrlAgentHooks {
+ int hook_index_auth_; ///< index of "auth" hook point.
+ int hook_index_response_; ///< index of "response" hook point.
+
+ /// Constructor that registers hook points.
+ CtrlAgentHooks() {
+ hook_index_auth_ = HooksManager::registerHook("auth");
+ hook_index_response_ = HooksManager::registerHook("response");
+ }
+};
+
+} // end of anonymous namespace.
+
+// Declare a Hooks object. As this is outside any function or method, it
+// will be instantiated (and the constructor run) when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+CtrlAgentHooks Hooks;
+
+namespace isc {
+namespace agent {
+
+HttpRequestPtr
+CtrlAgentResponseCreator::createNewHttpRequest() const {
+ return (HttpRequestPtr(new PostHttpRequestJson()));
+}
+
+HttpResponsePtr
+CtrlAgentResponseCreator::
+createStockHttpResponse(const HttpRequestPtr& request,
+ const HttpStatusCode& status_code) const {
+ HttpResponsePtr response = createStockHttpResponseInternal(request, status_code);
+ response->finalize();
+ return (response);
+}
+
+HttpResponsePtr
+CtrlAgentResponseCreator::
+createStockHttpResponseInternal(const HttpRequestPtr& request,
+ const HttpStatusCode& status_code) const {
+ // The request hasn't been finalized so the request object
+ // doesn't contain any information about the HTTP version number
+ // used. But, the context should have this data (assuming the
+ // HTTP version is parsed ok).
+ HttpVersion http_version(request->context()->http_version_major_,
+ request->context()->http_version_minor_);
+ // We only accept HTTP version 1.0 or 1.1. If other version number is found
+ // we fall back to HTTP/1.0.
+ if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) {
+ http_version.major_ = 1;
+ http_version.minor_ = 0;
+ }
+ // This will generate the response holding JSON content.
+ HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
+ return (response);
+}
+
+HttpResponsePtr
+CtrlAgentResponseCreator::
+createDynamicHttpResponse(HttpRequestPtr request) {
+ // First check authentication.
+ HttpResponseJsonPtr http_response;
+
+ // Context will hold the server configuration.
+ CtrlAgentCfgContextPtr ctx;
+
+ // There is a hierarchy of the objects through which we need to pass to get
+ // the configuration context. We may simplify this at some point but since
+ // we're in the singleton we want to make sure that we're using most current
+ // configuration.
+ boost::shared_ptr<CtrlAgentController> controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(CtrlAgentController::instance());
+ if (controller) {
+ CtrlAgentProcessPtr process = controller->getCtrlAgentProcess();
+ if (process) {
+ CtrlAgentCfgMgrPtr cfgmgr = process->getCtrlAgentCfgMgr();
+ if (cfgmgr) {
+ ctx = cfgmgr->getCtrlAgentCfgContext();
+ if (ctx) {
+ const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
+ if (auth) {
+ // Check authentication.
+ http_response = auth->checkAuth(*this, request);
+ }
+ }
+ }
+ }
+ }
+
+ // Callout point for "auth".
+ bool reset_handle = false;
+ if (HooksManager::calloutsPresent(Hooks.hook_index_auth_)) {
+ // Get callout handle.
+ CalloutHandlePtr callout_handle = request->getCalloutHandle();
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
+ // Pass arguments.
+ callout_handle->setArgument("request", request);
+ callout_handle->setArgument("response", http_response);
+
+ // Call callouts.
+ HooksManager::callCallouts(Hooks.hook_index_auth_, *callout_handle);
+ callout_handle->getArgument("request", request);
+ callout_handle->getArgument("response", http_response);
+
+ // Status other than continue means 'please reset the handle'.
+ if (callout_handle->getStatus() != CalloutHandle::NEXT_STEP_CONTINUE) {
+ reset_handle = true;
+ }
+ }
+
+ // The basic HTTP authentication check or a callout failed and
+ // left a response.
+ if (http_response) {
+ return (http_response);
+ }
+
+ // Reset the handle when a hook asks for.
+ if (reset_handle) {
+ request->resetCalloutHandle();
+ }
+
+ // The request is always non-null, because this is verified by the
+ // createHttpResponse method. Let's try to convert it to the
+ // PostHttpRequestJson type as this is the type generated by the
+ // createNewHttpRequest. If the conversion result is null it means that
+ // the caller did not use createNewHttpRequest method to create this
+ // instance. This is considered an error in the server logic.
+ PostHttpRequestJsonPtr request_json =
+ boost::dynamic_pointer_cast<PostHttpRequestJson>(request);
+ if (!request_json) {
+ // Notify the client that we have a problem with our server.
+ return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
+ }
+
+ // We have already checked that the request is finalized so the call
+ // to getBodyAsJson must not trigger an exception.
+ ConstElementPtr command = request_json->getBodyAsJson();
+
+ // Process command doesn't generate exceptions but can possibly return
+ // null response, if the handler is not implemented properly. This is
+ // again an internal server issue.
+ ConstElementPtr response = CtrlAgentCommandMgr::instance().processCommand(command);
+ if (!response) {
+ // Notify the client that we have a problem with our server.
+ return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
+ }
+ // The response is ok, so let's create new HTTP response with the status OK.
+ http_response = boost::dynamic_pointer_cast<
+ HttpResponseJson>(createStockHttpResponseInternal(request, HttpStatusCode::OK));
+ http_response->setBodyAsJson(response);
+ http_response->finalize();
+
+ // Callout point for "response".
+ if (HooksManager::calloutsPresent(Hooks.hook_index_response_)) {
+ // Get callout handle.
+ CalloutHandlePtr callout_handle = request->getCalloutHandle();
+ ScopedCalloutHandleState callout_handle_state(callout_handle);
+
+ // Pass arguments.
+ callout_handle->setArgument("request", request);
+ callout_handle->setArgument("response", http_response);
+
+ // Call callouts.
+ HooksManager::callCallouts(Hooks.hook_index_response_,
+ *callout_handle);
+ callout_handle->getArgument("response", http_response);
+
+ // Ignore status as the HTTP response is used instead.
+ }
+
+ return (http_response);
+}
+
+} // end of namespace isc::agent
+} // end of namespace isc
diff --git a/src/bin/agent/ca_response_creator.h b/src/bin/agent/ca_response_creator.h
new file mode 100644
index 0000000..bf216e8
--- /dev/null
+++ b/src/bin/agent/ca_response_creator.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2017-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 CTRL_AGENT_RESPONSE_CREATOR_H
+#define CTRL_AGENT_RESPONSE_CREATOR_H
+
+#include <agent/ca_command_mgr.h>
+#include <http/response_creator.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace agent {
+
+class CtrlAgentResponseCreator;
+
+/// @brief Pointer to the @ref CtrlAgentResponseCreator.
+typedef boost::shared_ptr<CtrlAgentResponseCreator> CtrlAgentResponseCreatorPtr;
+
+/// @brief Concrete implementation of the HTTP response creator used
+/// by the Control Agent.
+///
+/// See the documentation of the @ref isc::http::HttpResponseCreator for
+/// the basic information how HTTP response creators are utilized by
+/// the libkea-http library to generate HTTP responses.
+///
+/// This creator expects that received requests are encapsulated in the
+/// @ref isc::http::PostHttpRequestJson objects. The generated responses
+/// are encapsulated in the HttpResponseJson objects.
+///
+/// This class uses @ref CtrlAgentCommandMgr singleton to process commands
+/// conveyed in the HTTP body. The JSON responses returned by the manager
+/// are placed in the body of the generated HTTP responses.
+class CtrlAgentResponseCreator : public http::HttpResponseCreator {
+public:
+
+ /// @brief Create a new request.
+ ///
+ /// This method creates a bare instance of the @ref
+ /// isc::http::PostHttpRequestJson.
+ ///
+ /// @return Pointer to the new instance of the @ref
+ /// isc::http::PostHttpRequestJson.
+ virtual http::HttpRequestPtr createNewHttpRequest() const;
+
+ /// @brief Creates stock HTTP response.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @param status_code Status code of the response.
+ /// @return Pointer to an @ref isc::http::HttpResponseJson object
+ /// representing stock HTTP response.
+ virtual http::HttpResponsePtr
+ createStockHttpResponse(const http::HttpRequestPtr& request,
+ const http::HttpStatusCode& status_code) const;
+
+private:
+
+ /// @brief Creates unfinalized stock HTTP response.
+ ///
+ /// The unfinalized response is the response that can't be sent over the
+ /// wire until @c finalize() is called, which commits the contents of the
+ /// message body.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @param status_code Status code of the response.
+ /// @return Pointer to an @ref isc::http::HttpResponseJson object
+ /// representing stock HTTP response.
+ http::HttpResponsePtr
+ createStockHttpResponseInternal(const http::HttpRequestPtr& request,
+ const http::HttpStatusCode& status_code) const;
+
+ /// @brief Creates implementation specific HTTP response.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @return Pointer to an object representing HTTP response.
+ virtual http::HttpResponsePtr
+ createDynamicHttpResponse(http::HttpRequestPtr request);
+};
+
+} // end of namespace isc::agent
+} // end of namespace isc
+
+#endif
diff --git a/src/bin/agent/ca_response_creator_factory.h b/src/bin/agent/ca_response_creator_factory.h
new file mode 100644
index 0000000..cd3a86d
--- /dev/null
+++ b/src/bin/agent/ca_response_creator_factory.h
@@ -0,0 +1,55 @@
+// Copyright (C) 2017 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 CTRL_AGENT_RESPONSE_CREATOR_FACTORY_H
+#define CTRL_AGENT_RESPONSE_CREATOR_FACTORY_H
+
+#include <agent/ca_response_creator.h>
+#include <http/response_creator_factory.h>
+
+namespace isc {
+namespace agent {
+
+/// @brief HTTP response creator factory for Control Agent.
+///
+/// See the documentation of the @ref isc::http::HttpResponseCreatorFactory
+/// for the details how the response factory object is used by the
+/// @ref isc::http::HttpListener.
+///
+/// This class always returns the same instance of the
+/// @ref CtrlAgentResponseCreator which @ref isc::http::HttpListener and
+/// @ref isc::http::HttpConnection classes use to generate HTTP response
+/// messages which comply with the formats required by the Control Agent.
+class CtrlAgentResponseCreatorFactory : public http::HttpResponseCreatorFactory {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates sole instance of the @ref CtrlAgentResponseCreator object
+ /// returned by the @ref CtrlAgentResponseCreatorFactory::create.
+ CtrlAgentResponseCreatorFactory()
+ : sole_creator_(new CtrlAgentResponseCreator()) {
+ }
+
+ /// @brief Returns an instance of the @ref CtrlAgentResponseCreator which
+ /// is used by HTTP server to generate responses to commands.
+ ///
+ /// @return Pointer to the @ref CtrlAgentResponseCreator object.
+ virtual http::HttpResponseCreatorPtr create() const {
+ return (sole_creator_);
+ }
+
+private:
+
+ /// @brief Instance of the @ref CtrlAgentResponseCreator returned.
+ http::HttpResponseCreatorPtr sole_creator_;
+
+};
+
+} // end of namespace isc::agent
+} // end of namespace isc
+
+#endif
diff --git a/src/bin/agent/location.hh b/src/bin/agent/location.hh
new file mode 100644
index 0000000..3f820cc
--- /dev/null
+++ b/src/bin/agent/location.hh
@@ -0,0 +1,306 @@
+// A Bison parser, made by GNU Bison 3.8.2.
+
+// Locations for Bison parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton. Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+/**
+ ** \file location.hh
+ ** Define the isc::agent::location class.
+ */
+
+#ifndef YY_AGENT_LOCATION_HH_INCLUDED
+# define YY_AGENT_LOCATION_HH_INCLUDED
+
+# include <iostream>
+# include <string>
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus
+# if 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# else
+# define YY_NULLPTR ((void*)0)
+# endif
+# endif
+
+#line 14 "agent_parser.yy"
+namespace isc { namespace agent {
+#line 59 "location.hh"
+
+ /// A point in a source file.
+ class position
+ {
+ public:
+ /// Type for file name.
+ typedef const std::string filename_type;
+ /// Type for line and column numbers.
+ typedef int counter_type;
+
+ /// Construct a position.
+ explicit position (filename_type* f = YY_NULLPTR,
+ counter_type l = 1,
+ counter_type c = 1)
+ : filename (f)
+ , line (l)
+ , column (c)
+ {}
+
+
+ /// Initialization.
+ void initialize (filename_type* fn = YY_NULLPTR,
+ counter_type l = 1,
+ counter_type c = 1)
+ {
+ filename = fn;
+ line = l;
+ column = c;
+ }
+
+ /** \name Line and Column related manipulators
+ ** \{ */
+ /// (line related) Advance to the COUNT next lines.
+ void lines (counter_type count = 1)
+ {
+ if (count)
+ {
+ column = 1;
+ line = add_ (line, count, 1);
+ }
+ }
+
+ /// (column related) Advance to the COUNT next columns.
+ void columns (counter_type count = 1)
+ {
+ column = add_ (column, count, 1);
+ }
+ /** \} */
+
+ /// File name to which this position refers.
+ filename_type* filename;
+ /// Current line number.
+ counter_type line;
+ /// Current column number.
+ counter_type column;
+
+ private:
+ /// Compute max (min, lhs+rhs).
+ static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min)
+ {
+ return lhs + rhs < min ? min : lhs + rhs;
+ }
+ };
+
+ /// Add \a width columns, in place.
+ inline position&
+ operator+= (position& res, position::counter_type width)
+ {
+ res.columns (width);
+ return res;
+ }
+
+ /// Add \a width columns.
+ inline position
+ operator+ (position res, position::counter_type width)
+ {
+ return res += width;
+ }
+
+ /// Subtract \a width columns, in place.
+ inline position&
+ operator-= (position& res, position::counter_type width)
+ {
+ return res += -width;
+ }
+
+ /// Subtract \a width columns.
+ inline position
+ operator- (position res, position::counter_type width)
+ {
+ return res -= width;
+ }
+
+ /** \brief Intercept output stream redirection.
+ ** \param ostr the destination output stream
+ ** \param pos a reference to the position to redirect
+ */
+ template <typename YYChar>
+ std::basic_ostream<YYChar>&
+ operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
+ {
+ if (pos.filename)
+ ostr << *pos.filename << ':';
+ return ostr << pos.line << '.' << pos.column;
+ }
+
+ /// Two points in a source file.
+ class location
+ {
+ public:
+ /// Type for file name.
+ typedef position::filename_type filename_type;
+ /// Type for line and column numbers.
+ typedef position::counter_type counter_type;
+
+ /// Construct a location from \a b to \a e.
+ location (const position& b, const position& e)
+ : begin (b)
+ , end (e)
+ {}
+
+ /// Construct a 0-width location in \a p.
+ explicit location (const position& p = position ())
+ : begin (p)
+ , end (p)
+ {}
+
+ /// Construct a 0-width location in \a f, \a l, \a c.
+ explicit location (filename_type* f,
+ counter_type l = 1,
+ counter_type c = 1)
+ : begin (f, l, c)
+ , end (f, l, c)
+ {}
+
+
+ /// Initialization.
+ void initialize (filename_type* f = YY_NULLPTR,
+ counter_type l = 1,
+ counter_type c = 1)
+ {
+ begin.initialize (f, l, c);
+ end = begin;
+ }
+
+ /** \name Line and Column related manipulators
+ ** \{ */
+ public:
+ /// Reset initial location to final location.
+ void step ()
+ {
+ begin = end;
+ }
+
+ /// Extend the current location to the COUNT next columns.
+ void columns (counter_type count = 1)
+ {
+ end += count;
+ }
+
+ /// Extend the current location to the COUNT next lines.
+ void lines (counter_type count = 1)
+ {
+ end.lines (count);
+ }
+ /** \} */
+
+
+ public:
+ /// Beginning of the located region.
+ position begin;
+ /// End of the located region.
+ position end;
+ };
+
+ /// Join two locations, in place.
+ inline location&
+ operator+= (location& res, const location& end)
+ {
+ res.end = end.end;
+ return res;
+ }
+
+ /// Join two locations.
+ inline location
+ operator+ (location res, const location& end)
+ {
+ return res += end;
+ }
+
+ /// Add \a width columns to the end position, in place.
+ inline location&
+ operator+= (location& res, location::counter_type width)
+ {
+ res.columns (width);
+ return res;
+ }
+
+ /// Add \a width columns to the end position.
+ inline location
+ operator+ (location res, location::counter_type width)
+ {
+ return res += width;
+ }
+
+ /// Subtract \a width columns to the end position, in place.
+ inline location&
+ operator-= (location& res, location::counter_type width)
+ {
+ return res += -width;
+ }
+
+ /// Subtract \a width columns to the end position.
+ inline location
+ operator- (location res, location::counter_type width)
+ {
+ return res -= width;
+ }
+
+ /** \brief Intercept output stream redirection.
+ ** \param ostr the destination output stream
+ ** \param loc a reference to the location to redirect
+ **
+ ** Avoid duplicate information.
+ */
+ template <typename YYChar>
+ std::basic_ostream<YYChar>&
+ operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
+ {
+ location::counter_type end_col
+ = 0 < loc.end.column ? loc.end.column - 1 : 0;
+ ostr << loc.begin;
+ if (loc.end.filename
+ && (!loc.begin.filename
+ || *loc.begin.filename != *loc.end.filename))
+ ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col;
+ else if (loc.begin.line < loc.end.line)
+ ostr << '-' << loc.end.line << '.' << end_col;
+ else if (loc.begin.column < end_col)
+ ostr << '-' << end_col;
+ return ostr;
+ }
+
+#line 14 "agent_parser.yy"
+} } // isc::agent
+#line 305 "location.hh"
+
+#endif // !YY_AGENT_LOCATION_HH_INCLUDED
diff --git a/src/bin/agent/main.cc b/src/bin/agent/main.cc
new file mode 100644
index 0000000..6868708
--- /dev/null
+++ b/src/bin/agent/main.cc
@@ -0,0 +1,49 @@
+// 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/.
+
+#include <config.h>
+#include <agent/ca_controller.h>
+#include <exceptions/exceptions.h>
+#include <cstdlib>
+#include <iostream>
+
+using namespace isc::agent;
+using namespace isc::process;
+
+int main(int argc, char* argv[]) {
+ int ret = EXIT_SUCCESS;
+
+ // Launch the controller passing in command line arguments.
+ // Exit program with the controller's return code.
+ try {
+ // Instantiate/fetch the application controller singleton.
+ DControllerBasePtr& controller = CtrlAgentController::instance();
+
+ // 'false' value disables test mode.
+ ret = controller->launch(argc, argv, false);
+ } catch (const VersionMessage& ex) {
+ std::string msg(ex.what());
+ if (!msg.empty()) {
+ std::cout << msg << std::endl;
+ }
+ } catch (const InvalidUsage& ex) {
+ std::string msg(ex.what());
+ if (!msg.empty()) {
+ std::cerr << msg << std::endl;
+ }
+ ret = EXIT_FAILURE;
+ } catch (const std::exception& ex) {
+ std::cerr << "Service failed: " << ex.what() << std::endl;
+ ret = EXIT_FAILURE;
+ } catch (...) {
+ std::cerr << "Service failed" << std::endl;
+ ret = EXIT_FAILURE;
+ }
+
+ CtrlAgentController::instance().reset();
+
+ return (ret);
+}
diff --git a/src/bin/agent/parser_context.cc b/src/bin/agent/parser_context.cc
new file mode 100644
index 0000000..7c18e03
--- /dev/null
+++ b/src/bin/agent/parser_context.cc
@@ -0,0 +1,204 @@
+// Copyright (C) 2017-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 <agent/parser_context.h>
+#include <agent/agent_parser.h>
+#include <agent/ca_log.h>
+#include <exceptions/exceptions.h>
+#include <cc/dhcp_config_error.h>
+#include <cc/data.h>
+#include <fstream>
+#include <sstream>
+#include <limits>
+
+namespace isc {
+namespace agent {
+
+ParserContext::ParserContext()
+ : sfile_(0), ctx_(NO_KEYWORDS), trace_scanning_(false), trace_parsing_(false)
+{
+}
+
+ParserContext::~ParserContext()
+{
+}
+
+isc::data::ElementPtr
+ParserContext::parseString(const std::string& str, ParserType parser_type)
+{
+ scanStringBegin(str, parser_type);
+ return (parseCommon());
+}
+
+isc::data::ElementPtr
+ParserContext::parseFile(const std::string& filename, ParserType parser_type) {
+ FILE* f = fopen(filename.c_str(), "r");
+ if (!f) {
+ isc_throw(ParseError, "Unable to open file " << filename);
+ }
+ scanFileBegin(f, filename, parser_type);
+ return (parseCommon());
+}
+
+isc::data::ElementPtr
+ParserContext::parseCommon() {
+ isc::agent::AgentParser parser(*this);
+ // Uncomment this to get detailed parser logs.
+ // trace_parsing_ = true;
+ parser.set_debug_level(trace_parsing_);
+ try {
+ int res = parser.parse();
+ if (res != 0) {
+ isc_throw(ParseError, "Parser abort");
+ }
+ scanEnd();
+ }
+ catch (...) {
+ scanEnd();
+ throw;
+ }
+ if (stack_.size() == 1) {
+ return (stack_[0]);
+ } else {
+ isc_throw(ParseError, "Expected exactly one terminal Element expected, found "
+ << stack_.size());
+ }
+}
+
+void
+ParserContext::error(const isc::agent::location& loc,
+ const std::string& what,
+ size_t pos)
+{
+ if (pos == 0) {
+ isc_throw(ParseError, loc << ": " << what);
+ } else {
+ isc_throw(ParseError, loc << " (near " << pos << "): " << what);
+ }
+}
+
+void
+ParserContext::error(const std::string& what)
+{
+ isc_throw(ParseError, what);
+}
+
+void
+ParserContext::fatal(const std::string& what)
+{
+ isc_throw(ParseError, what);
+}
+
+isc::data::Element::Position
+ParserContext::loc2pos(isc::agent::location& loc)
+{
+ const std::string& file = *loc.begin.filename;
+ const uint32_t line = loc.begin.line;
+ const uint32_t pos = loc.begin.column;
+ return (isc::data::Element::Position(file, line, pos));
+}
+
+void
+ParserContext::require(const std::string& name,
+ isc::data::Element::Position open_loc,
+ isc::data::Element::Position close_loc)
+{
+ ConstElementPtr value = stack_.back()->get(name);
+ if (!value) {
+ isc_throw(ParseError,
+ "missing parameter '" << name << "' ("
+ << stack_.back()->getPosition() << ") ["
+ << contextName() << " map between "
+ << open_loc << " and " << close_loc << "]");
+ }
+}
+
+void
+ParserContext::unique(const std::string& name,
+ isc::data::Element::Position loc)
+{
+ ConstElementPtr value = stack_.back()->get(name);
+ if (value) {
+ if (ctx_ != NO_KEYWORDS) {
+ isc_throw(ParseError, loc << ": duplicate " << name
+ << " entries in " << contextName()
+ << " map (previous at " << value->getPosition() << ")");
+ } else {
+ isc_throw(ParseError, loc << ": duplicate " << name
+ << " entries in JSON"
+ << " map (previous at " << value->getPosition() << ")");
+ }
+ }
+}
+
+void
+ParserContext::enter(const LexerContext& ctx)
+{
+ cstack_.push_back(ctx_);
+ ctx_ = ctx;
+}
+
+void
+ParserContext::leave()
+{
+ if (cstack_.empty()) {
+ fatal("unbalanced syntactic context");
+ }
+ ctx_ = cstack_.back();
+ cstack_.pop_back();
+}
+
+const std::string
+ParserContext::contextName()
+{
+ switch (ctx_) {
+ case NO_KEYWORDS:
+ return ("__no keywords__");
+ case CONFIG:
+ return ("toplevel");
+ case AGENT:
+ return ("Control-agent");
+ case AUTHENTICATION:
+ return ("authentication");
+ case AUTH_TYPE:
+ return ("auth-type");
+ case CLIENTS:
+ return ("clients");
+ case CONTROL_SOCKETS:
+ return ("control-sockets");
+ case SERVER:
+ return ("xxx-server");
+ case SOCKET_TYPE:
+ return ("socket-type");
+ case HOOKS_LIBRARIES:
+ return ("hooks-libraries");
+ case LOGGERS:
+ return ("loggers");
+ case OUTPUT_OPTIONS:
+ return ("output-options");
+ default:
+ return ("__unknown__");
+ }
+}
+
+void
+ParserContext::warning(const isc::agent::location& loc,
+ const std::string& what) {
+ std::ostringstream msg;
+ msg << loc << ": " << what;
+ LOG_WARN(agent_logger, CTRL_AGENT_CONFIG_SYNTAX_WARNING)
+ .arg(msg.str());
+}
+
+void
+ParserContext::warnAboutExtraCommas(const isc::agent::location& loc) {
+ warning(loc, "Extraneous comma. A piece of configuration may have been omitted.");
+}
+
+} // end of isc::eval namespace
+} // end of isc namespace
diff --git a/src/bin/agent/parser_context.h b/src/bin/agent/parser_context.h
new file mode 100644
index 0000000..a6816d7
--- /dev/null
+++ b/src/bin/agent/parser_context.h
@@ -0,0 +1,317 @@
+// Copyright (C) 2017-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 PARSER_CONTEXT_H
+#define PARSER_CONTEXT_H
+#include <string>
+#include <map>
+#include <vector>
+#include <agent/agent_parser.h>
+#include <agent/parser_context_decl.h>
+#include <exceptions/exceptions.h>
+
+// Tell Flex the lexer's prototype ...
+#define YY_DECL isc::agent::AgentParser::symbol_type agent_lex (ParserContext& driver)
+
+// ... and declare it for the parser's sake.
+YY_DECL;
+
+namespace isc {
+namespace agent {
+
+/// @brief Parser context is a wrapper around flex/bison instances dedicated to
+/// Control-agent config file parser.
+///
+/// It follows the same principle as other components. The primary interface
+/// are @ref parseString and @ref parseFile methods. All other methods are
+/// public for testing purposes only. This interface allows parsing the
+/// whole configuration with syntactic checking (which is by far the most
+/// frequent use), but it also allows parsing input as generic JSON or
+/// parse only content of the Control-agent object, which is a subset
+/// of full grammar (this will be very useful for unit-tests to not duplicate
+/// unnecessary parts of the config file).
+class ParserContext
+{
+public:
+
+ /// @brief Defines currently supported scopes
+ ///
+ /// AgentParser is able to parse several types of scope. Usually,
+ /// when it parses a config file, it expects the data to have a map
+ /// with Control-agent in it and all the parameters within that map.
+ /// However, sometimes the parser is expected to parse only a subset
+ /// of that information.
+ typedef enum {
+ /// This parser will parse the content as generic JSON.
+ PARSER_JSON,
+
+ /// This parser will expect the content as Control-agent config wrapped
+ /// in a map (that's the regular config file)
+ PARSER_AGENT,
+
+ /// This parser will expect only the content of Control-agent.
+ PARSER_SUB_AGENT
+ } ParserType;
+
+ /// @brief Default constructor.
+ ParserContext();
+
+ /// @brief destructor
+ virtual ~ParserContext();
+
+ /// @brief JSON elements being parsed.
+ std::vector<isc::data::ElementPtr> stack_;
+
+ /// @brief Method called before scanning starts on a string.
+ ///
+ /// @param str string to be parsed
+ /// @param type specifies expected content
+ void scanStringBegin(const std::string& str, ParserType type);
+
+ /// @brief Method called before scanning starts on a file.
+ ///
+ /// @param f stdio FILE pointer
+ /// @param filename file to be parsed
+ /// @param type specifies expected content
+ void scanFileBegin(FILE* f, const std::string& filename, ParserType type);
+
+ /// @brief Method called after the last tokens are scanned.
+ void scanEnd();
+
+ /// @brief Divert input to an include file.
+ ///
+ /// @param filename file to be included
+ void includeFile(const std::string& filename);
+
+ /// @brief Run the parser on the string specified.
+ ///
+ /// This method parses specified string. Depending on the value of
+ /// parser_type, parser may either check only that the input is valid
+ /// JSON, or may do more specific syntax checking. See @ref ParserType
+ /// for supported syntax checkers.
+ ///
+ /// @param str string to be parsed
+ /// @param parser_type specifies expected content (usually AGENT or generic JSON)
+ /// @return Element structure representing parsed text.
+ isc::data::ElementPtr parseString(const std::string& str,
+ ParserType parser_type);
+
+ /// @brief Run the parser on the file specified.
+ ///
+ /// This method parses specified file. Depending on the value of
+ /// parser_type, parser may either check only that the input is valid
+ /// JSON, or may do more specific syntax checking. See @ref ParserType
+ /// for supported syntax checkers.
+ ///
+ /// @param filename file to be parsed
+ /// @param parser_type specifies expected content (usually PARSER_AGENT or
+ /// PARSER_JSON)
+ /// @return Element structure representing parsed text.
+ isc::data::ElementPtr parseFile(const std::string& filename,
+ ParserType parser_type);
+
+ /// @brief Error handler
+ ///
+ /// @note The optional position for an error in a string begins by 1
+ /// so the caller should add 1 to the position of the C++ string.
+ ///
+ /// @param loc location within the parsed file where the problem was experienced.
+ /// @param what string explaining the nature of the error.
+ /// @param pos optional position for in string errors.
+ /// @throw ParseError
+ void error(const isc::agent::location& loc,
+ const std::string& what,
+ size_t pos = 0);
+
+ /// @brief Error handler
+ ///
+ /// This is a simplified error reporting tool for possible future
+ /// cases when the AgentParser is not able to handle the packet.
+ ///
+ /// @param what string explaining the nature of the error.
+ /// @throw ParseError
+ void error(const std::string& what);
+
+ /// @brief Fatal error handler
+ ///
+ /// This is for should not happen but fatal errors.
+ /// Used by YY_FATAL_ERROR macro so required to be static.
+ ///
+ /// @param what string explaining the nature of the error.
+ /// @throw ParseError
+ static void fatal(const std::string& what);
+
+ /// @brief Converts bison's position to one understandable by isc::data::Element
+ ///
+ /// Convert a bison location into an element position
+ /// (take the begin, the end is lost)
+ ///
+ /// @param loc location in bison format
+ /// @return Position in format accepted by Element
+ isc::data::Element::Position loc2pos(isc::agent::location& loc);
+
+ /// @brief Check if a required parameter is present
+ ///
+ /// Check if a required parameter is present in the map at the top
+ /// of the stack and raise an error when it is not.
+ ///
+ /// @param name name of the parameter to check
+ /// @param open_loc location of the opening curly bracket
+ /// @param close_loc location of the closing curly bracket
+ /// @throw ParseError
+ void require(const std::string& name,
+ isc::data::Element::Position open_loc,
+ isc::data::Element::Position close_loc);
+
+ /// @brief Check if a parameter is already present
+ ///
+ /// Check if a parameter is already present in the map at the top
+ /// of the stack and raise an error when it is.
+ ///
+ /// @param name name of the parameter to check
+ /// @param loc location of the current parameter
+ /// @throw ParseError
+ void unique(const std::string& name,
+ isc::data::Element::Position loc);
+
+ /// @brief Warning handler
+ ///
+ /// @param loc location within the parsed file where the problem was experienced
+ /// @param what string explaining the nature of the error
+ ///
+ /// @throw ParseError
+ void warning(const isc::agent::location& loc, const std::string& what);
+
+ /// @brief Warning for extra commas
+ ///
+ /// @param loc location within the parsed file of the extra comma
+ ///
+ /// @throw ParseError
+ void warnAboutExtraCommas(const isc::agent::location& loc);
+
+ /// @brief Defines syntactic contexts for lexical tie-ins
+ typedef enum {
+ ///< This one is used in pure JSON mode.
+ NO_KEYWORDS,
+
+ ///< Used while parsing top level (that contains Control-agent)
+ CONFIG,
+
+ ///< Used while parsing content of Agent.
+ AGENT,
+
+ ///< Used while parsing Control-agent/Authentication.
+ AUTHENTICATION,
+
+ ///< Used while parsing Control-agent/Authentication/type.
+ AUTH_TYPE,
+
+ ///< Used while parsing Control-agent/Authentication/clients.
+ CLIENTS,
+
+ ///< Used while parsing Control-agent/control-sockets.
+ CONTROL_SOCKETS,
+
+ ///< Used while parsing Control-agent/control-socket/*-server.
+ SERVER,
+
+ ///< Used while parsing Control-agent/control-socket/*-server/socket-type.
+ SOCKET_TYPE,
+
+ ///< Used while parsing Control-agent/hooks-libraries.
+ HOOKS_LIBRARIES,
+
+ ///< Used while parsing Control-agent/loggers structures.
+ LOGGERS,
+
+ ///< Used while parsing Control-agent/loggers/output_options structures.
+ OUTPUT_OPTIONS
+ } LexerContext;
+
+ /// @brief File name
+ std::string file_;
+
+ /// @brief File name stack
+ std::vector<std::string> files_;
+
+ /// @brief Location of the current token
+ ///
+ /// The lexer will keep updating it. This variable will be useful
+ /// for logging errors.
+ isc::agent::location loc_;
+
+ /// @brief Location stack
+ std::vector<isc::agent::location> locs_;
+
+ /// @brief Lexer state stack
+ std::vector<struct yy_buffer_state*> states_;
+
+ /// @brief sFile (aka FILE)
+ FILE* sfile_;
+
+ /// @brief sFile (aka FILE) stack
+ ///
+ /// This is a stack of files. Typically there's only one file (the
+ /// one being currently parsed), but there may be more if one
+ /// file includes another.
+ std::vector<FILE*> sfiles_;
+
+ /// @brief Current syntactic context
+ LexerContext ctx_;
+
+ /// @brief Enter a new syntactic context
+ ///
+ /// Entering a new syntactic context is useful in several ways.
+ /// First, it allows the parser to avoid conflicts. Second, it
+ /// allows the lexer to return different tokens depending on
+ /// context (e.g. if "renew-timer" string is detected, the lexer
+ /// will return STRING token if in JSON mode or RENEW_TIMER if
+ /// in DHCP6 mode. Finally, the syntactic context allows the
+ /// error message to be more descriptive if the input string
+ /// does not parse properly. Control Agent parser uses simplified
+ /// contexts: either it recognizes keywords (value set to KEYWORDS)
+ /// or not (value set to NO_KEYWORDS).
+ ///
+ /// Make sure to call @ref leave() once the parsing of your
+ /// context is complete.
+ ///
+ /// @param ctx the syntactic context to enter into
+ void enter(const LexerContext& ctx);
+
+ /// @brief Leave a syntactic context
+ ///
+ /// @ref enter() must be called before (when entering a new scope
+ /// or context). Once you complete the parsing, this method
+ /// should be called.
+ ///
+ /// @throw isc::Unexpected if unbalanced (more leave() than enter() calls)
+ void leave();
+
+ /// @brief Get the syntactic context name
+ ///
+ /// @return printable name of the context.
+ const std::string contextName();
+
+ private:
+ /// @brief Flag determining scanner debugging.
+ bool trace_scanning_;
+
+ /// @brief Flag determining parser debugging.
+ bool trace_parsing_;
+
+ /// @brief Syntactic context stack
+ std::vector<LexerContext> cstack_;
+
+ /// @brief Common part of parseXXX
+ ///
+ /// @return Element structure representing parsed text.
+ isc::data::ElementPtr parseCommon();
+};
+
+} // end of isc::eval namespace
+} // end of isc namespace
+
+#endif
diff --git a/src/bin/agent/parser_context_decl.h b/src/bin/agent/parser_context_decl.h
new file mode 100644
index 0000000..3ab5448
--- /dev/null
+++ b/src/bin/agent/parser_context_decl.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 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 AGENT_CONTEXT_DECL_H
+#define AGENT_CONTEXT_DECL_H
+
+/// @file agent/parser_context_decl.h Forward declaration of the ParserContext class
+
+namespace isc {
+namespace agent {
+
+class ParserContext;
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif
diff --git a/src/bin/agent/simple_parser.cc b/src/bin/agent/simple_parser.cc
new file mode 100644
index 0000000..0b267e7
--- /dev/null
+++ b/src/bin/agent/simple_parser.cc
@@ -0,0 +1,191 @@
+// Copyright (C) 2017-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 <agent/simple_parser.h>
+#include <cc/data.h>
+#include <cc/dhcp_config_error.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/hooks_parser.h>
+#include <http/basic_auth_config.h>
+#include <boost/foreach.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace agent {
+/// @brief This sets of arrays define the default values in various scopes
+/// of the Control Agent Configuration.
+///
+/// Each of those is documented in @file agent/simple_parser.cc. This
+/// is different than most other comments in Kea code. The reason
+/// for placing those in .cc rather than .h file is that it
+/// is expected to be one centralized place to look at for
+/// the default values. This is expected to be looked at also by
+/// people who are not skilled in C or C++, so they may be
+/// confused with the differences between declaration and definition.
+/// As such, there's one file to look at that hopefully is readable
+/// without any C or C++ skills.
+///
+/// @{
+
+/// @brief This table defines default values for global options.
+///
+/// These are global Control Agent parameters.
+const SimpleDefaults AgentSimpleParser::AGENT_DEFAULTS = {
+ { "http-host", Element::string, "127.0.0.1" },
+ { "http-port", Element::integer, "8000" },
+ { "trust-anchor", Element::string, "" },
+ { "cert-file", Element::string, "" },
+ { "key-file", Element::string, "" },
+ { "cert-required", Element::boolean, "true" }
+};
+
+/// @brief This table defines default values for authentication.
+const SimpleDefaults AgentSimpleParser::AUTH_DEFAULTS = {
+ { "type", Element::string, "basic" },
+ { "realm", Element::string, "kea-control-agent" },
+ { "directory", Element::string, "" }
+};
+
+/// @brief This table defines default values for control sockets.
+///
+const SimpleDefaults AgentSimpleParser::SOCKET_DEFAULTS = {
+ { "socket-type", Element::string, "unix" }
+};
+
+/// @}
+
+/// ---------------------------------------------------------------------------
+/// --- end of default values -------------------------------------------------
+/// ---------------------------------------------------------------------------
+
+size_t AgentSimpleParser::setAllDefaults(const isc::data::ElementPtr& global) {
+ size_t cnt = 0;
+
+ // Set global defaults first.
+ cnt = setDefaults(global, AGENT_DEFAULTS);
+
+ // After set the defaults for authentication if it exists.
+ ConstElementPtr authentication = global->get("authentication");
+ if (authentication) {
+ ElementPtr auth = boost::const_pointer_cast<Element>(authentication);
+ if (auth) {
+ cnt += SimpleParser::setDefaults(auth, AUTH_DEFAULTS);
+ }
+ }
+
+ // Now set the defaults for control-sockets, if any.
+ ConstElementPtr sockets = global->get("control-sockets");
+ if (sockets) {
+ ElementPtr d2 = boost::const_pointer_cast<Element>(sockets->get("d2"));
+ if (d2) {
+ cnt += SimpleParser::setDefaults(d2, SOCKET_DEFAULTS);
+ }
+
+ ElementPtr d4 = boost::const_pointer_cast<Element>(sockets->get("dhcp4"));
+ if (d4) {
+ cnt += SimpleParser::setDefaults(d4, SOCKET_DEFAULTS);
+ }
+
+ ElementPtr d6 = boost::const_pointer_cast<Element>(sockets->get("dhcp6"));
+ if (d6) {
+ cnt += SimpleParser::setDefaults(d6, SOCKET_DEFAULTS);
+ }
+ }
+
+ return (cnt);
+}
+
+void
+AgentSimpleParser::checkTlsSetup(const isc::data::ConstElementPtr& config) {
+ ConstElementPtr ca = config->get("trust-anchor");
+ ConstElementPtr cert = config->get("cert-file");
+ ConstElementPtr key = config->get("key-file");
+ bool have_ca = (ca && !ca->stringValue().empty());
+ bool have_cert = (cert && !cert->stringValue().empty());
+ bool have_key = (key && !key->stringValue().empty());
+ if (!have_ca && !have_cert && !have_key) {
+ // No TLS parameter so TLS is not used.
+ return;
+ }
+ // TLS is used: all 3 parameters are required.
+ if (!have_ca) {
+ isc_throw(ConfigError, "trust-anchor parameter is missing or empty:"
+ " all or none of TLS parameters must be set");
+ }
+ if (!have_cert) {
+ isc_throw(ConfigError, "cert-file parameter is missing or empty:"
+ " all or none of TLS parameters must be set");
+ }
+ if (!have_key) {
+ isc_throw(ConfigError, "key-file parameter is missing or empty:"
+ " all or none of TLS parameters must be set");
+ }
+}
+
+void
+AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx,
+ const isc::data::ConstElementPtr& config,
+ bool check_only) {
+
+ // Let's get the HTTP parameters first.
+ ctx->setHttpHost(SimpleParser::getString(config, "http-host"));
+ ctx->setHttpPort(SimpleParser::getIntType<uint16_t>(config, "http-port"));
+
+ // TLS parameter are second.
+ ctx->setTrustAnchor(SimpleParser::getString(config, "trust-anchor"));
+ ctx->setCertFile(SimpleParser::getString(config, "cert-file"));
+ ctx->setKeyFile(SimpleParser::getString(config, "key-file"));
+ ctx->setCertRequired(SimpleParser::getBoolean(config, "cert-required"));
+
+ // Control sockets are third.
+ ConstElementPtr ctrl_sockets = config->get("control-sockets");
+ if (ctrl_sockets) {
+ auto sockets_map = ctrl_sockets->mapValue();
+ for (auto cs = sockets_map.cbegin(); cs != sockets_map.cend(); ++cs) {
+ ctx->setControlSocketInfo(cs->second, cs->first);
+ }
+ }
+
+ // Basic HTTP authentications are forth.
+ ConstElementPtr auth_config = config->get("authentication");
+ if (auth_config) {
+ using namespace isc::http;
+ BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+ auth->parse(auth_config);
+ ctx->setAuthConfig(auth);
+ }
+
+ // User context can be done at anytime.
+ ConstElementPtr user_context = config->get("user-context");
+ if (user_context) {
+ ctx->setContext(user_context);
+ }
+
+ // Finally, let's get the hook libs!
+ using namespace isc::hooks;
+ HooksConfig& libraries = ctx->getHooksConfig();
+ ConstElementPtr hooks = config->get("hooks-libraries");
+ if (hooks) {
+ HooksLibrariesParser hooks_parser;
+ hooks_parser.parse(libraries, hooks);
+ libraries.verifyLibraries(hooks->getPosition());
+ }
+
+ if (!check_only) {
+ // This occurs last as if it succeeds, there is no easy way
+ // revert it. As a result, the failure to commit a subsequent
+ // change causes problems when trying to roll back.
+ HooksManager::prepareUnloadLibraries();
+ static_cast<void>(HooksManager::unloadLibraries());
+ libraries.loadLibraries();
+ }
+}
+
+}
+}
diff --git a/src/bin/agent/simple_parser.h b/src/bin/agent/simple_parser.h
new file mode 100644
index 0000000..20f61d8
--- /dev/null
+++ b/src/bin/agent/simple_parser.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2017-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 AGENT_SIMPLE_PARSER_H
+#define AGENT_SIMPLE_PARSER_H
+
+#include <cc/simple_parser.h>
+#include <agent/ca_cfg_mgr.h>
+
+namespace isc {
+namespace agent {
+
+/// @brief SimpleParser specialized for Control Agent
+///
+/// This class is a @ref isc::data::SimpleParser dedicated to Control Agent.
+/// In particular, it contains all the default values for the whole
+/// agent and for the socket defaults.
+///
+/// For the actual values, see @file agent/simple_parser.cc
+class AgentSimpleParser : public isc::data::SimpleParser {
+public:
+ /// @brief Sets all defaults for Control Agent configuration
+ ///
+ /// This method sets global, option data and option definitions defaults.
+ ///
+ /// @param global scope to be filled in with defaults.
+ /// @return number of default values added
+ static size_t setAllDefaults(const isc::data::ElementPtr& global);
+
+ /// @brief Check TLS setup consistency i.e. all or none.
+ ///
+ /// @param config - Element tree structure that holds configuration.
+ /// @throw ConfigError when the configuration is not consistent.
+ void checkTlsSetup(const isc::data::ConstElementPtr& config);
+
+ /// @brief Parses the control agent configuration
+ ///
+ /// @param ctx - parsed information will be stored here
+ /// @param config - Element tree structure that holds configuration
+ /// @param check_only - if true the configuration is verified only, not applied
+ ///
+ /// @throw ConfigError if any issues are encountered.
+ void parse(const CtrlAgentCfgContextPtr& ctx,
+ const isc::data::ConstElementPtr& config,
+ bool check_only);
+
+ // see simple_parser.cc for comments for those parameters
+ static const isc::data::SimpleDefaults AGENT_DEFAULTS;
+ static const isc::data::SimpleDefaults AUTH_DEFAULTS;
+ static const isc::data::SimpleDefaults SOCKET_DEFAULTS;
+};
+
+}
+}
+#endif
diff --git a/src/bin/agent/tests/Makefile.am b/src/bin/agent/tests/Makefile.am
new file mode 100644
index 0000000..7cfedfe
--- /dev/null
+++ b/src/bin/agent/tests/Makefile.am
@@ -0,0 +1,120 @@
+SUBDIRS = .
+
+# Add to the tarball:
+EXTRA_DIST = testdata/get_config.json
+EXTRA_DIST += testdata/hiddenp
+EXTRA_DIST += testdata/hiddens
+EXTRA_DIST += testdata/hiddenu
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+# Shell tests
+SHTESTS = ca_process_tests.sh
+
+# Run shell tests on "make check".
+check_SCRIPTS = $(SHTESTS)
+TESTS = $(SHTESTS)
+
+# As with every file generated by ./configure, clean them up when running
+# "make distclean", but not on "make clean".
+DISTCLEANFILES = $(SHTESTS)
+DISTCLEANFILES += test_basic_auth_libraries.h
+DISTCLEANFILES += test_callout_libraries.h
+DISTCLEANFILES += test_data_files_config.h
+
+# Don't install shell tests.
+noinst_SCRIPTS = $(SHTESTS)
+
+if HAVE_GTEST
+
+# C++ tests
+PROGRAM_TESTS = ca_unittests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/agent/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/agent\"
+AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../agent_parser.yy\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+ca_unittests_SOURCES = ca_cfg_mgr_unittests.cc
+ca_unittests_SOURCES += ca_command_mgr_unittests.cc
+ca_unittests_SOURCES += ca_controller_unittests.cc
+ca_unittests_SOURCES += ca_process_unittests.cc
+ca_unittests_SOURCES += ca_response_creator_unittests.cc
+ca_unittests_SOURCES += ca_response_creator_factory_unittests.cc
+ca_unittests_SOURCES += ca_unittests.cc
+ca_unittests_SOURCES += parser_unittests.cc
+ca_unittests_SOURCES += get_config_unittest.cc
+
+ca_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+ca_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+ca_unittests_LDADD = $(top_builddir)/src/bin/agent/libagent.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/process/testutils/libprocesstest.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/asiolink/testutils/libasiolinktest.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+ca_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+ca_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS)
+ca_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD)
+
+# The basic callout library - contains standard callouts
+libcallout_la_SOURCES = callout_library.cc
+libcallout_la_CXXFLAGS = $(AM_CXXFLAGS)
+libcallout_la_CPPFLAGS = $(AM_CPPFLAGS)
+libcallout_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libcallout_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libcallout_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libcallout_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The basic HTTP auth as a callout library
+libbasicauth_la_SOURCES = basic_auth_library.cc
+libbasicauth_la_CXXFLAGS = $(AM_CXXFLAGS)
+libbasicauth_la_CPPFLAGS = $(AM_CPPFLAGS)
+libbasicauth_la_LIBADD = $(top_builddir)/src/lib/http/libkea-http.la
+libbasicauth_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libbasicauth_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libbasicauth_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libbasicauth_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libbasicauth_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libbasicauth_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libbasicauth_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
+libbasicauth_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+nodist_ca_unittests_SOURCES = test_data_files_config.h
+nodist_ca_unittests_SOURCES += test_basic_auth_libraries.h
+nodist_ca_unittests_SOURCES += test_callout_libraries.h
+
+# Run C++ tests on "make check".
+TESTS += $(PROGRAM_TESTS)
+
+# Don't install test libraries.
+noinst_LTLIBRARIES = libcallout.la libbasicauth.la
+
+# Don't install C++ tests.
+noinst_PROGRAMS = $(PROGRAM_TESTS)
+
+endif
diff --git a/src/bin/agent/tests/Makefile.in b/src/bin/agent/tests/Makefile.in
new file mode 100644
index 0000000..84f3345
--- /dev/null
+++ b/src/bin/agent/tests/Makefile.in
@@ -0,0 +1,1324 @@
+# 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 = $(SHTESTS) $(am__EXEEXT_2)
+
+# Run C++ tests on "make check".
+@HAVE_GTEST_TRUE@am__append_1 = $(PROGRAM_TESTS)
+@HAVE_GTEST_TRUE@noinst_PROGRAMS = $(am__EXEEXT_1)
+subdir = src/bin/agent/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 = ca_process_tests.sh test_basic_auth_libraries.h \
+ test_callout_libraries.h test_data_files_config.h
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = ca_unittests$(EXEEXT)
+PROGRAMS = $(noinst_PROGRAMS)
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@libbasicauth_la_DEPENDENCIES = \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.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/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)
+am__libbasicauth_la_SOURCES_DIST = basic_auth_library.cc
+@HAVE_GTEST_TRUE@am_libbasicauth_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libbasicauth_la-basic_auth_library.lo
+libbasicauth_la_OBJECTS = $(am_libbasicauth_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 =
+libbasicauth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(libbasicauth_la_CXXFLAGS) $(CXXFLAGS) \
+ $(libbasicauth_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libbasicauth_la_rpath =
+@HAVE_GTEST_TRUE@libcallout_la_DEPENDENCIES = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la
+am__libcallout_la_SOURCES_DIST = callout_library.cc
+@HAVE_GTEST_TRUE@am_libcallout_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libcallout_la-callout_library.lo
+libcallout_la_OBJECTS = $(am_libcallout_la_OBJECTS)
+libcallout_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(libcallout_la_CXXFLAGS) $(CXXFLAGS) $(libcallout_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libcallout_la_rpath =
+am__ca_unittests_SOURCES_DIST = ca_cfg_mgr_unittests.cc \
+ ca_command_mgr_unittests.cc ca_controller_unittests.cc \
+ ca_process_unittests.cc ca_response_creator_unittests.cc \
+ ca_response_creator_factory_unittests.cc ca_unittests.cc \
+ parser_unittests.cc get_config_unittest.cc
+@HAVE_GTEST_TRUE@am_ca_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_cfg_mgr_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_command_mgr_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_controller_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_process_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_response_creator_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_response_creator_factory_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-ca_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-parser_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ ca_unittests-get_config_unittest.$(OBJEXT)
+nodist_ca_unittests_OBJECTS =
+ca_unittests_OBJECTS = $(am_ca_unittests_OBJECTS) \
+ $(nodist_ca_unittests_OBJECTS)
+@HAVE_GTEST_TRUE@ca_unittests_DEPENDENCIES = \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/bin/agent/libagent.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/testutils/libprocesstest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.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/testutils/libasiolinktest.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/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.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/testutils/libkea-testutils.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)
+ca_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(ca_unittests_LDFLAGS) $(LDFLAGS) -o $@
+SCRIPTS = $(noinst_SCRIPTS)
+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)/ca_unittests-ca_cfg_mgr_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-ca_controller_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-ca_process_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-ca_response_creator_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-ca_unittests.Po \
+ ./$(DEPDIR)/ca_unittests-get_config_unittest.Po \
+ ./$(DEPDIR)/ca_unittests-parser_unittests.Po \
+ ./$(DEPDIR)/libbasicauth_la-basic_auth_library.Plo \
+ ./$(DEPDIR)/libcallout_la-callout_library.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 = $(libbasicauth_la_SOURCES) $(libcallout_la_SOURCES) \
+ $(ca_unittests_SOURCES) $(nodist_ca_unittests_SOURCES)
+DIST_SOURCES = $(am__libbasicauth_la_SOURCES_DIST) \
+ $(am__libcallout_la_SOURCES_DIST) \
+ $(am__ca_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; \
+}
+@HAVE_GTEST_TRUE@am__EXEEXT_2 = $(am__EXEEXT_1)
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(srcdir)/ca_process_tests.sh.in \
+ $(srcdir)/test_basic_auth_libraries.h.in \
+ $(srcdir)/test_callout_libraries.h.in \
+ $(srcdir)/test_data_files_config.h.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 = .
+
+# Add to the tarball:
+EXTRA_DIST = testdata/get_config.json testdata/hiddenp \
+ testdata/hiddens testdata/hiddenu
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+
+# Shell tests
+SHTESTS = ca_process_tests.sh
+
+# Run shell tests on "make check".
+check_SCRIPTS = $(SHTESTS)
+
+# As with every file generated by ./configure, clean them up when running
+# "make distclean", but not on "make clean".
+DISTCLEANFILES = $(SHTESTS) test_basic_auth_libraries.h \
+ test_callout_libraries.h test_data_files_config.h
+
+# Don't install shell tests.
+noinst_SCRIPTS = $(SHTESTS)
+
+# C++ tests
+@HAVE_GTEST_TRUE@PROGRAM_TESTS = ca_unittests
+@HAVE_GTEST_TRUE@AM_CPPFLAGS = -I$(top_srcdir)/src/lib \
+@HAVE_GTEST_TRUE@ -I$(top_builddir)/src/lib \
+@HAVE_GTEST_TRUE@ -I$(top_srcdir)/src/bin \
+@HAVE_GTEST_TRUE@ -I$(top_builddir)/src/bin $(BOOST_INCLUDES) \
+@HAVE_GTEST_TRUE@ $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) \
+@HAVE_GTEST_TRUE@ -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/agent/tests\" \
+@HAVE_GTEST_TRUE@ -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\" \
+@HAVE_GTEST_TRUE@ -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/agent\" \
+@HAVE_GTEST_TRUE@ -DSYNTAX_FILE=\"$(abs_srcdir)/../agent_parser.yy\"
+@HAVE_GTEST_TRUE@AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@HAVE_GTEST_TRUE@@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+@HAVE_GTEST_TRUE@ca_unittests_SOURCES = ca_cfg_mgr_unittests.cc \
+@HAVE_GTEST_TRUE@ ca_command_mgr_unittests.cc \
+@HAVE_GTEST_TRUE@ ca_controller_unittests.cc \
+@HAVE_GTEST_TRUE@ ca_process_unittests.cc \
+@HAVE_GTEST_TRUE@ ca_response_creator_unittests.cc \
+@HAVE_GTEST_TRUE@ ca_response_creator_factory_unittests.cc \
+@HAVE_GTEST_TRUE@ ca_unittests.cc parser_unittests.cc \
+@HAVE_GTEST_TRUE@ get_config_unittest.cc
+@HAVE_GTEST_TRUE@ca_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@ca_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+@HAVE_GTEST_TRUE@ca_unittests_LDADD = \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/bin/agent/libagent.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/testutils/libprocesstest.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.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/testutils/libasiolinktest.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/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.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/testutils/libkea-testutils.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)
+
+# The basic callout library - contains standard callouts
+@HAVE_GTEST_TRUE@libcallout_la_SOURCES = callout_library.cc
+@HAVE_GTEST_TRUE@libcallout_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libcallout_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libcallout_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.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@libcallout_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The basic HTTP auth as a callout library
+@HAVE_GTEST_TRUE@libbasicauth_la_SOURCES = basic_auth_library.cc
+@HAVE_GTEST_TRUE@libbasicauth_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libbasicauth_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libbasicauth_la_LIBADD = \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.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/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) $(BOOST_LIBS)
+@HAVE_GTEST_TRUE@libbasicauth_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+@HAVE_GTEST_TRUE@nodist_ca_unittests_SOURCES = \
+@HAVE_GTEST_TRUE@ test_data_files_config.h \
+@HAVE_GTEST_TRUE@ test_basic_auth_libraries.h \
+@HAVE_GTEST_TRUE@ test_callout_libraries.h
+
+# Don't install test libraries.
+@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libcallout.la libbasicauth.la
+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/bin/agent/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/bin/agent/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):
+ca_process_tests.sh: $(top_builddir)/config.status $(srcdir)/ca_process_tests.sh.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+test_basic_auth_libraries.h: $(top_builddir)/config.status $(srcdir)/test_basic_auth_libraries.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+test_callout_libraries.h: $(top_builddir)/config.status $(srcdir)/test_callout_libraries.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+test_data_files_config.h: $(top_builddir)/config.status $(srcdir)/test_data_files_config.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+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
+
+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}; \
+ }
+
+libbasicauth.la: $(libbasicauth_la_OBJECTS) $(libbasicauth_la_DEPENDENCIES) $(EXTRA_libbasicauth_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libbasicauth_la_LINK) $(am_libbasicauth_la_rpath) $(libbasicauth_la_OBJECTS) $(libbasicauth_la_LIBADD) $(LIBS)
+
+libcallout.la: $(libcallout_la_OBJECTS) $(libcallout_la_DEPENDENCIES) $(EXTRA_libcallout_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libcallout_la_LINK) $(am_libcallout_la_rpath) $(libcallout_la_OBJECTS) $(libcallout_la_LIBADD) $(LIBS)
+
+ca_unittests$(EXEEXT): $(ca_unittests_OBJECTS) $(ca_unittests_DEPENDENCIES) $(EXTRA_ca_unittests_DEPENDENCIES)
+ @rm -f ca_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(ca_unittests_LINK) $(ca_unittests_OBJECTS) $(ca_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_controller_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_process_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_response_creator_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-ca_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-get_config_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca_unittests-parser_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbasicauth_la-basic_auth_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcallout_la-callout_library.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 $@ $<
+
+libbasicauth_la-basic_auth_library.lo: basic_auth_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbasicauth_la_CPPFLAGS) $(CPPFLAGS) $(libbasicauth_la_CXXFLAGS) $(CXXFLAGS) -MT libbasicauth_la-basic_auth_library.lo -MD -MP -MF $(DEPDIR)/libbasicauth_la-basic_auth_library.Tpo -c -o libbasicauth_la-basic_auth_library.lo `test -f 'basic_auth_library.cc' || echo '$(srcdir)/'`basic_auth_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libbasicauth_la-basic_auth_library.Tpo $(DEPDIR)/libbasicauth_la-basic_auth_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='basic_auth_library.cc' object='libbasicauth_la-basic_auth_library.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) $(libbasicauth_la_CPPFLAGS) $(CPPFLAGS) $(libbasicauth_la_CXXFLAGS) $(CXXFLAGS) -c -o libbasicauth_la-basic_auth_library.lo `test -f 'basic_auth_library.cc' || echo '$(srcdir)/'`basic_auth_library.cc
+
+libcallout_la-callout_library.lo: callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcallout_la_CPPFLAGS) $(CPPFLAGS) $(libcallout_la_CXXFLAGS) $(CXXFLAGS) -MT libcallout_la-callout_library.lo -MD -MP -MF $(DEPDIR)/libcallout_la-callout_library.Tpo -c -o libcallout_la-callout_library.lo `test -f 'callout_library.cc' || echo '$(srcdir)/'`callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcallout_la-callout_library.Tpo $(DEPDIR)/libcallout_la-callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_library.cc' object='libcallout_la-callout_library.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) $(libcallout_la_CPPFLAGS) $(CPPFLAGS) $(libcallout_la_CXXFLAGS) $(CXXFLAGS) -c -o libcallout_la-callout_library.lo `test -f 'callout_library.cc' || echo '$(srcdir)/'`callout_library.cc
+
+ca_unittests-ca_cfg_mgr_unittests.o: ca_cfg_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_cfg_mgr_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Tpo -c -o ca_unittests-ca_cfg_mgr_unittests.o `test -f 'ca_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`ca_cfg_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Tpo $(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_cfg_mgr_unittests.cc' object='ca_unittests-ca_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_cfg_mgr_unittests.o `test -f 'ca_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`ca_cfg_mgr_unittests.cc
+
+ca_unittests-ca_cfg_mgr_unittests.obj: ca_cfg_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_cfg_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Tpo -c -o ca_unittests-ca_cfg_mgr_unittests.obj `if test -f 'ca_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'ca_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_cfg_mgr_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Tpo $(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_cfg_mgr_unittests.cc' object='ca_unittests-ca_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_cfg_mgr_unittests.obj `if test -f 'ca_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'ca_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_cfg_mgr_unittests.cc'; fi`
+
+ca_unittests-ca_command_mgr_unittests.o: ca_command_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_command_mgr_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Tpo -c -o ca_unittests-ca_command_mgr_unittests.o `test -f 'ca_command_mgr_unittests.cc' || echo '$(srcdir)/'`ca_command_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Tpo $(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_command_mgr_unittests.cc' object='ca_unittests-ca_command_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_command_mgr_unittests.o `test -f 'ca_command_mgr_unittests.cc' || echo '$(srcdir)/'`ca_command_mgr_unittests.cc
+
+ca_unittests-ca_command_mgr_unittests.obj: ca_command_mgr_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_command_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Tpo -c -o ca_unittests-ca_command_mgr_unittests.obj `if test -f 'ca_command_mgr_unittests.cc'; then $(CYGPATH_W) 'ca_command_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_command_mgr_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Tpo $(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_command_mgr_unittests.cc' object='ca_unittests-ca_command_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_command_mgr_unittests.obj `if test -f 'ca_command_mgr_unittests.cc'; then $(CYGPATH_W) 'ca_command_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_command_mgr_unittests.cc'; fi`
+
+ca_unittests-ca_controller_unittests.o: ca_controller_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_controller_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_controller_unittests.Tpo -c -o ca_unittests-ca_controller_unittests.o `test -f 'ca_controller_unittests.cc' || echo '$(srcdir)/'`ca_controller_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_controller_unittests.Tpo $(DEPDIR)/ca_unittests-ca_controller_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_controller_unittests.cc' object='ca_unittests-ca_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_controller_unittests.o `test -f 'ca_controller_unittests.cc' || echo '$(srcdir)/'`ca_controller_unittests.cc
+
+ca_unittests-ca_controller_unittests.obj: ca_controller_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_controller_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_controller_unittests.Tpo -c -o ca_unittests-ca_controller_unittests.obj `if test -f 'ca_controller_unittests.cc'; then $(CYGPATH_W) 'ca_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_controller_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_controller_unittests.Tpo $(DEPDIR)/ca_unittests-ca_controller_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_controller_unittests.cc' object='ca_unittests-ca_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_controller_unittests.obj `if test -f 'ca_controller_unittests.cc'; then $(CYGPATH_W) 'ca_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_controller_unittests.cc'; fi`
+
+ca_unittests-ca_process_unittests.o: ca_process_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_process_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_process_unittests.Tpo -c -o ca_unittests-ca_process_unittests.o `test -f 'ca_process_unittests.cc' || echo '$(srcdir)/'`ca_process_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_process_unittests.Tpo $(DEPDIR)/ca_unittests-ca_process_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_process_unittests.cc' object='ca_unittests-ca_process_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_process_unittests.o `test -f 'ca_process_unittests.cc' || echo '$(srcdir)/'`ca_process_unittests.cc
+
+ca_unittests-ca_process_unittests.obj: ca_process_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_process_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_process_unittests.Tpo -c -o ca_unittests-ca_process_unittests.obj `if test -f 'ca_process_unittests.cc'; then $(CYGPATH_W) 'ca_process_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_process_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_process_unittests.Tpo $(DEPDIR)/ca_unittests-ca_process_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_process_unittests.cc' object='ca_unittests-ca_process_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_process_unittests.obj `if test -f 'ca_process_unittests.cc'; then $(CYGPATH_W) 'ca_process_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_process_unittests.cc'; fi`
+
+ca_unittests-ca_response_creator_unittests.o: ca_response_creator_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_response_creator_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_response_creator_unittests.Tpo -c -o ca_unittests-ca_response_creator_unittests.o `test -f 'ca_response_creator_unittests.cc' || echo '$(srcdir)/'`ca_response_creator_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_response_creator_unittests.Tpo $(DEPDIR)/ca_unittests-ca_response_creator_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_response_creator_unittests.cc' object='ca_unittests-ca_response_creator_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_response_creator_unittests.o `test -f 'ca_response_creator_unittests.cc' || echo '$(srcdir)/'`ca_response_creator_unittests.cc
+
+ca_unittests-ca_response_creator_unittests.obj: ca_response_creator_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_response_creator_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_response_creator_unittests.Tpo -c -o ca_unittests-ca_response_creator_unittests.obj `if test -f 'ca_response_creator_unittests.cc'; then $(CYGPATH_W) 'ca_response_creator_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_response_creator_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_response_creator_unittests.Tpo $(DEPDIR)/ca_unittests-ca_response_creator_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_response_creator_unittests.cc' object='ca_unittests-ca_response_creator_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_response_creator_unittests.obj `if test -f 'ca_response_creator_unittests.cc'; then $(CYGPATH_W) 'ca_response_creator_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_response_creator_unittests.cc'; fi`
+
+ca_unittests-ca_response_creator_factory_unittests.o: ca_response_creator_factory_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_response_creator_factory_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Tpo -c -o ca_unittests-ca_response_creator_factory_unittests.o `test -f 'ca_response_creator_factory_unittests.cc' || echo '$(srcdir)/'`ca_response_creator_factory_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Tpo $(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_response_creator_factory_unittests.cc' object='ca_unittests-ca_response_creator_factory_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_response_creator_factory_unittests.o `test -f 'ca_response_creator_factory_unittests.cc' || echo '$(srcdir)/'`ca_response_creator_factory_unittests.cc
+
+ca_unittests-ca_response_creator_factory_unittests.obj: ca_response_creator_factory_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_response_creator_factory_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Tpo -c -o ca_unittests-ca_response_creator_factory_unittests.obj `if test -f 'ca_response_creator_factory_unittests.cc'; then $(CYGPATH_W) 'ca_response_creator_factory_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_response_creator_factory_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Tpo $(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_response_creator_factory_unittests.cc' object='ca_unittests-ca_response_creator_factory_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_response_creator_factory_unittests.obj `if test -f 'ca_response_creator_factory_unittests.cc'; then $(CYGPATH_W) 'ca_response_creator_factory_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_response_creator_factory_unittests.cc'; fi`
+
+ca_unittests-ca_unittests.o: ca_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-ca_unittests.Tpo -c -o ca_unittests-ca_unittests.o `test -f 'ca_unittests.cc' || echo '$(srcdir)/'`ca_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_unittests.Tpo $(DEPDIR)/ca_unittests-ca_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_unittests.cc' object='ca_unittests-ca_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_unittests.o `test -f 'ca_unittests.cc' || echo '$(srcdir)/'`ca_unittests.cc
+
+ca_unittests-ca_unittests.obj: ca_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-ca_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-ca_unittests.Tpo -c -o ca_unittests-ca_unittests.obj `if test -f 'ca_unittests.cc'; then $(CYGPATH_W) 'ca_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-ca_unittests.Tpo $(DEPDIR)/ca_unittests-ca_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='ca_unittests.cc' object='ca_unittests-ca_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-ca_unittests.obj `if test -f 'ca_unittests.cc'; then $(CYGPATH_W) 'ca_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/ca_unittests.cc'; fi`
+
+ca_unittests-parser_unittests.o: parser_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-parser_unittests.o -MD -MP -MF $(DEPDIR)/ca_unittests-parser_unittests.Tpo -c -o ca_unittests-parser_unittests.o `test -f 'parser_unittests.cc' || echo '$(srcdir)/'`parser_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-parser_unittests.Tpo $(DEPDIR)/ca_unittests-parser_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parser_unittests.cc' object='ca_unittests-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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-parser_unittests.o `test -f 'parser_unittests.cc' || echo '$(srcdir)/'`parser_unittests.cc
+
+ca_unittests-parser_unittests.obj: parser_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-parser_unittests.obj -MD -MP -MF $(DEPDIR)/ca_unittests-parser_unittests.Tpo -c -o ca_unittests-parser_unittests.obj `if test -f 'parser_unittests.cc'; then $(CYGPATH_W) 'parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/parser_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-parser_unittests.Tpo $(DEPDIR)/ca_unittests-parser_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parser_unittests.cc' object='ca_unittests-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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-parser_unittests.obj `if test -f 'parser_unittests.cc'; then $(CYGPATH_W) 'parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/parser_unittests.cc'; fi`
+
+ca_unittests-get_config_unittest.o: get_config_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-get_config_unittest.o -MD -MP -MF $(DEPDIR)/ca_unittests-get_config_unittest.Tpo -c -o ca_unittests-get_config_unittest.o `test -f 'get_config_unittest.cc' || echo '$(srcdir)/'`get_config_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-get_config_unittest.Tpo $(DEPDIR)/ca_unittests-get_config_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='get_config_unittest.cc' object='ca_unittests-get_config_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-get_config_unittest.o `test -f 'get_config_unittest.cc' || echo '$(srcdir)/'`get_config_unittest.cc
+
+ca_unittests-get_config_unittest.obj: get_config_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ca_unittests-get_config_unittest.obj -MD -MP -MF $(DEPDIR)/ca_unittests-get_config_unittest.Tpo -c -o ca_unittests-get_config_unittest.obj `if test -f 'get_config_unittest.cc'; then $(CYGPATH_W) 'get_config_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/get_config_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ca_unittests-get_config_unittest.Tpo $(DEPDIR)/ca_unittests-get_config_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='get_config_unittest.cc' object='ca_unittests-get_config_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) $(ca_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ca_unittests-get_config_unittest.obj `if test -f 'get_config_unittest.cc'; then $(CYGPATH_W) 'get_config_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/get_config_unittest.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_SCRIPTS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -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-libtool clean-noinstLTLIBRARIES \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_cfg_mgr_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_controller_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_process_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_response_creator_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-get_config_unittest.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-parser_unittests.Po
+ -rm -f ./$(DEPDIR)/libbasicauth_la-basic_auth_library.Plo
+ -rm -f ./$(DEPDIR)/libcallout_la-callout_library.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)/ca_unittests-ca_cfg_mgr_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_command_mgr_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_controller_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_process_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_response_creator_factory_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_response_creator_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-ca_unittests.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-get_config_unittest.Po
+ -rm -f ./$(DEPDIR)/ca_unittests-parser_unittests.Po
+ -rm -f ./$(DEPDIR)/libbasicauth_la-basic_auth_library.Plo
+ -rm -f ./$(DEPDIR)/libcallout_la-callout_library.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) 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-noinstLTLIBRARIES 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/bin/agent/tests/basic_auth_library.cc b/src/bin/agent/tests/basic_auth_library.cc
new file mode 100644
index 0000000..924d656
--- /dev/null
+++ b/src/bin/agent/tests/basic_auth_library.cc
@@ -0,0 +1,289 @@
+// Copyright (C) 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/.
+
+/// @file
+/// @brief Basic HTTP Authentication callout library
+
+#include <config.h>
+
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <hooks/hooks.h>
+#include <http/basic_auth_config.h>
+#include <http/post_request_json.h>
+#include <http/response_creator.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::http;
+using namespace isc;
+using namespace std;
+
+namespace {
+
+/// @brief Response creator.
+class ResponseCreator : public HttpResponseCreator {
+public:
+ /// @brief Create a new request.
+ /// @return Pointer to the new instance of the @ref
+ /// isc::http::PostHttpRequestJson.
+ virtual HttpRequestPtr
+ createNewHttpRequest() const;
+
+ /// @brief Create stock HTTP response.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @param status_code Status code of the response.
+ /// @return Pointer to an @ref isc::http::HttpResponseJson object
+ /// representing stock HTTP response.
+ virtual HttpResponsePtr
+ createStockHttpResponse(const HttpRequestPtr& request,
+ const HttpStatusCode& status_code) const;
+
+ /// @brief Creates implementation specific HTTP response.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @return Pointer to an object representing HTTP response.
+ virtual HttpResponsePtr
+ createDynamicHttpResponse(HttpRequestPtr request);
+};
+
+HttpRequestPtr
+ResponseCreator::createNewHttpRequest() const {
+ return (HttpRequestPtr(new PostHttpRequestJson()));
+}
+
+HttpResponsePtr
+ResponseCreator::createStockHttpResponse(const HttpRequestPtr& /*request*/,
+ const HttpStatusCode& status_code) const {
+ HttpVersion http_version(1, 1);
+ HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
+ response->finalize();
+ return (response);
+}
+
+HttpResponsePtr
+ResponseCreator::createDynamicHttpResponse(HttpRequestPtr /*request*/) {
+ isc_throw(NotImplemented, "createDynamicHttpResponse should not be called");
+}
+
+/// @brief The type of shared pointers to response creators.
+typedef boost::shared_ptr<ResponseCreator> ResponseCreatorPtr;
+
+/// @brief Implementation.
+class Impl {
+public:
+
+ /// @brief Constructor.
+ Impl();
+
+ /// @brief Destructor.
+ ~Impl();
+
+ /// @brief Configure.
+ ///
+ /// @param config element pointer to client list.
+ void configure(ConstElementPtr config);
+
+ /// @brief Basic HTTP authentication configuration.
+ BasicHttpAuthConfigPtr config_;
+
+ /// @brief Response creator.
+ ResponseCreatorPtr creator_;
+};
+
+Impl::Impl()
+ : config_(new BasicHttpAuthConfig()), creator_(new ResponseCreator()) {
+}
+
+Impl::~Impl() {
+}
+
+void
+Impl::configure(ConstElementPtr config) {
+ config_->parse(config);
+}
+
+/// @brief The type of shared pointers to implementations.
+typedef boost::shared_ptr<Impl> ImplPtr;
+
+/// @brief The implementation.
+ImplPtr impl;
+
+extern "C" {
+
+// Framework functions.
+
+/// @brief returns Kea hooks version.
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+/// @brief This function is called when the library is loaded.
+///
+/// @param handle library handle.
+/// @return 0 when initialization is successful, 1 otherwise.
+int
+load(LibraryHandle& handle) {
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ try {
+ impl.reset(new Impl());
+ ConstElementPtr config = handle.getParameter("config");
+ impl->configure(config);
+ } catch (const std::exception& ex) {
+ std::cerr << "load error: " << ex.what() << std::endl;
+ return (1);
+ }
+
+ return (0);
+}
+
+/// @brief This function is called when the library is unloaded.
+///
+/// @return always 0.
+int
+unload() {
+ impl.reset();
+ return (0);
+}
+
+// Callout functions.
+
+/// @brief This callout is called at the "auth" hook.
+///
+/// @param handle CalloutHandle.
+/// @return 0 upon success, non-zero otherwise.
+int
+auth(CalloutHandle& handle) {
+ // Sanity.
+ if (!impl) {
+ std::cerr << "no implementation" << std::endl;
+ return (0);
+ }
+
+ // Get the parameters.
+ HttpRequestPtr request;
+ HttpResponseJsonPtr response;
+ handle.getArgument("request", request);
+ handle.getArgument("response", response);
+
+ if (response) {
+ std::cerr << "response already set" << std::endl;
+ return (0);
+ }
+ if (!request) {
+ std::cerr << "no request" << std::endl;
+ return (0);
+ }
+ PostHttpRequestJsonPtr request_json =
+ boost::dynamic_pointer_cast<PostHttpRequestJson>(request);
+ if (!request_json) {
+ std::cerr << "no json post request" << std::endl;
+ return (0);
+ }
+ ConstElementPtr command = request_json->getBodyAsJson();
+ if (!command) {
+ std::cerr << "no command" << std::endl;
+ return (0);
+ }
+ if (command->getType() != Element::map) {
+ std::cerr << "command is not a map" << std::endl;
+ return (0);
+ }
+
+ // Modify request.
+ int extra = 0;
+ ConstElementPtr extra_elem = command->get("extra");
+ ElementPtr mutable_command = boost::const_pointer_cast<Element>(command);
+ if (extra_elem) {
+ if (extra_elem->getType() == Element::integer) {
+ extra = extra_elem->intValue();
+ }
+ mutable_command->remove("extra");
+ request_json->setBodyAsJson(command);
+ }
+ handle.setContext("extra", extra);
+
+ // Perform authentication.
+ response = impl->config_->checkAuth(*impl->creator_, request);
+
+ // Set parameters.
+ handle.setArgument("request", request);
+ handle.setArgument("response", response);
+ return (0);
+}
+
+/// @brief This callout is called at the "response" hook.
+///
+/// @param handle CalloutHandle.
+/// @return 0 upon success, non-zero otherwise.
+int
+response(CalloutHandle& handle) {
+ // Sanity.
+ if (!impl) {
+ std::cerr << "no implementation" << std::endl;
+ return (0);
+ }
+
+ // Get the parameters.
+ HttpRequestPtr request;
+ HttpResponseJsonPtr response;
+ handle.getArgument("request", request);
+ handle.getArgument("response", response);
+
+ if (!request) {
+ std::cerr << "no request" << std::endl;
+ return (0);
+ }
+ if (!response) {
+ std::cerr << "no response" << std::endl;
+ return (0);
+ }
+
+ // Modify response.
+ ConstElementPtr body = response->getBodyAsJson();
+ if (!body) {
+ std::cerr << "no body" << std::endl;
+ return (0);
+ }
+ if (body->getType() != Element::list) {
+ std::cerr << "body is not a list" << std::endl;
+ return (0);
+ }
+ if (body->size() < 1) {
+ std::cerr << "body is empty" << std::endl;
+ return (0);
+ }
+ ConstElementPtr answer = body->get(0);
+ if (!answer || (answer->getType() != Element::map)) {
+ std::cerr << "answer is not map" << std::endl;
+ return (0);
+ }
+ ElementPtr mutable_answer = boost::const_pointer_cast<Element>(answer);
+ try {
+ int extra = 0;
+ handle.getContext("extra", extra);
+ mutable_answer->set("got", Element::create(extra));
+ } catch (const NoSuchCalloutContext&) {
+ std::cerr << "can't find 'extra' context\n";
+ } catch (...) {
+ std::cerr << "getContext('extra') failed\n";
+ }
+ response->setBodyAsJson(body);
+
+ // Set parameters.
+ handle.setArgument("response", response);
+ return (0);
+}
+
+}
+}
diff --git a/src/bin/agent/tests/ca_cfg_mgr_unittests.cc b/src/bin/agent/tests/ca_cfg_mgr_unittests.cc
new file mode 100644
index 0000000..a7b40c5
--- /dev/null
+++ b/src/bin/agent/tests/ca_cfg_mgr_unittests.cc
@@ -0,0 +1,703 @@
+// Copyright (C) 2016-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 <agent/ca_cfg_mgr.h>
+#include <agent/parser_context.h>
+#include <exceptions/exceptions.h>
+#include <process/testutils/d_test_stubs.h>
+#include <process/d_cfg_mgr.h>
+#include <http/basic_auth_config.h>
+#include <agent/tests/test_callout_libraries.h>
+#include <agent/tests/test_data_files_config.h>
+#include <boost/pointer_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc::agent;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::http;
+using namespace isc::process;
+
+namespace {
+
+/// @brief Almost regular agent CfgMgr with internal parse method exposed.
+class NakedAgentCfgMgr : public CtrlAgentCfgMgr {
+public:
+ using CtrlAgentCfgMgr::parse;
+};
+
+// Tests construction of CtrlAgentCfgMgr class.
+TEST(CtrlAgentCfgMgr, construction) {
+ boost::scoped_ptr<CtrlAgentCfgMgr> cfg_mgr;
+
+ // Verify that configuration manager constructions without error.
+ ASSERT_NO_THROW(cfg_mgr.reset(new CtrlAgentCfgMgr()));
+
+ // Verify that the context can be retrieved and is not null.
+ CtrlAgentCfgContextPtr context;
+ ASSERT_NO_THROW(context = cfg_mgr->getCtrlAgentCfgContext());
+ EXPECT_TRUE(context);
+
+ // Verify that the manager can be destructed without error.
+ EXPECT_NO_THROW(cfg_mgr.reset());
+}
+
+// Tests if getContext can be retrieved.
+TEST(CtrlAgentCfgMgr, getContext) {
+ CtrlAgentCfgMgr cfg_mgr;
+
+ CtrlAgentCfgContextPtr ctx;
+ ASSERT_NO_THROW(ctx = cfg_mgr.getCtrlAgentCfgContext());
+ ASSERT_TRUE(ctx);
+}
+
+// Tests if context can store and retrieve HTTP parameters
+TEST(CtrlAgentCfgMgr, contextHttpParams) {
+ CtrlAgentCfgContext ctx;
+
+ // Check http parameters
+ ctx.setHttpPort(12345);
+ EXPECT_EQ(12345, ctx.getHttpPort());
+
+ ctx.setHttpHost("alnitak");
+ EXPECT_EQ("alnitak", ctx.getHttpHost());
+}
+
+// Tests if context can store and retrieve TLS parameters.
+TEST(CtrlAgentCfgMgr, contextTlsParams) {
+ CtrlAgentCfgContext ctx;
+
+ // Check TLS parameters
+ ctx.setTrustAnchor("my-ca");
+ EXPECT_EQ("my-ca", ctx.getTrustAnchor());
+
+ ctx.setCertFile("my-cert");
+ EXPECT_EQ("my-cert", ctx.getCertFile());
+
+ ctx.setKeyFile("my-key");
+ EXPECT_EQ("my-key", ctx.getKeyFile());
+
+ EXPECT_TRUE(ctx.getCertRequired());
+ ctx.setCertRequired(false);
+ EXPECT_FALSE(ctx.getCertRequired());
+}
+
+// Tests if context can store and retrieve control socket information.
+TEST(CtrlAgentCfgMgr, contextSocketInfo) {
+
+ CtrlAgentCfgContext ctx;
+
+ // Check control socket parameters
+ // By default, there are no control sockets stored.
+ EXPECT_FALSE(ctx.getControlSocketInfo("d2"));
+ EXPECT_FALSE(ctx.getControlSocketInfo("dhcp4"));
+ EXPECT_FALSE(ctx.getControlSocketInfo("dhcp6"));
+
+ ConstElementPtr socket1 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"socket1\" }");
+ ConstElementPtr socket2 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"socket2\" }");
+ ConstElementPtr socket3 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"socket3\" }");
+ // Ok, now set the control socket for D2
+ EXPECT_NO_THROW(ctx.setControlSocketInfo(socket1, "d2"));
+
+ // Now check the values returned
+ EXPECT_EQ(socket1, ctx.getControlSocketInfo("d2"));
+ EXPECT_FALSE(ctx.getControlSocketInfo("dhcp4"));
+ EXPECT_FALSE(ctx.getControlSocketInfo("dhcp6"));
+
+ // Now set the v6 socket and sanity check again
+ EXPECT_NO_THROW(ctx.setControlSocketInfo(socket2, "dhcp6"));
+
+ // Should be possible to retrieve two sockets.
+ EXPECT_EQ(socket1, ctx.getControlSocketInfo("d2"));
+ EXPECT_EQ(socket2, ctx.getControlSocketInfo("dhcp6"));
+ EXPECT_FALSE(ctx.getControlSocketInfo("dhcp4"));
+
+ // Finally, set the third control socket.
+ EXPECT_NO_THROW(ctx.setControlSocketInfo(socket3, "dhcp4"));
+ EXPECT_EQ(socket1, ctx.getControlSocketInfo("d2"));
+ EXPECT_EQ(socket2, ctx.getControlSocketInfo("dhcp6"));
+ EXPECT_EQ(socket3, ctx.getControlSocketInfo("dhcp4"));
+}
+
+// Tests if copied context retains all parameters.
+TEST(CtrlAgentCfgMgr, contextSocketInfoCopy) {
+
+ CtrlAgentCfgContext ctx;
+
+ ConstElementPtr socket1 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"socket1\" }");
+ ConstElementPtr socket2 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"socket2\" }");
+ ConstElementPtr socket3 = Element::fromJSON("{ \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"socket3\" }");
+ // Ok, now set the control sockets
+ EXPECT_NO_THROW(ctx.setControlSocketInfo(socket1, "d2"));
+ EXPECT_NO_THROW(ctx.setControlSocketInfo(socket2, "dhcp4"));
+ EXPECT_NO_THROW(ctx.setControlSocketInfo(socket3, "dhcp6"));
+
+ EXPECT_NO_THROW(ctx.setHttpPort(12345));
+ EXPECT_NO_THROW(ctx.setHttpHost("bellatrix"));
+
+ HooksConfig& libs = ctx.getHooksConfig();
+ string exp_name("testlib1.so");
+ ConstElementPtr exp_param(new StringElement("myparam"));
+ libs.add(exp_name, exp_param);
+
+ BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+ auth->setRealm("foobar");
+ auth->add("foo", "", "bar", "");
+ EXPECT_NO_THROW(ctx.setAuthConfig(auth));
+
+ // Make a copy.
+ ConfigPtr copy_base(ctx.clone());
+ CtrlAgentCfgContextPtr copy = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(copy_base);
+ ASSERT_TRUE(copy);
+
+ // Now check the values returned
+ EXPECT_EQ(12345, copy->getHttpPort());
+ EXPECT_EQ("bellatrix", copy->getHttpHost());
+
+ // Check socket info
+ ASSERT_TRUE(copy->getControlSocketInfo("d2"));
+ ASSERT_TRUE(copy->getControlSocketInfo("dhcp4"));
+ ASSERT_TRUE(copy->getControlSocketInfo("dhcp6"));
+ EXPECT_EQ(socket1->str(), copy->getControlSocketInfo("d2")->str());
+ EXPECT_EQ(socket2->str(), copy->getControlSocketInfo("dhcp4")->str());
+ EXPECT_EQ(socket3->str(), copy->getControlSocketInfo("dhcp6")->str());
+
+ // Check hook libs
+ const HookLibsCollection& libs2 = copy->getHooksConfig().get();
+ ASSERT_EQ(1, libs2.size());
+ EXPECT_EQ(exp_name, libs2[0].first);
+ ASSERT_TRUE(libs2[0].second);
+ EXPECT_EQ(exp_param->str(), libs2[0].second->str());
+
+ // Check authentication
+ const HttpAuthConfigPtr& auth2 = copy->getAuthConfig();
+ ASSERT_TRUE(auth2);
+ EXPECT_EQ(auth->toElement()->str(), auth2->toElement()->str());
+}
+
+
+// Tests if the context can store and retrieve hook libs information.
+TEST(CtrlAgentCfgMgr, contextHookParams) {
+ CtrlAgentCfgContext ctx;
+
+ // By default there should be no hooks.
+ HooksConfig& libs = ctx.getHooksConfig();
+ EXPECT_TRUE(libs.get().empty());
+
+ libs.add("libone.so", ConstElementPtr());
+ libs.add("libtwo.so", Element::fromJSON("{\"foo\": true}"));
+ libs.add("libthree.so", Element::fromJSON("{\"bar\": 42}"));
+
+ const HooksConfig& stored_libs = ctx.getHooksConfig();
+ EXPECT_EQ(3, stored_libs.get().size());
+
+ // @todo add a == operator to HooksConfig
+ EXPECT_EQ(libs.get(), stored_libs.get());
+}
+
+// Test if the context can store and retrieve basic HTTP authentication
+// configuration.
+TEST(CtrlAgentCfgMgr, contextAuthConfig) {
+ CtrlAgentCfgContext ctx;
+
+ // By default there should be no authentication.
+ EXPECT_FALSE(ctx.getAuthConfig());
+ BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+ EXPECT_NO_THROW(ctx.setAuthConfig(auth));
+
+ auth->setRealm("foobar");
+ auth->add("foo", "", "bar", "");
+ auth->add("test", "", "123\xa3", "");
+
+ const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig();
+ ASSERT_TRUE(stored_auth);
+ EXPECT_FALSE(stored_auth->empty());
+ EXPECT_EQ(auth->toElement()->str(), stored_auth->toElement()->str());
+}
+
+// Test if the context can store and retrieve basic HTTP authentication
+// configuration using files.
+TEST(CtrlAgentCfgMgr, contextAuthConfigFile) {
+ CtrlAgentCfgContext ctx;
+
+ // By default there should be no authentication.
+ EXPECT_FALSE(ctx.getAuthConfig());
+ BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+ EXPECT_NO_THROW(ctx.setAuthConfig(auth));
+
+ auth->setRealm("foobar");
+ auth->setDirectory("/tmp");
+ auth->add("", "/tmp/foo", "", "/tmp/bar");
+ auth->add("", "/tmp/test", "", "/tmp/pwd");
+
+ const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig();
+ ASSERT_TRUE(stored_auth);
+ EXPECT_FALSE(stored_auth->empty());
+ EXPECT_EQ(auth->toElement()->str(), stored_auth->toElement()->str());
+}
+
+/// Control Agent configurations used in tests.
+const char* AGENT_CONFIGS[] = {
+
+ // configuration 0: empty (nothing specified)
+ "{ }",
+
+ // Configuration 1: http parameters only (no control sockets, not hooks)
+ "{ \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001\n"
+ "}",
+
+ // Configuration 2: http and 1 socket
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " }\n"
+ " }\n"
+ "}",
+
+ // Configuration 3: http and all 3 sockets
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " },\n"
+ " \"dhcp6\": {\n"
+ " \"socket-name\": \"/tmp/socket-v6\"\n"
+ " },\n"
+ " \"d2\": {\n"
+ " \"socket-name\": \"/tmp/socket-d2\"\n"
+ " }\n"
+ " }\n"
+ "}",
+
+ // Configuration 4: http, 1 socket and hooks
+ // CA is able to load hook libraries that augment its operation.
+ // The primary functionality is the ability to add new commands.
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " }\n"
+ " },\n"
+ " \"hooks-libraries\": ["
+ " {"
+ " \"library\": \"%LIBRARY%\","
+ " \"parameters\": {\n"
+ " \"param1\": \"foo\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ "}",
+
+ // Configuration 5: http and 1 socket (d2 only)
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"control-sockets\": {\n"
+ " \"d2\": {\n"
+ " \"socket-name\": \"/tmp/socket-d2\"\n"
+ " }\n"
+ " }\n"
+ "}",
+
+ // Configuration 6: http and 1 socket (dhcp6 only)
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp6\": {\n"
+ " \"socket-name\": \"/tmp/socket-v6\"\n"
+ " }\n"
+ " }\n"
+ "}",
+
+ // Configuration 7: http, 1 socket and authentication
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"authentication\": {\n"
+ " \"type\": \"basic\",\n"
+ " \"realm\": \"foobar\",\n"
+ " \"clients\": ["
+ " {"
+ " \"user\": \"foo\",\n"
+ " \"password\": \"bar\"\n"
+ " },{\n"
+ " \"user\": \"test\",\n"
+ " \"password\": \"123\\u00a3\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " }\n"
+ " }\n"
+ "}",
+
+ // Configuration 8: http and 2 sockets with user contexts and comments
+ "{\n"
+ " \"user-context\": { \"comment\": \"Indirect comment\" },\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"authentication\": {\n"
+ " \"comment\": \"basic HTTP authentication\",\n"
+ " \"type\": \"basic\",\n"
+ " \"realm\": \"foobar\",\n"
+ " \"clients\": ["
+ " {"
+ " \"comment\": \"foo is authorized\",\n"
+ " \"user\": \"foo\",\n"
+ " \"password\": \"bar\"\n"
+ " },{\n"
+ " \"user\": \"test\",\n"
+ " \"user-context\": { \"no password\": true }\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"comment\": \"dhcp4 socket\",\n"
+ " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " },\n"
+ " \"dhcp6\": {\n"
+ " \"socket-name\": \"/tmp/socket-v6\",\n"
+ " \"user-context\": { \"version\": 1 }\n"
+ " }\n"
+ " }\n"
+ "}",
+
+ // Configuration 9: https aka http over TLS
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"trust-anchor\": \"my-ca\",\n"
+ " \"cert-file\": \"my-cert\",\n"
+ " \"key-file\": \"my-key\",\n"
+ " \"cert-required\": false\n"
+ "}",
+
+ // Configuration 10: http, 1 socket and authentication using files
+ "{\n"
+ " \"http-host\": \"betelgeuse\",\n"
+ " \"http-port\": 8001,\n"
+ " \"authentication\": {\n"
+ " \"type\": \"basic\",\n"
+ " \"realm\": \"foobar\",\n"
+ " \"directory\": \"" CA_TEST_DATA_DIR "\",\n"
+ " \"clients\": ["
+ " {"
+ " \"user-file\": \"hiddenu\",\n"
+ " \"password-file\": \"hiddenp\"\n"
+ " },{\n"
+ " \"password-file\": \"hiddens\"\n"
+ " }\n"
+ " ]\n"
+ " },\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"socket-name\": \"/tmp/socket-v4\"\n"
+ " }\n"
+ " }\n"
+ "}"
+};
+
+/// @brief Class used for testing CfgMgr
+class AgentParserTest : public isc::process::ConfigParseTest {
+public:
+
+ /// @brief Tries to load input text as a configuration
+ ///
+ /// @param config text containing input configuration
+ /// @param expected_answer expected result of configuration (0 = success)
+ void configParse(const char* config, int expected_answer) {
+ isc::agent::ParserContext parser;
+ ConstElementPtr json = parser.parseString(config, ParserContext::PARSER_SUB_AGENT);
+
+ EXPECT_NO_THROW(answer_ = cfg_mgr_.parse(json, false));
+ EXPECT_TRUE(checkAnswer(expected_answer));
+ }
+
+ /// @brief Replaces %LIBRARY% with specified library name
+ ///
+ /// @param config input config text (should contain "%LIBRARY%" string)
+ /// @param lib_name %LIBRARY% will be replaced with that name
+ /// @return configuration text with library name replaced
+ std::string pathReplacer(const char* config, const char* lib_name) {
+ string txt(config);
+ txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name));
+ return (txt);
+ }
+
+ /// Configuration Manager (used in tests)
+ NakedAgentCfgMgr cfg_mgr_;
+};
+
+// This test verifies if an empty config is handled properly. In practice such
+// a config makes little sense, but perhaps it's ok for a default deployment.
+// Sadly, our bison parser requires at last one parameter to be present.
+// Until we determine whether we want the empty config to be allowed or not,
+// this test remains disabled.
+TEST_F(AgentParserTest, DISABLED_configParseEmpty) {
+ configParse(AGENT_CONFIGS[0], 0);
+}
+
+// This test checks if a config with only HTTP parameters is parsed properly.
+TEST_F(AgentParserTest, configParseHttpOnly) {
+ configParse(AGENT_CONFIGS[1], 0);
+
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ("betelgeuse", ctx->getHttpHost());
+ EXPECT_EQ(8001, ctx->getHttpPort());
+}
+
+// Tests if a single socket can be configured. BTW this test also checks
+// if default value for socket-type is specified (the config doesn't have it,
+// so the default value should be filed in).
+TEST_F(AgentParserTest, configParseSocketDhcp4) {
+ configParse(AGENT_CONFIGS[2], 0);
+
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ ConstElementPtr socket = ctx->getControlSocketInfo("dhcp4");
+ ASSERT_TRUE(socket);
+ EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
+ socket->str());
+ EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6"));
+ EXPECT_FALSE(ctx->getControlSocketInfo("d2"));
+}
+
+// Tests if a single socket can be configured. BTW this test also checks
+// if default value for socket-type is specified (the config doesn't have it,
+// so the default value should be filed in).
+TEST_F(AgentParserTest, configParseSocketD2) {
+ configParse(AGENT_CONFIGS[5], 0);
+
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ ConstElementPtr socket = ctx->getControlSocketInfo("d2");
+ ASSERT_TRUE(socket);
+ EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }",
+ socket->str());
+
+ EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4"));
+ EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6"));
+}
+
+// Tests if a single socket can be configured. BTW this test also checks
+// if default value for socket-type is specified (the config doesn't have it,
+// so the default value should be filed in).
+TEST_F(AgentParserTest, configParseSocketDhcp6) {
+ configParse(AGENT_CONFIGS[6], 0);
+
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ ConstElementPtr socket = ctx->getControlSocketInfo("dhcp6");
+ ASSERT_TRUE(socket);
+ EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }",
+ socket->str());
+ EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4"));
+ EXPECT_FALSE(ctx->getControlSocketInfo("d2"));
+}
+
+// This tests if all 3 sockets can be configured and makes sure the parser
+// doesn't confuse them.
+TEST_F(AgentParserTest, configParse3Sockets) {
+ configParse(AGENT_CONFIGS[3], 0);
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ ConstElementPtr socket2 = ctx->getControlSocketInfo("d2");
+ ConstElementPtr socket4 = ctx->getControlSocketInfo("dhcp4");
+ ConstElementPtr socket6 = ctx->getControlSocketInfo("dhcp6");
+ ASSERT_TRUE(socket2);
+ EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }",
+ socket2->str());
+ ASSERT_TRUE(socket4);
+ EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }",
+ socket4->str());
+ ASSERT_TRUE(socket6);
+ EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }",
+ socket6->str());
+}
+
+// This test checks that the config file with hook library specified can be
+// loaded. This one is a bit tricky, because the parser sanity checks the lib
+// name. In particular, it checks if such a library exists. Therefore we
+// can't use AGENT_CONFIGS[4] as is, but need to run it through path replacer.
+TEST_F(AgentParserTest, configParseHooks) {
+ // Create the configuration with proper lib path.
+ std::string cfg = pathReplacer(AGENT_CONFIGS[4], CALLOUT_LIBRARY);
+ // The configuration should be successful.
+ configParse(cfg.c_str(), 0);
+
+ // The context now should have the library specified.
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ const HookLibsCollection libs = ctx->getHooksConfig().get();
+ ASSERT_EQ(1, libs.size());
+ EXPECT_EQ(string(CALLOUT_LIBRARY), libs[0].first);
+ ASSERT_TRUE(libs[0].second);
+ EXPECT_EQ("{ \"param1\": \"foo\" }", libs[0].second->str());
+}
+
+// This test checks that the config file with basic HTTP authentication can be
+// loaded.
+TEST_F(AgentParserTest, configParseAuth) {
+ configParse(AGENT_CONFIGS[7], 0);
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
+ ASSERT_TRUE(auth);
+ const BasicHttpAuthConfigPtr& basic_auth =
+ boost::dynamic_pointer_cast<BasicHttpAuthConfig>(auth);
+ ASSERT_TRUE(basic_auth);
+
+ // Check realm
+ EXPECT_EQ("foobar", basic_auth->getRealm());
+
+ // Check credentials
+ auto credentials = basic_auth->getCredentialMap();
+ EXPECT_EQ(2, credentials.size());
+ std::string user;
+ EXPECT_NO_THROW(user = credentials.at("Zm9vOmJhcg=="));
+ EXPECT_EQ("foo", user);
+ EXPECT_NO_THROW(user = credentials.at("dGVzdDoxMjPCow=="));
+ EXPECT_EQ("test", user);
+
+ // Check clients.
+ BasicHttpAuthConfig expected;
+ expected.setRealm("foobar");
+ expected.add("foo", "", "bar", "");
+ expected.add("test", "", "123\xa3", "");
+ EXPECT_EQ(expected.toElement()->str(), basic_auth->toElement()->str());
+}
+
+// This test checks comments.
+TEST_F(AgentParserTest, comments) {
+ configParse(AGENT_CONFIGS[8], 0);
+ CtrlAgentCfgContextPtr agent_ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(agent_ctx);
+
+ // Check global user context.
+ ConstElementPtr ctx = agent_ctx->getContext();
+ ASSERT_TRUE(ctx);
+ ASSERT_EQ(1, ctx->size());
+ ASSERT_TRUE(ctx->get("comment"));
+ EXPECT_EQ("\"Indirect comment\"", ctx->get("comment")->str());
+
+ // There is a DHCP4 control socket.
+ ConstElementPtr socket4 = agent_ctx->getControlSocketInfo("dhcp4");
+ ASSERT_TRUE(socket4);
+
+ // Check DHCP4 control socket user context.
+ ConstElementPtr ctx4 = socket4->get("user-context");
+ ASSERT_TRUE(ctx4);
+ ASSERT_EQ(1, ctx4->size());
+ ASSERT_TRUE(ctx4->get("comment"));
+ EXPECT_EQ("\"dhcp4 socket\"", ctx4->get("comment")->str());
+
+ // There is a DHCP6 control socket.
+ ConstElementPtr socket6 = agent_ctx->getControlSocketInfo("dhcp6");
+ ASSERT_TRUE(socket6);
+
+ // Check DHCP6 control socket user context.
+ ConstElementPtr ctx6 = socket6->get("user-context");
+ ASSERT_TRUE(ctx6);
+ ASSERT_EQ(1, ctx6->size());
+ ASSERT_TRUE(ctx6->get("version"));
+ EXPECT_EQ("1", ctx6->get("version")->str());
+
+ // Check authentication comment.
+ const HttpAuthConfigPtr& auth = agent_ctx->getAuthConfig();
+ ASSERT_TRUE(auth);
+ ConstElementPtr ctx7 = auth->getContext();
+ ASSERT_TRUE(ctx7);
+ ASSERT_EQ(1, ctx7->size());
+ ASSERT_TRUE(ctx7->get("comment"));
+ EXPECT_EQ("\"basic HTTP authentication\"", ctx7->get("comment")->str());
+
+ // Check basic HTTP authentication client comment.
+ const BasicHttpAuthConfigPtr& basic_auth =
+ boost::dynamic_pointer_cast<BasicHttpAuthConfig>(auth);
+ ASSERT_TRUE(basic_auth);
+ auto clients = basic_auth->getClientList();
+ ASSERT_EQ(2, clients.size());
+ ConstElementPtr ctx8 = clients.front().getContext();
+ ASSERT_TRUE(ctx8);
+ ASSERT_EQ(1, ctx8->size());
+ ASSERT_TRUE(ctx8->get("comment"));
+ EXPECT_EQ("\"foo is authorized\"", ctx8->get("comment")->str());
+
+ // Check basic HTTP authentication client user context.
+ ConstElementPtr ctx9 = clients.back().getContext();
+ ASSERT_TRUE(ctx9);
+ ASSERT_EQ(1, ctx9->size());
+ ASSERT_TRUE(ctx9->get("no password"));
+ EXPECT_EQ("true", ctx9->get("no password")->str());
+}
+
+// This test checks if a config with TLS parameters is parsed properly.
+TEST_F(AgentParserTest, configParseTls) {
+ configParse(AGENT_CONFIGS[9], 0);
+
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ("my-ca", ctx->getTrustAnchor());
+ EXPECT_EQ("my-cert", ctx->getCertFile());
+ EXPECT_EQ("my-key", ctx->getKeyFile());
+ EXPECT_FALSE(ctx->getCertRequired());
+}
+
+// This test checks that the config file with basic HTTP authentication
+// using files can be loaded.
+TEST_F(AgentParserTest, configParseAuthFile) {
+ configParse(AGENT_CONFIGS[10], 0);
+ CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext();
+ const HttpAuthConfigPtr& auth = ctx->getAuthConfig();
+ ASSERT_TRUE(auth);
+ const BasicHttpAuthConfigPtr& basic_auth =
+ boost::dynamic_pointer_cast<BasicHttpAuthConfig>(auth);
+ ASSERT_TRUE(basic_auth);
+
+ // Check realm
+ EXPECT_EQ("foobar", basic_auth->getRealm());
+
+ // Check directory
+ EXPECT_EQ(std::string(CA_TEST_DATA_DIR), basic_auth->getDirectory());
+
+ // Check credentials
+ auto credentials = basic_auth->getCredentialMap();
+ EXPECT_EQ(2, credentials.size());
+ std::string user;
+ EXPECT_NO_THROW(user = credentials.at("a2VhdGVzdDpLZWFUZXN0"));
+ EXPECT_EQ("keatest", user);
+ EXPECT_NO_THROW(user = credentials.at("a2VhOnRlc3Q="));
+ EXPECT_EQ("kea", user);
+
+ // Check clients.
+ BasicHttpAuthConfig expected;
+ expected.setRealm("foobar");
+ expected.setDirectory(std::string(CA_TEST_DATA_DIR));
+ expected.add("", "hiddenu", "", "hiddenp");
+ expected.add("", "", "", "hiddens", true);
+ EXPECT_EQ(expected.toElement()->str(), basic_auth->toElement()->str());
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/agent/tests/ca_command_mgr_unittests.cc b/src/bin/agent/tests/ca_command_mgr_unittests.cc
new file mode 100644
index 0000000..6353916
--- /dev/null
+++ b/src/bin/agent/tests/ca_command_mgr_unittests.cc
@@ -0,0 +1,425 @@
+// Copyright (C) 2017-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 <agent/ca_cfg_mgr.h>
+#include <agent/ca_command_mgr.h>
+#include <agent/ca_controller.h>
+#include <agent/ca_process.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+#include <asiolink/testutils/test_server_unix_socket.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <process/testutils/d_test_stubs.h>
+#include <boost/pointer_cast.hpp>
+#include <gtest/gtest.h>
+#include <testutils/sandbox.h>
+#include <cstdlib>
+#include <functional>
+#include <vector>
+#include <thread>
+
+using namespace isc::agent;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::process;
+
+namespace {
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Test fixture class for @ref CtrlAgentCommandMgr.
+///
+/// @todo Add tests for various commands, including the cases when the
+/// commands are forwarded to other servers via unix sockets.
+/// Meanwhile, this is just a placeholder for the tests.
+class CtrlAgentCommandMgrTest : public DControllerTest {
+public:
+ isc::test::Sandbox sandbox;
+
+ /// @brief Constructor.
+ ///
+ /// Deregisters all commands except 'list-commands'.
+ CtrlAgentCommandMgrTest()
+ : DControllerTest(CtrlAgentController::instance),
+ mgr_(CtrlAgentCommandMgr::instance()) {
+ mgr_.deregisterAll();
+ removeUnixSocketFile();
+ initProcess();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Deregisters all commands except 'list-commands'.
+ virtual ~CtrlAgentCommandMgrTest() {
+ mgr_.deregisterAll();
+ removeUnixSocketFile();
+ }
+
+ /// @brief Verifies received answer
+ ///
+ /// @todo Add better checks for failure cases and for
+ /// verification of the response parameters.
+ ///
+ /// @param answer answer to be verified
+ /// @param expected_code0 code expected to be returned in first result within
+ /// the answer.
+ /// @param expected_code1 code expected to be returned in second result within
+ /// the answer.
+ /// @param expected_code2 code expected to be returned in third result within
+ /// the answer.
+ void checkAnswer(const ConstElementPtr& answer, const int expected_code0 = 0,
+ const int expected_code1 = -1, const int expected_code2 = -1) {
+ std::vector<int> expected_codes;
+ if (expected_code0 >= 0) {
+ expected_codes.push_back(expected_code0);
+ }
+
+ if (expected_code1 >= 0) {
+ expected_codes.push_back(expected_code1);
+ }
+
+ if (expected_code2 >= 0) {
+ expected_codes.push_back(expected_code2);
+ }
+
+ int status_code;
+ // There may be multiple answers returned within a list.
+ std::vector<ElementPtr> answer_list = answer->listValue();
+
+ ASSERT_EQ(expected_codes.size(), answer_list.size());
+ // Check all answers.
+ for (auto ans = answer_list.cbegin(); ans != answer_list.cend();
+ ++ans) {
+ ConstElementPtr text;
+ ASSERT_NO_THROW(text = isc::config::parseAnswer(status_code, *ans));
+ EXPECT_EQ(expected_codes[std::distance(answer_list.cbegin(), ans)],
+ status_code)
+ << "answer contains text: " << text->stringValue();
+ }
+ }
+
+ /// @brief Returns socket file path.
+ ///
+ /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
+ /// socket file is created in the location pointed to by this variable.
+ /// Otherwise, it is created in the build directory.
+ std::string unixSocketFilePath() {
+ std::string socket_path;
+ const char* env = getenv("KEA_SOCKET_TEST_DIR");
+ if (env) {
+ socket_path = std::string(env) + "/test-socket";
+ } else {
+ socket_path = sandbox.join("test-socket");
+ }
+ return (socket_path);
+ }
+
+ /// @brief Removes unix socket descriptor.
+ void removeUnixSocketFile() {
+ static_cast<void>(remove(unixSocketFilePath().c_str()));
+ }
+
+ /// @brief Returns pointer to CtrlAgentProcess instance.
+ CtrlAgentProcessPtr getCtrlAgentProcess() {
+ return (boost::dynamic_pointer_cast<CtrlAgentProcess>(getProcess()));
+ }
+
+ /// @brief Returns pointer to CtrlAgentCfgMgr instance for a process.
+ CtrlAgentCfgMgrPtr getCtrlAgentCfgMgr() {
+ CtrlAgentCfgMgrPtr p;
+ if (getCtrlAgentProcess()) {
+ p = getCtrlAgentProcess()->getCtrlAgentCfgMgr();
+ }
+ return (p);
+ }
+
+ /// @brief Returns a pointer to the configuration context.
+ CtrlAgentCfgContextPtr getCtrlAgentCfgContext() {
+ CtrlAgentCfgContextPtr p;
+ if (getCtrlAgentCfgMgr()) {
+ p = getCtrlAgentCfgMgr()->getCtrlAgentCfgContext();
+ }
+ return (p);
+ }
+
+ /// @brief Adds configuration of the control socket.
+ ///
+ /// @param service Service for which socket configuration is to be added.
+ void
+ configureControlSocket(const std::string& service) {
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+
+ ElementPtr control_socket = Element::createMap();
+ control_socket->set("socket-name",
+ Element::create(unixSocketFilePath()));
+ ctx->setControlSocketInfo(control_socket, service);
+ }
+
+ /// @brief Create and bind server side socket.
+ ///
+ /// @param response Stub response to be sent from the server socket to the
+ /// client.
+ /// @param use_thread Indicates if the IO service will be ran in thread.
+ void bindServerSocket(const std::string& response,
+ const bool use_thread = false) {
+ server_socket_.reset(new test::TestServerUnixSocket(*getIOService(),
+ unixSocketFilePath(),
+ response));
+ server_socket_->startTimer(TEST_TIMEOUT);
+ server_socket_->bindServerSocket(use_thread);
+ }
+
+ /// @brief Creates command with no arguments.
+ ///
+ /// @param command_name Command name.
+ /// @param service Service value to be added to the command. This value is
+ /// specified as a list of comma separated values, e.g. "dhcp4, dhcp6".
+ ///
+ /// @return Pointer to the instance of the created command.
+ ConstElementPtr createCommand(const std::string& command_name,
+ const std::string& service) {
+ ElementPtr command = Element::createMap();
+ command->set("command", Element::create(command_name));
+
+ // Only add the 'service' parameter if non-empty.
+ if (!service.empty()) {
+ std::string s = boost::replace_all_copy(service, ",", "\",\"");
+ s = std::string("[ \"") + s + std::string("\" ]");
+ command->set("service", Element::fromJSON(s));
+ }
+
+ command->set("arguments", Element::createMap());
+
+ return (command);
+ }
+
+ /// @brief Test forwarding the command.
+ ///
+ /// @param server_type Server for which the client socket should be
+ /// configured.
+ /// @param service Service to be included in the command.
+ /// @param expected_result0 Expected first result in response from the server.
+ /// @param expected_result1 Expected second result in response from the server.
+ /// @param expected_result2 Expected third result in response from the server.
+ /// server socket after which the IO service should be stopped.
+ /// @param expected_responses Number of responses after which the test finishes.
+ /// @param server_response Stub response to be sent by the server.
+ void testForward(const std::string& configured_service,
+ const std::string& service,
+ const int expected_result0,
+ const int expected_result1 = -1,
+ const int expected_result2 = -1,
+ const size_t expected_responses = 1,
+ const std::string& server_response = "{ \"result\": 0 }") {
+ // Configure client side socket.
+ configureControlSocket(configured_service);
+ // Create server side socket.
+ bindServerSocket(server_response, true);
+
+ // The client side communication is synchronous. To be able to respond
+ // to this we need to run the server side socket at the same time as the
+ // client. Running IO service in a thread guarantees that the server
+ //responds as soon as it receives the control command.
+ std::thread th(std::bind(&IOService::run, getIOService().get()));
+
+
+ // Wait for the IO service in thread to actually run.
+ server_socket_->waitForRunning();
+
+ ConstElementPtr command = createCommand("foo", service);
+ ConstElementPtr answer = mgr_.processCommand(command);
+
+ // Stop IO service immediately and let the thread die.
+ getIOService()->stop();
+
+ // Wait for the thread to finish.
+ th.join();
+
+ // Cancel all asynchronous operations on the server.
+ server_socket_->stopServer();
+
+ // We have some cancelled operations for which we need to invoke the
+ // handlers with the operation_aborted error code.
+ getIOService()->get_io_service().reset();
+ getIOService()->poll();
+
+ EXPECT_EQ(expected_responses, server_socket_->getResponseNum());
+ checkAnswer(answer, expected_result0, expected_result1, expected_result2);
+ }
+
+ /// @brief a convenience reference to control agent command manager
+ CtrlAgentCommandMgr& mgr_;
+
+ /// @brief Pointer to the test server unix socket.
+ test::TestServerUnixSocketPtr server_socket_;
+};
+
+/// Just a basic test checking that non-existent command is handled
+/// properly.
+TEST_F(CtrlAgentCommandMgrTest, bogus) {
+ ConstElementPtr answer;
+ EXPECT_NO_THROW(answer = mgr_.processCommand(createCommand("fish-and-chips-please", "")));
+ checkAnswer(answer, isc::config::CONTROL_RESULT_COMMAND_UNSUPPORTED);
+};
+
+// Test verifying that parameter other than command, arguments and service is
+// rejected and that the correct error is returned.
+TEST_F(CtrlAgentCommandMgrTest, extraParameter) {
+ ElementPtr command = Element::createMap();
+ command->set("command", Element::create("list-commands"));
+ command->set("arguments", Element::createMap());
+ command->set("extra-arg", Element::createMap());
+
+ ConstElementPtr answer;
+ EXPECT_NO_THROW(answer = mgr_.processCommand(command));
+ checkAnswer(answer, isc::config::CONTROL_RESULT_ERROR);
+}
+
+/// Just a basic test checking that 'list-commands' is supported.
+TEST_F(CtrlAgentCommandMgrTest, listCommands) {
+ ConstElementPtr answer;
+ EXPECT_NO_THROW(answer = mgr_.processCommand(createCommand("list-commands", "")));
+
+ checkAnswer(answer, isc::config::CONTROL_RESULT_SUCCESS);
+};
+
+/// Check that control command is successfully forwarded to the DHCPv4 server.
+TEST_F(CtrlAgentCommandMgrTest, forwardToDHCPv4Server) {
+ testForward("dhcp4", "dhcp4", isc::config::CONTROL_RESULT_SUCCESS);
+}
+
+/// Check that control command is successfully forwarded to the DHCPv6 server.
+TEST_F(CtrlAgentCommandMgrTest, forwardToDHCPv6Server) {
+ testForward("dhcp6", "dhcp6", isc::config::CONTROL_RESULT_SUCCESS);
+}
+
+/// Check that control command is successfully forwarded to the D2 server.
+TEST_F(CtrlAgentCommandMgrTest, forwardToD2Server) {
+ testForward("d2", "d2", isc::config::CONTROL_RESULT_SUCCESS);
+}
+
+/// Check that the same command is forwarded to multiple servers.
+TEST_F(CtrlAgentCommandMgrTest, forwardToBothDHCPServers) {
+ configureControlSocket("dhcp6");
+
+ testForward("dhcp4", "dhcp4,dhcp6", isc::config::CONTROL_RESULT_SUCCESS,
+ isc::config::CONTROL_RESULT_SUCCESS, -1, 2);
+}
+
+/// Check that the same command is forwarded to all servers.
+TEST_F(CtrlAgentCommandMgrTest, forwardToAllServers) {
+ configureControlSocket("dhcp6");
+ configureControlSocket("d2");
+
+ testForward("dhcp4", "dhcp4,dhcp6,d2", isc::config::CONTROL_RESULT_SUCCESS,
+ isc::config::CONTROL_RESULT_SUCCESS,
+ isc::config::CONTROL_RESULT_SUCCESS, 3);
+}
+
+/// Check that the command may forwarded to the second server even if
+/// forwarding to a first server fails.
+TEST_F(CtrlAgentCommandMgrTest, failForwardToServer) {
+ testForward("dhcp6", "dhcp4,dhcp6",
+ isc::config::CONTROL_RESULT_ERROR,
+ isc::config::CONTROL_RESULT_SUCCESS);
+}
+
+/// Check that control command is not forwarded if the service is not specified.
+TEST_F(CtrlAgentCommandMgrTest, noService) {
+ testForward("dhcp6", "",
+ isc::config::CONTROL_RESULT_COMMAND_UNSUPPORTED,
+ -1, -1, 0);
+}
+
+/// Check that error is returned to the client when the server to which the
+/// command was forwarded sent an invalid message.
+TEST_F(CtrlAgentCommandMgrTest, invalidAnswer) {
+ testForward("dhcp6", "dhcp6",
+ isc::config::CONTROL_RESULT_ERROR, -1, -1, 1,
+ "{ \"result\": }");
+}
+
+/// Check that connection is dropped if it takes too long. The test checks
+/// client's behavior when partial JSON is returned. Client will be waiting
+/// for the '}' and will timeout because it is never received.
+/// @todo Currently this test is disabled because we don't have configurable
+/// timeout value. It is hardcoded to 5 sec, which is too long for the
+/// unit test to run.
+TEST_F(CtrlAgentCommandMgrTest, DISABLED_connectionTimeout) {
+ testForward("dhcp6", "dhcp6",
+ isc::config::CONTROL_RESULT_ERROR, -1, -1, 1,
+ "{ \"result\": 0");
+}
+
+/// Check that error is returned to the client if the forwarding socket is
+/// not configured for the given service.
+TEST_F(CtrlAgentCommandMgrTest, noClientSocket) {
+ ConstElementPtr command = createCommand("foo", "dhcp4");
+ ConstElementPtr answer = mgr_.handleCommand("foo", ConstElementPtr(),
+ command);
+
+ checkAnswer(answer, isc::config::CONTROL_RESULT_ERROR);
+}
+
+/// Check that error is returned to the client if the remote server to
+/// which the control command is to be forwarded is not available.
+TEST_F(CtrlAgentCommandMgrTest, noServerSocket) {
+ configureControlSocket("dhcp6");
+
+ ConstElementPtr command = createCommand("foo", "dhcp6");
+ ConstElementPtr answer = mgr_.handleCommand("foo", ConstElementPtr(),
+ command);
+
+ checkAnswer(answer, isc::config::CONTROL_RESULT_ERROR);
+}
+
+// Check that list-commands command is forwarded when the service
+// value is specified.
+TEST_F(CtrlAgentCommandMgrTest, forwardListCommands) {
+ // Configure client side socket.
+ configureControlSocket("dhcp4");
+ // Create server side socket.
+ bindServerSocket("{ \"result\" : 3 }", true);
+
+ // The client side communication is synchronous. To be able to respond
+ // to this we need to run the server side socket at the same time.
+ // Running IO service in a thread guarantees that the server responds
+ // as soon as it receives the control command.
+ std::thread th(std::bind(&IOService::run, getIOService().get()));
+
+ // Wait for the IO service in thread to actually run.
+ server_socket_->waitForRunning();
+
+ ConstElementPtr command = createCommand("list-commands", "dhcp4");
+ ConstElementPtr answer = mgr_.handleCommand("list-commands", ConstElementPtr(),
+ command);
+
+ // Stop IO service immediately and let the thread die.
+ getIOService()->stop();
+
+ // Wait for the thread to finish.
+ th.join();
+
+ // Cancel all asynchronous operations on the server.
+ server_socket_->stopServer();
+
+ // We have some cancelled operations for which we need to invoke the
+ // handlers with the operation_aborted error code.
+ getIOService()->get_io_service().reset();
+ getIOService()->poll();
+
+ // Answer of 3 is specific to the stub response we send when the
+ // command is forwarded. So having this value returned means that
+ // the command was forwarded as expected.
+ checkAnswer(answer, 3);
+}
+
+}
diff --git a/src/bin/agent/tests/ca_controller_unittests.cc b/src/bin/agent/tests/ca_controller_unittests.cc
new file mode 100644
index 0000000..f0f8848
--- /dev/null
+++ b/src/bin/agent/tests/ca_controller_unittests.cc
@@ -0,0 +1,836 @@
+// 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/.
+
+#include <config.h>
+
+#include <asiolink/testutils/timed_signal.h>
+#include <agent/ca_controller.h>
+#include <agent/ca_process.h>
+#include <agent/ca_command_mgr.h>
+#include <cc/data.h>
+#include <cc/command_interpreter.h>
+#include <process/testutils/d_test_stubs.h>
+#include <boost/pointer_cast.hpp>
+#include <sstream>
+#include <unistd.h>
+
+using namespace isc::asiolink::test;
+using namespace isc::agent;
+using namespace isc::data;
+using namespace isc::http;
+using namespace isc::process;
+using namespace boost::posix_time;
+using namespace std;
+
+namespace {
+
+/// @brief Valid Control Agent Config used in tests.
+const char* valid_agent_config =
+ "{"
+ " \"http-host\": \"127.0.0.1\","
+ " \"http-port\": 8081,"
+ " \"control-sockets\": {"
+ " \"dhcp4\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/first/dhcp4/socket\""
+ " },"
+ " \"dhcp6\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/first/dhcp6/socket\""
+ " }"
+ " }"
+ "}";
+
+/// @brief test fixture class for testing CtrlAgentController class.
+///
+/// This class derives from DControllerTest and wraps CtrlAgentController. Much
+/// of the underlying functionality is in the DControllerBase class which
+/// has extensive set of unit tests that are independent from the Control
+/// Agent.
+class CtrlAgentControllerTest : public DControllerTest {
+public:
+
+ /// @brief Constructor.
+ CtrlAgentControllerTest()
+ : DControllerTest(CtrlAgentController::instance) {
+ }
+
+ /// @brief Returns pointer to CtrlAgentProcess instance.
+ CtrlAgentProcessPtr getCtrlAgentProcess() {
+ return (boost::dynamic_pointer_cast<CtrlAgentProcess>(getProcess()));
+ }
+
+ /// @brief Returns pointer to CtrlAgentCfgMgr instance for a process.
+ CtrlAgentCfgMgrPtr getCtrlAgentCfgMgr() {
+ CtrlAgentCfgMgrPtr p;
+ if (getCtrlAgentProcess()) {
+ p = getCtrlAgentProcess()->getCtrlAgentCfgMgr();
+ }
+ return (p);
+ }
+
+ /// @brief Returns a pointer to the configuration context.
+ CtrlAgentCfgContextPtr getCtrlAgentCfgContext() {
+ CtrlAgentCfgContextPtr p;
+ if (getCtrlAgentCfgMgr()) {
+ p = getCtrlAgentCfgMgr()->getCtrlAgentCfgContext();
+ }
+ return (p);
+ }
+
+ /// @brief Tests that socket info structure contains 'unix' socket-type
+ /// value and the expected socket-name.
+ ///
+ /// @param service Service type.
+ /// @param exp_socket_name Expected socket name.
+ void testUnixSocketInfo(const std::string& service,
+ const std::string& exp_socket_name) {
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+
+ ConstElementPtr sock_info = ctx->getControlSocketInfo(service);
+ ASSERT_TRUE(sock_info);
+ ASSERT_TRUE(sock_info->contains("socket-type"));
+ EXPECT_EQ("unix", sock_info->get("socket-type")->stringValue());
+ ASSERT_TRUE(sock_info->contains("socket-name"));
+ EXPECT_EQ(exp_socket_name,
+ sock_info->get("socket-name")->stringValue());
+ }
+
+ /// @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 exp_status is an integer against which to compare the status.
+ /// @param exp_txt is expected text (not checked if "")
+ ///
+ void checkAnswer(isc::data::ConstElementPtr answer,
+ int exp_status,
+ string exp_txt = "") {
+ int rcode = 0;
+ isc::data::ConstElementPtr comment;
+ comment = isc::config::parseAnswer(rcode, answer);
+
+ if (rcode != exp_status) {
+ ADD_FAILURE() << "Expected status code " << exp_status
+ << " but received " << rcode << ", comment: "
+ << (comment ? comment->str() : "(none)");
+ }
+
+ // Ok, parseAnswer interface is weird. If there are no arguments,
+ // it returns content of text. But if there is an argument,
+ // it returns the argument and it's not possible to retrieve
+ // "text" (i.e. comment).
+ if (comment->getType() != Element::string) {
+ comment = answer->get("text");
+ }
+
+ if (!exp_txt.empty()) {
+ EXPECT_EQ(exp_txt, comment->stringValue());
+ }
+ }
+
+ /// @brief Checks whether specified command is registered
+ ///
+ /// @param name name of the command to be checked
+ /// @param expect_true true - must be registered, false - must not be
+ void checkCommandRegistered(const std::string& name, bool expect_true = true) {
+
+ // First get the list of registered commands
+ ConstElementPtr lst = Element::fromJSON("{ \"command\": \"list-commands\" }");
+ ConstElementPtr rsp = CtrlAgentCommandMgr::instance().processCommand(lst);
+
+ // The response must be an array with at least one element
+ ASSERT_TRUE(rsp);
+ ASSERT_EQ(Element::list, rsp->getType());
+ ASSERT_LE(1, rsp->size());
+ ConstElementPtr args = rsp->get(0)->get("arguments");
+ ASSERT_TRUE(args);
+
+ string args_txt = args->str();
+
+ if (expect_true) {
+ EXPECT_TRUE(args_txt.find(name) != string::npos);
+ } else {
+ EXPECT_TRUE(args_txt.find(name) == string::npos);
+ }
+ }
+
+};
+
+// Basic Controller instantiation testing.
+// Verifies that the controller singleton gets created and that the
+// basic derivation from the base class is intact.
+TEST_F(CtrlAgentControllerTest, basicInstanceTesting) {
+ // Verify the singleton instance can be fetched and that
+ // it has the correct type.
+ DControllerBasePtr& controller = DControllerTest::getController();
+ ASSERT_TRUE(controller);
+ ASSERT_NO_THROW(boost::dynamic_pointer_cast<CtrlAgentController>(controller));
+
+ // Verify that controller's app name is correct.
+ EXPECT_TRUE(checkAppName(CtrlAgentController::agent_app_name_));
+
+ // Verify that controller's bin name is correct.
+ EXPECT_TRUE(checkBinName(CtrlAgentController::agent_bin_name_));
+
+ // Verify that controller's IOService exists.
+ EXPECT_TRUE(checkIOService());
+
+ // Verify that the Process does NOT exist.
+ EXPECT_FALSE(checkProcess());
+}
+
+
+// Tests basic command line processing.
+// Verifies that:
+// 1. Standard command line options are supported.
+// 2. Invalid options are detected.
+TEST_F(CtrlAgentControllerTest, commandLineArgs) {
+ char* argv[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-c"),
+ const_cast<char*>(DControllerTest::CFG_TEST_FILE),
+ const_cast<char*>("-d") };
+ int argc = 4;
+
+ // Verify that verbose flag is false initially.
+ EXPECT_TRUE(checkVerbose(false));
+
+ // Verify that standard options can be parsed without error.
+ EXPECT_NO_THROW(parseArgs(argc, argv));
+
+ // Verify that verbose flag is true.
+ EXPECT_TRUE(checkVerbose(true));
+
+ // Verify configuration file name is correct.
+ EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE));
+
+ // Verify that an unknown option is detected.
+ char* argv2[] = { const_cast<char*>("progName"),
+ const_cast<char*>("-x") };
+ argc = 2;
+ EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
+}
+
+// Tests application process creation and initialization.
+// Verifies that the process can be successfully created and initialized.
+TEST_F(CtrlAgentControllerTest, initProcessTesting) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+}
+
+// Tests launch and normal shutdown (stand alone mode).
+// This creates an interval timer to generate a normal shutdown and then
+// launches with a valid, stand-alone command line and no simulated errors.
+TEST_F(CtrlAgentControllerTest, launchNormalShutdown) {
+ // Write valid_agent_config and then run launch() for 1000 ms.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 1000, elapsed_time);
+
+ // Give a generous margin to accommodate slower test environs.
+ EXPECT_TRUE(elapsed_time.total_milliseconds() >= 800 &&
+ elapsed_time.total_milliseconds() <= 1300);
+}
+
+// Tests that the SIGINT triggers a normal shutdown.
+TEST_F(CtrlAgentControllerTest, sigintShutdown) {
+ // Setup to raise SIGHUP in 1 ms.
+ TimedSignal sighup(*getIOService(), SIGINT, 1);
+
+ // Write valid_agent_config and then run launch() for a maximum
+ // of 1000 ms.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 1000, elapsed_time);
+
+ // Signaled shutdown should make our elapsed time much smaller than
+ // the maximum run time. Give generous margin to accommodate slow
+ // test environs.
+ EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
+}
+
+// Tests that the SIGTERM triggers a normal shutdown.
+TEST_F(CtrlAgentControllerTest, sigtermShutdown) {
+ // Setup to raise SIGTERM in 1 ms.
+ TimedSignal sighup(*getIOService(), SIGTERM, 1);
+
+ // Write valid_agent_config and then run launch() for a maximum of 1 s.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 1000, elapsed_time);
+
+ // Signaled shutdown should make our elapsed time much smaller than
+ // the maximum run time. Give generous margin to accommodate slow
+ // test environs.
+ EXPECT_TRUE(elapsed_time.total_milliseconds() < 300);
+}
+
+// Tests that the sockets settings are updated upon successful reconfiguration.
+TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) {
+ // This configuration should be used to override the initial configuration.
+ const char* second_config =
+ "{"
+ " \"http-host\": \"127.0.0.1\","
+ " \"http-port\": 8080,"
+ " \"control-sockets\": {"
+ " \"dhcp4\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/second/dhcp4/socket\""
+ " },"
+ " \"dhcp6\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/second/dhcp6/socket\""
+ " }"
+ " }"
+ "}";
+
+ // This check callback is called before the shutdown.
+ auto check_callback = [&] {
+ CtrlAgentProcessPtr process = getCtrlAgentProcess();
+ ASSERT_TRUE(process);
+
+ // Check that the HTTP listener still exists after reconfiguration.
+ ConstHttpListenerPtr listener = process->getHttpListener();
+ ASSERT_TRUE(listener);
+ EXPECT_TRUE(process->isListening());
+
+ // The listener should have been reconfigured to use new address and port.
+ EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
+ EXPECT_EQ(8080, listener->getLocalPort());
+ };
+
+ // Schedule reconfiguration.
+ scheduleTimedWrite(second_config, 100);
+ // Schedule SIGHUP signal to trigger reconfiguration.
+ TimedSignal sighup(*getIOService(), SIGHUP, 200);
+
+ // Start the server.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 500,
+ static_cast<const TestCallback&>(check_callback),
+ elapsed_time);
+
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+
+ // The server should now hold the new listener configuration.
+ EXPECT_EQ("127.0.0.1", ctx->getHttpHost());
+ EXPECT_EQ(8080, ctx->getHttpPort());
+
+ // The forwarding configuration should have been updated too.
+ testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
+ testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
+
+ // After the shutdown the HTTP listener no longer exists.
+ CtrlAgentProcessPtr process = getCtrlAgentProcess();
+ ASSERT_TRUE(process);
+ ConstHttpListenerPtr listener = process->getHttpListener();
+ ASSERT_FALSE(listener);
+ EXPECT_FALSE(process->isListening());
+}
+
+// Tests that the server continues to use an old configuration when the listener
+// reconfiguration is unsuccessful.
+TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) {
+ // This is invalid configuration. We're using restricted port number and
+ // IP address of 1.1.1.1.
+ const char* second_config =
+ "{"
+ " \"http-host\": \"1.1.1.1\","
+ " \"http-port\": 1,"
+ " \"control-sockets\": {"
+ " \"dhcp4\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/second/dhcp4/socket\""
+ " },"
+ " \"dhcp6\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/second/dhcp6/socket\""
+ " }"
+ " }"
+ "}";
+
+ // This check callback is called before the shutdown.
+ auto check_callback = [&] {
+ CtrlAgentProcessPtr process = getCtrlAgentProcess();
+ ASSERT_TRUE(process);
+
+ // We should still be using an original listener.
+ ConstHttpListenerPtr listener = process->getHttpListener();
+ ASSERT_TRUE(listener);
+ EXPECT_TRUE(process->isListening());
+
+ EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
+ EXPECT_EQ(8081, listener->getLocalPort());
+ };
+
+ // Schedule reconfiguration.
+ scheduleTimedWrite(second_config, 100);
+ // Schedule SIGHUP signal to trigger reconfiguration.
+ TimedSignal sighup(*getIOService(), SIGHUP, 200);
+
+ // Start the server.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 500,
+ static_cast<const TestCallback&>(check_callback),
+ elapsed_time);
+
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+
+ // The reconfiguration should have been unsuccessful, and the server should
+ // still use the original configuration.
+ EXPECT_EQ("127.0.0.1", ctx->getHttpHost());
+ EXPECT_EQ(8081, ctx->getHttpPort());
+
+ // Same for forwarding.
+ testUnixSocketInfo("dhcp4", "/first/dhcp4/socket");
+ testUnixSocketInfo("dhcp6", "/first/dhcp6/socket");
+
+ // After the shutdown the HTTP listener no longer exists.
+ CtrlAgentProcessPtr process = getCtrlAgentProcess();
+ ASSERT_TRUE(process);
+ ConstHttpListenerPtr listener = process->getHttpListener();
+ ASSERT_FALSE(listener);
+ EXPECT_FALSE(process->isListening());
+}
+
+// Tests that it is possible to update the configuration in such a way that the
+// listener configuration remains the same. The server should continue using the
+// listener instance it has been using prior to the reconfiguration.
+TEST_F(CtrlAgentControllerTest, noListenerChange) {
+ // This configuration should be used to override the initial configuration.
+ const char* second_config =
+ "{"
+ " \"http-host\": \"127.0.0.1\","
+ " \"http-port\": 8081,"
+ " \"control-sockets\": {"
+ " \"dhcp4\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/second/dhcp4/socket\""
+ " },"
+ " \"dhcp6\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/second/dhcp6/socket\""
+ " }"
+ " }"
+ "}";
+
+ // This check callback is called before the shutdown.
+ auto check_callback = [&] {
+ CtrlAgentProcessPtr process = getCtrlAgentProcess();
+ ASSERT_TRUE(process);
+
+ // Check that the HTTP listener still exists after reconfiguration.
+ ConstHttpListenerPtr listener = process->getHttpListener();
+ ASSERT_TRUE(listener);
+ EXPECT_TRUE(process->isListening());
+
+ EXPECT_EQ("127.0.0.1", listener->getLocalAddress().toText());
+ EXPECT_EQ(8081, listener->getLocalPort());
+ };
+
+ // Schedule reconfiguration.
+ scheduleTimedWrite(second_config, 100);
+ // Schedule SIGHUP signal to trigger reconfiguration.
+ TimedSignal sighup(*getIOService(), SIGHUP, 200);
+
+ // Start the server.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 500,
+ static_cast<const TestCallback&>(check_callback),
+ elapsed_time);
+
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+
+ // The server should use a correct listener configuration.
+ EXPECT_EQ("127.0.0.1", ctx->getHttpHost());
+ EXPECT_EQ(8081, ctx->getHttpPort());
+
+ // The forwarding configuration should have been updated.
+ testUnixSocketInfo("dhcp4", "/second/dhcp4/socket");
+ testUnixSocketInfo("dhcp6", "/second/dhcp6/socket");
+
+ CtrlAgentProcessPtr process = getCtrlAgentProcess();
+ ASSERT_TRUE(process);
+ ConstHttpListenerPtr listener = process->getHttpListener();
+ ASSERT_FALSE(listener);
+ EXPECT_FALSE(process->isListening());
+}
+
+// Tests that registerCommands actually registers anything.
+TEST_F(CtrlAgentControllerTest, registeredCommands) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl =
+ boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ ctrl->registerCommands();
+
+ // Check that the following command are really available.
+ checkCommandRegistered("build-report");
+ checkCommandRegistered("config-get");
+ checkCommandRegistered("config-reload");
+ checkCommandRegistered("config-set");
+ checkCommandRegistered("config-test");
+ checkCommandRegistered("config-write");
+ checkCommandRegistered("list-commands");
+ checkCommandRegistered("shutdown");
+ checkCommandRegistered("status-get");
+ checkCommandRegistered("version-get");
+
+ ctrl->deregisterCommands();
+}
+
+// Tests that config-write really writes a config file that contains
+// Control-agent configuration and not some other random nonsense.
+TEST_F(CtrlAgentControllerTest, configWrite) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl
+ = boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ // Now clean up after ourselves.
+ ctrl->registerCommands();
+
+ // First, build the command:
+ string file = string(TEST_DATA_BUILDDIR) + string("/config-write.json");
+ string cmd_txt = "{ \"command\": \"config-write\" }";
+ ConstElementPtr cmd = Element::fromJSON(cmd_txt);
+ ConstElementPtr params = Element::fromJSON("{\"filename\": \"" + file + "\" }");
+ CtrlAgentCommandMgr& mgr_ = CtrlAgentCommandMgr::instance();
+
+ // Send the command
+ ConstElementPtr answer = mgr_.handleCommand("config-write", params, cmd);
+
+ // Check that the command was successful
+ checkAnswer(answer, isc::config::CONTROL_RESULT_SUCCESS);
+
+ // Now check that the file is there.
+ ifstream f(file.c_str());
+ ASSERT_TRUE(f.good());
+
+ // Now that's some rough check that the config written really contains
+ // something that looks like Control-agent configuration.
+ ConstElementPtr from_file = Element::fromJSONFile(file, true);
+ ASSERT_TRUE(from_file);
+ ConstElementPtr ca = from_file->get("Control-agent");
+ ASSERT_TRUE(ca);
+ EXPECT_TRUE(ca->get("control-sockets"));
+ EXPECT_TRUE(ca->get("hooks-libraries"));
+ EXPECT_TRUE(ca->get("http-host"));
+ EXPECT_TRUE(ca->get("http-port"));
+
+ // Remove the file.
+ ::remove(file.c_str());
+
+ // Now clean up after ourselves.
+ ctrl->deregisterCommands();
+}
+
+// Tests if config-reload attempts to reload a file and reports that the
+// file is missing.
+TEST_F(CtrlAgentControllerTest, configReloadMissingFile) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl
+ = boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ // Now clean up after ourselves.
+ ctrl->registerCommands();
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ getController()->setConfigFile("does-not-exist.json");
+
+ // Build and execute the command.
+ string cmd_txt = "{ \"command\": \"config-reload\" }";
+ ConstElementPtr cmd = Element::fromJSON(cmd_txt);
+ ConstElementPtr params;
+ ConstElementPtr answer;
+ answer = CtrlAgentCommandMgr::instance().handleCommand("config-reload",
+ params, cmd);
+
+ // Verify the reload was rejected.
+ string expected = "{ \"result\": 1, \"text\": "
+ "\"Configuration parsing failed: "
+ "Unable to open file does-not-exist.json\" }";
+ EXPECT_EQ(expected, answer->str());
+
+ // Now clean up after ourselves.
+ ctrl->deregisterCommands();
+}
+
+// Tests if config-reload attempts to reload a file and reports that the
+// file is not a valid JSON.
+TEST_F(CtrlAgentControllerTest, configReloadBrokenFile) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl
+ = boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ // Now clean up after ourselves.
+ ctrl->registerCommands();
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ getController()->setConfigFile("testbad.json");
+
+ // Although Kea is smart, its AI routines are not smart enough to handle
+ // this one... at least not yet.
+ ofstream f("testbad.json", ios::trunc);
+ f << "bla bla bla...";
+ f.close();
+
+ // Build and execute the command.
+ string cmd_txt = "{ \"command\": \"config-reload\" }";
+ ConstElementPtr cmd = Element::fromJSON(cmd_txt);
+ ConstElementPtr params;
+ ConstElementPtr answer;
+ answer = CtrlAgentCommandMgr::instance().handleCommand("config-reload",
+ params, cmd);
+
+ // Verify the reload was rejected.
+ string expected = "{ \"result\": 1, \"text\": "
+ "\"Configuration parsing failed: "
+ "testbad.json:1.1: Invalid character: b\" }";
+ EXPECT_EQ(expected, answer->str());
+
+ // Remove the file.
+ ::remove("testbad.json");
+
+ // Now clean up after ourselves.
+ ctrl->deregisterCommands();
+}
+
+// Tests if config-reload attempts to reload a file and reports that the
+// file is missing.
+TEST_F(CtrlAgentControllerTest, configReloadFileValid) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl
+ = boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ // Now clean up after ourselves.
+ ctrl->registerCommands();
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ getController()->setConfigFile("testvalid.json");
+
+ // Ok, enough fooling around. Let's create a valid config.
+ ofstream f("testvalid.json", ios::trunc);
+ f << "{ \"Control-agent\": "
+ << string(valid_agent_config)
+ << " }" << endl;
+ f.close();
+
+ // Build and execute the command.
+ string cmd_txt = "{ \"command\": \"config-reload\" }";
+ ConstElementPtr cmd = Element::fromJSON(cmd_txt);
+ ConstElementPtr params;
+ ConstElementPtr answer;
+ answer = CtrlAgentCommandMgr::instance().handleCommand("config-reload",
+ params, cmd);
+
+
+ // Verify the reload was successful.
+ string expected = "{ \"result\": 0, \"text\": "
+ "\"Configuration applied successfully.\" }";
+ EXPECT_EQ(expected, answer->str());
+
+ // Check that the config was indeed applied?
+
+ // Remove the file.
+ ::remove("testvalid.json");
+
+ // Now clean up after ourselves.
+ ctrl->deregisterCommands();
+}
+
+// Tests that status-get returns expected info (pid, uptime and reload).
+TEST_F(CtrlAgentControllerTest, statusGet) {
+ // Start the server.
+ time_duration elapsed_time;
+ runWithConfig(valid_agent_config, 500, elapsed_time);
+
+ const DControllerBasePtr& ctrl = getController();
+ ConstElementPtr response;
+ ASSERT_NO_THROW(response = ctrl->statusGetHandler("status-get", ConstElementPtr()));
+ ASSERT_TRUE(response);
+ ASSERT_EQ(Element::map, response->getType());
+ EXPECT_EQ(2, response->size());
+ ConstElementPtr result = response->get("result");
+ ASSERT_TRUE(result);
+ ASSERT_EQ(Element::integer, result->getType());
+ EXPECT_EQ(0, result->intValue());
+ ConstElementPtr arguments = response->get("arguments");
+ ASSERT_EQ(Element::map, arguments->getType());
+
+ // The returned pid should be the pid of our process.
+ auto found_pid = arguments->get("pid");
+ ASSERT_TRUE(found_pid);
+ EXPECT_EQ(static_cast<int64_t>(getpid()), found_pid->intValue());
+
+ // It is hard to check the actual uptime (and reload) as it is based
+ // on current time. Let's just make sure it is within a reasonable
+ // range.
+ auto found_uptime = arguments->get("uptime");
+ ASSERT_TRUE(found_uptime);
+ EXPECT_LE(found_uptime->intValue(), 5);
+ EXPECT_GE(found_uptime->intValue(), 0);
+
+ auto found_reload = arguments->get("reload");
+ ASSERT_TRUE(found_reload);
+ EXPECT_LE(found_reload->intValue(), 5);
+ EXPECT_GE(found_reload->intValue(), 0);
+}
+
+TEST_F(CtrlAgentControllerTest, shutdown) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl
+ = boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ // Now clean up after ourselves.
+ ctrl->registerCommands();
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ getController()->setConfigFile("testvalid.json");
+
+ // Ok, enough fooling around. Let's create a valid config.
+ ofstream f("testvalid.json", ios::trunc);
+ f << "{ \"Control-agent\": "
+ << string(valid_agent_config)
+ << " }" << endl;
+ f.close();
+
+ // Build and execute the command.
+
+ ConstElementPtr cmd = Element::fromJSON("{ \"command\": \"shutdown\"}");
+ ConstElementPtr params;
+ ConstElementPtr answer;
+ answer = CtrlAgentCommandMgr::instance().handleCommand("shutdown",
+ params, cmd);
+
+ // Verify the reload was successful.
+ string expected = "{ \"result\": 0, \"text\": "
+ "\"Control Agent is shutting down\" }";
+
+ EXPECT_EQ(expected, answer->str());
+
+ int exit_value = ctrl->getExitValue();
+ EXPECT_EQ(0, exit_value);
+
+ // Remove the file.
+ ::remove("testvalid.json");
+
+ // Now clean up after ourselves.
+ ctrl->deregisterCommands();
+}
+
+
+TEST_F(CtrlAgentControllerTest, shutdownExitValue) {
+ ASSERT_NO_THROW(initProcess());
+ EXPECT_TRUE(checkProcess());
+
+ // The framework available makes it very difficult to test the actual
+ // code as CtrlAgentController is not initialized the same way it is
+ // in production code. In particular, the way CtrlAgentController
+ // is initialized in tests does not call registerCommands().
+ // This is a crude workaround for this problem. Proper solution should
+ // be developed sooner rather than later.
+ const DControllerBasePtr& base = getController();
+ const CtrlAgentControllerPtr& ctrl
+ = boost::dynamic_pointer_cast<CtrlAgentController>(base);
+ ASSERT_TRUE(ctrl);
+ // Now clean up after ourselves.
+ ctrl->registerCommands();
+
+ // This is normally set to whatever value is passed to -c when the server is
+ // started, but we're not starting it that way, so need to set it by hand.
+ getController()->setConfigFile("testvalid.json");
+
+ // Ok, enough fooling around. Let's create a valid config.
+ ofstream f("testvalid.json", ios::trunc);
+ f << "{ \"Control-agent\": "
+ << string(valid_agent_config)
+ << " }" << endl;
+ f.close();
+
+ // Build and execute the command.
+
+ ConstElementPtr cmd = Element::fromJSON("{ \"command\": \"shutdown\"}");
+ ConstElementPtr params = Element::fromJSON("{ \"exit-value\": 77 }");
+ ConstElementPtr answer;
+ answer = CtrlAgentCommandMgr::instance().handleCommand("shutdown",
+ params, cmd);
+
+ // Verify the reload was successful.
+ string expected = "{ \"result\": 0, \"text\": "
+ "\"Control Agent is shutting down\" }";
+
+ EXPECT_EQ(expected, answer->str());
+
+ int exit_value = ctrl->getExitValue();
+ EXPECT_EQ(77, exit_value);
+
+ // Remove the file.
+ ::remove("testvalid.json");
+
+ // Now clean up after ourselves.
+ ctrl->deregisterCommands();
+}
+
+}
diff --git a/src/bin/agent/tests/ca_process_tests.sh.in b/src/bin/agent/tests/ca_process_tests.sh.in
new file mode 100644
index 0000000..b533152
--- /dev/null
+++ b/src/bin/agent/tests/ca_process_tests.sh.in
@@ -0,0 +1,174 @@
+#!/bin/sh
+
+# 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/.
+
+# shellcheck disable=SC1091
+# SC1091: Not following: ... was not specified as input (see shellcheck -x).
+
+# shellcheck disable=SC2039
+# SC2039: In POSIX sh, 'local' is undefined.
+
+# Exit with error if commands exit with non-zero and if undefined variables are
+# used.
+set -eu
+
+# Include common test library.
+. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh"
+
+# Path to the temporary configuration file.
+CFG_FILE="@abs_top_builddir@/src/bin/agent/tests/test_config.json"
+# Path to the Control Agent log file.
+LOG_FILE="@abs_top_builddir@/src/bin/agent/tests/test.log"
+
+# Control Agent configuration to be stored in the configuration file.
+CONFIG="{
+ \"Control-agent\":
+ {
+ \"http-host\": \"127.0.0.1\",
+ \"loggers\": [
+ {
+ \"name\": \"kea-ctrl-agent\",
+ \"output_options\": [
+ {
+ \"output\": \"$LOG_FILE\"
+ }
+ ],
+ \"severity\": \"DEBUG\"
+ }
+ ]
+ }
+}"
+
+# Invalid configuration (syntax error) to check that Kea can check syntax.
+CONFIG_BAD_SYNTAX="{
+ \"Control-agent\":
+ {
+ \"http-port\": BOGUS
+ }
+}"
+
+# Invalid configuration (out of range port) to check that Kea can check syntax.
+CONFIG_BAD_VALUE="{
+ \"Control-agent\":
+ {
+ \"http-port\": 80000
+ }
+}"
+
+# Configuration with a password.
+CONFIG_PWD="{
+ \"Control-agent\":
+ {
+ \"http-host\": \"127.0.0.1\",
+ \"authentication\":
+ {
+ \"clients\": [
+ {
+ \"password\": \"sensitive\",
+ \"user\": \"superadmin\"
+ }
+ ],
+ \"type\": \"basic\"
+ }
+ }
+}"
+
+bin="kea-ctrl-agent"
+bin_path="@abs_top_builddir@/src/bin/agent"
+
+# Import common test library.
+. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh"
+
+# This test verifies that syntax checking works properly. This function
+# requires 3 parameters:
+# test_name
+# config - string with a content of the config (will be written to a file)
+# expected_code - expected exit code returned by kea (0 - success, 1 - failure)
+syntax_check_test() {
+ local test_name="${1}"
+ local config="${2}"
+ local expected_code="${3}"
+
+ # Log the start of the test and print test name.
+ test_start "${test_name}"
+ # Create correct configuration file.
+ create_config "${config}"
+ # Check it
+ printf 'Running command %s.\n' "\"${bin_path}/${bin} -t ${CFG_FILE}\""
+ run_command \
+ "${bin_path}/${bin}" -t "${CFG_FILE}"
+ if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then
+ printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}"
+ clean_exit 1
+ fi
+ test_finish 0
+}
+
+# This test verifies that Control Agent is shut down gracefully when it
+# receives a SIGINT or SIGTERM signal.
+shutdown_test() {
+ test_name=${1} # Test name
+ signum=${2} # Signal number
+ # Log the start of the test and print test name.
+ test_start "${test_name}"
+ # Create new configuration file.
+ create_config "${CONFIG}"
+ # Instruct Control Agent to log to the specific file.
+ set_logger
+ # Start Control Agent.
+ start_kea ${bin_path}/${bin}
+ # Wait up to 20s for Control Agent to start.
+ wait_for_kea 20
+ if [ "${_WAIT_FOR_KEA}" -eq 0 ]; then
+ printf "ERROR: timeout waiting for Control Agent to start.\n"
+ clean_exit 1
+ fi
+
+ # Check if it is still running. It could have terminated (e.g. as a result
+ # of configuration failure).
+ get_pid ${bin}
+ if [ "${_GET_PIDS_NUM}" -ne 1 ]; then
+ printf "ERROR: expected one Control Agent process to be started. Found %d processes\
+ started.\n" "${_GET_PIDS_NUM}"
+ clean_exit 1
+ fi
+
+ # Check in the log file, how many times server has been configured.
+ # It should be just once on startup.
+ get_reconfigs
+ if [ "${_GET_RECONFIGS}" -ne 1 ]; then
+ printf 'ERROR: server been configured %s time(s), but exactly 1 was expected.\n' "${_GET_RECONFIGS}"
+ clean_exit 1
+ else
+ printf "Server successfully configured.\n"
+ fi
+
+ # Send signal to Control Agent (SIGTERM, SIGINT etc.)
+ send_signal "${signum}" "${bin}"
+
+ # Now wait for process to log that it is exiting.
+ wait_for_message 10 "DCTL_SHUTDOWN" 1
+ if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then
+ printf "ERROR: Control Agent did not log shutdown.\n"
+ clean_exit 1
+ fi
+
+ # Make sure the server is down.
+ wait_for_server_down 5 ${bin}
+ assert_eq 1 "${_WAIT_FOR_SERVER_DOWN}" \
+ "Expected wait_for_server_down return %d, returned %d"
+
+ test_finish 0
+}
+
+server_pid_file_test "${CONFIG}" DCTL_ALREADY_RUNNING
+shutdown_test "ctrl-agent.sigterm_test" 15
+shutdown_test "ctrl-agent.sigint_test" 2
+syntax_check_test "ctrl-agent.syntax_check_success" "${CONFIG}" 0
+syntax_check_test "ctrl-agent.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1
+syntax_check_test "ctrl-agent.syntax_check_bad_values" "${CONFIG_BAD_VALUE}" 1
+password_redact_test "ctrl-agent.password_redact_test" "${CONFIG_PWD}" 0
diff --git a/src/bin/agent/tests/ca_process_unittests.cc b/src/bin/agent/tests/ca_process_unittests.cc
new file mode 100644
index 0000000..37e06f6
--- /dev/null
+++ b/src/bin/agent/tests/ca_process_unittests.cc
@@ -0,0 +1,90 @@
+// Copyright (C) 2016-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 <agent/ca_cfg_mgr.h>
+#include <agent/ca_process.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+#include <process/testutils/d_test_stubs.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+#include <functional>
+
+using namespace boost::posix_time;
+using namespace isc;
+using namespace isc::agent;
+using namespace isc::asiolink;
+using namespace isc::process;
+
+namespace {
+
+/// @brief CtrlAgentProcess test fixture class.
+class CtrlAgentProcessTest : public CtrlAgentProcess, public ::testing::Test {
+public:
+ /// @brief Constructor
+ CtrlAgentProcessTest() :
+ CtrlAgentProcess("agent-test",
+ IOServicePtr(new isc::asiolink::IOService())) {
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgMgr()->getCtrlAgentCfgContext();
+ ctx->setHttpHost("127.0.0.1");
+ ctx->setHttpPort(8081);
+ }
+
+ /// @brief Destructor
+ virtual ~CtrlAgentProcessTest() {
+ }
+
+ /// @brief Callback that will invoke shutdown method.
+ void genShutdownCallback() {
+ shutdown(isc::data::ConstElementPtr());
+ }
+};
+
+// Test construction of the CtrlAgentProcess object.
+TEST(CtrlAgentProcess, construction) {
+ // Verify that the constructor will fail if given an empty
+ // io service.
+ IOServicePtr lcl_io_service;
+ EXPECT_THROW(CtrlAgentProcess("TestProcess", lcl_io_service),
+ DProcessBaseError);
+
+ // Verify that the constructor succeeds with a valid io_service
+ lcl_io_service.reset(new IOService());
+ ASSERT_NO_THROW(CtrlAgentProcess("TestProcess", lcl_io_service));
+
+ // Verify tha the configuration is accessible after construction.
+ CtrlAgentProcess agent_process("TestProcess", lcl_io_service);
+ CtrlAgentCfgMgrPtr cfg_mgr = agent_process.getCtrlAgentCfgMgr();
+ ASSERT_TRUE(cfg_mgr);
+}
+
+// Verifies that en external call to shutdown causes the run method to
+// exit gracefully.
+TEST_F(CtrlAgentProcessTest, shutdown) {
+ // Use an asiolink IntervalTimer and callback to generate the
+ // shutdown invocation. (Note IntervalTimer setup is in milliseconds).
+ IntervalTimer timer(*getIoService());
+ timer.setup(std::bind(&CtrlAgentProcessTest::genShutdownCallback, this),
+ 200);
+
+ // Record start time, and invoke run().
+ ptime start = microsec_clock::universal_time();
+ EXPECT_NO_THROW(run());
+
+ // Record stop time.
+ ptime stop = microsec_clock::universal_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.
+ time_duration elapsed = stop - start;
+ EXPECT_TRUE(elapsed.total_milliseconds() >= 100 &&
+ elapsed.total_milliseconds() <= 400);
+}
+
+
+}
diff --git a/src/bin/agent/tests/ca_response_creator_factory_unittests.cc b/src/bin/agent/tests/ca_response_creator_factory_unittests.cc
new file mode 100644
index 0000000..8cef6fe
--- /dev/null
+++ b/src/bin/agent/tests/ca_response_creator_factory_unittests.cc
@@ -0,0 +1,39 @@
+// Copyright (C) 2017 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 <agent/ca_response_creator.h>
+#include <agent/ca_response_creator_factory.h>
+#include <boost/pointer_cast.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc::agent;
+
+namespace {
+
+// This test verifies that create() method always returns the same
+// instance of the CtrlAgentResponseCreator object.
+TEST(CtrlAgentResponseCreatorFactory, create) {
+ CtrlAgentResponseCreatorFactory factory;
+
+ // Invoke twice.
+ CtrlAgentResponseCreatorPtr response1;
+ CtrlAgentResponseCreatorPtr response2;
+ ASSERT_NO_THROW(response1 = boost::dynamic_pointer_cast<
+ CtrlAgentResponseCreator>(factory.create()));
+ ASSERT_NO_THROW(response2 = boost::dynamic_pointer_cast<
+ CtrlAgentResponseCreator>(factory.create()));
+
+ // It must always return non-null object.
+ ASSERT_TRUE(response1);
+ ASSERT_TRUE(response2);
+
+ // And it must always return the same object.
+ EXPECT_TRUE(response1 == response2);
+
+}
+
+}
diff --git a/src/bin/agent/tests/ca_response_creator_unittests.cc b/src/bin/agent/tests/ca_response_creator_unittests.cc
new file mode 100644
index 0000000..fe3895d
--- /dev/null
+++ b/src/bin/agent/tests/ca_response_creator_unittests.cc
@@ -0,0 +1,465 @@
+// Copyright (C) 2017-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 <agent/ca_controller.h>
+#include <agent/ca_process.h>
+#include <agent/ca_command_mgr.h>
+#include <agent/ca_response_creator.h>
+#include <cc/command_interpreter.h>
+#include <cryptolink/crypto_rng.h>
+#include <hooks/hooks_manager.h>
+#include <http/basic_auth_config.h>
+#include <http/post_request.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <process/testutils/d_test_stubs.h>
+#include <agent/tests/test_basic_auth_libraries.h>
+#include <gtest/gtest.h>
+#include <boost/pointer_cast.hpp>
+#include <functional>
+
+using namespace isc;
+using namespace isc::agent;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::http;
+using namespace isc::process;
+namespace ph = std::placeholders;
+
+namespace {
+
+/// @brief Test fixture class for @ref CtrlAgentResponseCreator.
+class CtrlAgentResponseCreatorTest : public DControllerTest {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates instance of the response creator and uses this instance to
+ /// create "empty" request. It also removes registered commands from the
+ /// command manager.
+ CtrlAgentResponseCreatorTest()
+ : DControllerTest(CtrlAgentController::instance),
+ response_creator_(),
+ request_(response_creator_.createNewHttpRequest()) {
+ // Deregisters commands.
+ CtrlAgentCommandMgr::instance().deregisterAll();
+ CtrlAgentCommandMgr::instance().
+ registerCommand("foo", std::bind(&CtrlAgentResponseCreatorTest::
+ fooCommandHandler,
+ this, ph::_1, ph::_2));
+
+ // Make sure that the request has been initialized properly.
+ if (!request_) {
+ ADD_FAILURE() << "CtrlAgentResponseCreator::createNewHttpRequest"
+ " returns NULL!";
+ }
+ HttpRequest::recordBasicAuth_ = true;
+ // Initialize process and cfgmgr.
+ try {
+ initProcess();
+ static_cast<void>(getCtrlAgentCfgContext());
+ } catch (const std::exception& ex) {
+ ADD_FAILURE() << "Initialization failed: " << ex.what();
+ }
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes registered commands from the command manager.
+ virtual ~CtrlAgentResponseCreatorTest() {
+ HttpRequest::recordBasicAuth_ = false;
+ CtrlAgentCommandMgr::instance().deregisterAll();
+ HooksManager::prepareUnloadLibraries();
+ static_cast<void>(HooksManager::unloadLibraries());
+ }
+
+ /// @brief Fills request context with required data to create new request.
+ ///
+ /// @param request Request which context should be configured.
+ void setBasicContext(const HttpRequestPtr& request) {
+ request->context()->method_ = "POST";
+ request->context()->http_version_major_ = 1;
+ request->context()->http_version_minor_ = 1;
+ request->context()->uri_ = "/foo";
+
+ // Content-Type
+ HttpHeaderContext content_type;
+ content_type.name_ = "Content-Type";
+ content_type.value_ = "application/json";
+ request->context()->headers_.push_back(content_type);
+
+ // Content-Length
+ HttpHeaderContext content_length;
+ content_length.name_ = "Content-Length";
+ content_length.value_ = "0";
+ request->context()->headers_.push_back(content_length);
+ }
+
+ /// @brief Test creation of stock response.
+ ///
+ /// @param status_code Status code to be included in the response.
+ /// @param must_contain Text that must be present in the textual
+ /// representation of the generated response.
+ void testStockResponse(const HttpStatusCode& status_code,
+ const std::string& must_contain) {
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(
+ response = response_creator_.createStockHttpResponse(request_,
+ status_code)
+ );
+ ASSERT_TRUE(response);
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+ // Make sure the response contains the string specified as argument.
+ EXPECT_TRUE(response_json->toString().find(must_contain) != std::string::npos);
+
+ }
+
+ /// @brief Handler for the 'foo' test command.
+ ///
+ /// @param command_name Command name, i.e. 'foo'.
+ /// @param command_arguments Command arguments (empty).
+ ///
+ /// @return Returns response with a single string "bar".
+ ConstElementPtr fooCommandHandler(const std::string& /*command_name*/,
+ const ConstElementPtr& /*command_arguments*/) {
+ ElementPtr arguments = Element::createList();
+ arguments->add(Element::create("bar"));
+ return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
+ }
+
+ /// @brief Returns a pointer to the configuration context.
+ CtrlAgentCfgContextPtr getCtrlAgentCfgContext() {
+ CtrlAgentProcessPtr process =
+ boost::dynamic_pointer_cast<CtrlAgentProcess>(getProcess());
+ if (!process) {
+ isc_throw(Unexpected, "no process");
+ }
+ CtrlAgentCfgMgrPtr cfgmgr = process->getCtrlAgentCfgMgr();
+ if (!cfgmgr) {
+ isc_throw(Unexpected, "no cfgmgr");
+ }
+ CtrlAgentCfgContextPtr ctx = cfgmgr->getCtrlAgentCfgContext();
+ if (!ctx) {
+ isc_throw(Unexpected, "no context");
+ }
+ return (ctx);
+ }
+
+ /// @brief Instance of the response creator.
+ CtrlAgentResponseCreator response_creator_;
+
+ /// @brief Instance of the "empty" request.
+ ///
+ /// The context belonging to this request may be modified by the unit
+ /// tests to verify various scenarios of response creation.
+ HttpRequestPtr request_;
+};
+
+// This test verifies that the created "empty" request has valid type.
+TEST_F(CtrlAgentResponseCreatorTest, createNewHttpRequest) {
+ // The request must be of PostHttpRequestJson type.
+ PostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
+ PostHttpRequestJson>(request_);
+ ASSERT_TRUE(request_json);
+}
+
+// Test that HTTP version of stock response is set to 1.0 if the request
+// context doesn't specify any version.
+TEST_F(CtrlAgentResponseCreatorTest, createStockHttpResponseNoVersion) {
+ testStockResponse(HttpStatusCode::BAD_REQUEST, "HTTP/1.0 400 Bad Request");
+}
+
+// Test that HTTP version of stock response is set to 1.0 if the request
+// version is higher than 1.1.
+TEST_F(CtrlAgentResponseCreatorTest, createStockHttpResponseHighVersion) {
+ request_->context()->http_version_major_ = 2;
+ testStockResponse(HttpStatusCode::REQUEST_TIMEOUT,
+ "HTTP/1.0 408 Request Timeout");
+}
+
+// Test that the server responds with version 1.1 if request version is 1.1.
+TEST_F(CtrlAgentResponseCreatorTest, createStockHttpResponseCorrectVersion) {
+ request_->context()->http_version_major_ = 1;
+ request_->context()->http_version_minor_ = 1;
+ testStockResponse(HttpStatusCode::NO_CONTENT, "HTTP/1.1 204 No Content");
+}
+
+// Test successful server response when the client specifies valid command.
+TEST_F(CtrlAgentResponseCreatorTest, createDynamicHttpResponse) {
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ std::string::npos);
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ std::string::npos);
+}
+
+// This test verifies that Internal Server Error is returned when invalid C++
+// request type is used. This is considered an error in the server logic.
+TEST_F(CtrlAgentResponseCreatorTest, createDynamicHttpResponseInvalidType) {
+ PostHttpRequestPtr request(new PostHttpRequest());
+ setBasicContext(request);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ request->context()->body_ = "{ \"command\": \"list-commands\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request->finalize());
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must contain Internal Server Error status code.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 500 Internal Server Error") !=
+ std::string::npos);
+}
+
+// This test verifies that Unauthorized is returned when authentication is
+// required but not provided by request.
+TEST_F(CtrlAgentResponseCreatorTest, noAuth) {
+ setBasicContext(request_);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ request_->context()->body_ = "{ \"command\": \"list-commands\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Require authentication.
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+ ASSERT_NO_THROW(ctx->setAuthConfig(auth));
+ auth->setRealm("ISC.ORG");
+ auth->add("foo", "", "bar", "");
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_TRUE(request_->getBasicAuth().empty());
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must contain Unauthorized status code.
+ std::string expected = "HTTP/1.1 401 Unauthorized";
+ EXPECT_TRUE(response_json->toString().find(expected) != std::string::npos);
+ // Reponse should contain WWW-Authenticate header with configured realm.
+ expected = "WWW-Authenticate: Basic realm=\"ISC.ORG\"";
+ EXPECT_TRUE(response_json->toString().find(expected) != std::string::npos);
+}
+
+// Test successful server response when the client is authenticated.
+TEST_F(CtrlAgentResponseCreatorTest, basicAuth) {
+ setBasicContext(request_);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ request_->context()->body_ = "{ \"command\": \"list-commands\" }";
+
+ // Add basic HTTP authentication header.
+ const BasicHttpAuth& basic_auth = BasicHttpAuth("foo", "bar");
+ const BasicAuthHttpHeaderContext& basic_auth_header =
+ BasicAuthHttpHeaderContext(basic_auth);
+ request_->context()->headers_.push_back(basic_auth_header);
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Require authentication.
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ BasicHttpAuthConfigPtr auth(new BasicHttpAuthConfig());
+ ASSERT_NO_THROW(ctx->setAuthConfig(auth));
+ // In fact the realm is used only on errors... set it anyway.
+ auth->setRealm("ISC.ORG");
+ auth->add("foo", "", "bar", "");
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_EQ("foo", request_->getBasicAuth());
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ std::string::npos);
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ std::string::npos);
+}
+
+// This test verifies that Unauthorized is returned when authentication is
+// required but not provided by request using the hook.
+TEST_F(CtrlAgentResponseCreatorTest, hookNoAuth) {
+ setBasicContext(request_);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ // We add a random value in the extra entry: see next unit test
+ // for an explanation about how it is used.
+ auto r32 = isc::cryptolink::random(4);
+ ASSERT_EQ(4, r32.size());
+ int extra = r32[0];
+ extra = (extra << 8) | r32[1];
+ extra = (extra << 8) | r32[2];
+ extra = (extra << 8) | r32[3];
+ request_->context()->body_ = "{ \"command\": \"list-commands\", ";
+ request_->context()->body_ += "\"extra\": " + std::to_string(extra) + " }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Setup hook.
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ HooksConfig& hooks_cfg = ctx->getHooksConfig();
+ std::string auth_cfg = "{ \"config\": {\n"
+ "\"type\": \"basic\",\n"
+ "\"realm\": \"ISC.ORG\",\n"
+ "\"clients\": [{\n"
+ " \"user\": \"foo\",\n"
+ " \"password\": \"bar\"\n"
+ " }]}}";
+ ConstElementPtr auth_json;
+ ASSERT_NO_THROW(auth_json = Element::fromJSON(auth_cfg));
+ hooks_cfg.add(std::string(BASIC_AUTH_LIBRARY), auth_json);
+ ASSERT_NO_THROW(hooks_cfg.loadLibraries());
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_TRUE(request_->getBasicAuth().empty());
+ ASSERT_TRUE(response);
+
+ // Request should have no extra.
+ EXPECT_EQ("{ \"command\": \"list-commands\" }",
+ request_->context()->body_);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must contain Unauthorized status code.
+ std::string expected = "HTTP/1.1 401 Unauthorized";
+ EXPECT_TRUE(response_json->toString().find(expected) != std::string::npos);
+ // Reponse should contain WWW-Authenticate header with configured realm.
+ expected = "WWW-Authenticate: Basic realm=\"ISC.ORG\"";
+ EXPECT_TRUE(response_json->toString().find(expected) != std::string::npos);
+}
+
+// Test successful server response when the client is authenticated.
+TEST_F(CtrlAgentResponseCreatorTest, hookBasicAuth) {
+ setBasicContext(request_);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ // We add a random value in the extra entry:
+ // - this proves that the auth callout can get the request argument
+ // - this proves that the auth callout can modify the request argument
+ // before the request is executed (the extra entry if still present
+ // would make the command to be rejected as malformed)
+ // - this proves that a value can be communicate between the auth
+ // and response callout points
+ // - this proves that the response callout can get the response argument
+ // - this proves that the response callout can modify the response
+ // argument
+ auto r32 = isc::cryptolink::random(4);
+ ASSERT_EQ(4, r32.size());
+ int extra = r32[0];
+ extra = (extra << 8) | r32[1];
+ extra = (extra << 8) | r32[2];
+ extra = (extra << 8) | r32[3];
+ if (extra == 0) {
+ extra = 1;
+ }
+ std::string extra_str = std::to_string(extra);
+ request_->context()->body_ = "{ \"command\": \"list-commands\", ";
+ request_->context()->body_ += "\"extra\": " + extra_str + " }";
+
+ // Add basic HTTP authentication header.
+ const BasicHttpAuth& basic_auth = BasicHttpAuth("foo", "bar");
+ const BasicAuthHttpHeaderContext& basic_auth_header =
+ BasicAuthHttpHeaderContext(basic_auth);
+ request_->context()->headers_.push_back(basic_auth_header);
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Setup hook.
+ CtrlAgentCfgContextPtr ctx = getCtrlAgentCfgContext();
+ ASSERT_TRUE(ctx);
+ HooksConfig& hooks_cfg = ctx->getHooksConfig();
+ std::string auth_cfg = "{ \"config\": {\n"
+ "\"type\": \"basic\",\n"
+ "\"realm\": \"ISC.ORG\",\n"
+ "\"clients\": [{\n"
+ " \"user\": \"foo\",\n"
+ " \"password\": \"bar\"\n"
+ " }]}}";
+ ConstElementPtr auth_json;
+ ASSERT_NO_THROW(auth_json = Element::fromJSON(auth_cfg));
+ hooks_cfg.add(std::string(BASIC_AUTH_LIBRARY), auth_json);
+ ASSERT_NO_THROW(hooks_cfg.loadLibraries());
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ EXPECT_EQ("foo", request_->getBasicAuth());
+ ASSERT_TRUE(response);
+
+ // Request should have no extra.
+ EXPECT_EQ("{ \"command\": \"list-commands\" }",
+ request_->context()->body_);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ std::string::npos);
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ std::string::npos);
+ // Response must contain JSON body with "comment": "got".
+ std::string expected = "\"got\": " + extra_str + ", ";
+ EXPECT_TRUE(response_json->toString().find(expected) !=
+ std::string::npos);
+}
+
+}
diff --git a/src/bin/agent/tests/ca_unittests.cc b/src/bin/agent/tests/ca_unittests.cc
new file mode 100644
index 0000000..85b494c
--- /dev/null
+++ b/src/bin/agent/tests/ca_unittests.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2016-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/.
+
+#include <config.h>
+
+#include <log/logger_support.h>
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // See the documentation of the KEA_* environment variables in
+ // the developer's guide for info on how to tweak logging
+ 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/bin/agent/tests/callout_library.cc b/src/bin/agent/tests/callout_library.cc
new file mode 100644
index 0000000..e001c6f
--- /dev/null
+++ b/src/bin/agent/tests/callout_library.cc
@@ -0,0 +1,72 @@
+// Copyright (C) 2017-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/.
+
+/// @file
+/// @brief Basic callout library
+///
+/// This is source of a test library for Control Agent.
+///
+/// - Only the "version" framework function is supplied.
+///
+/// - hookpt_one callout is supplied.
+
+#include <config.h>
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+extern "C" {
+
+// Callouts. All return their result through the "result" argument.
+
+int
+context_create(CalloutHandle& handle) {
+ handle.setContext("result", static_cast<int>(10));
+ handle.setArgument("result", static_cast<int>(10));
+ return (0);
+}
+
+// First callout adds the passed "integer" argument to the initialized context
+// value of 10. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("integer", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result += data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Framework functions.
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+// load() initializes the user library if the main image was statically linked.
+int
+load(isc::hooks::LibraryHandle&) {
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ return (0);
+}
+
+}
+}
+
diff --git a/src/bin/agent/tests/get_config_unittest.cc b/src/bin/agent/tests/get_config_unittest.cc
new file mode 100644
index 0000000..40d9c50
--- /dev/null
+++ b/src/bin/agent/tests/get_config_unittest.cc
@@ -0,0 +1,309 @@
+// Copyright (C) 2017-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 <cc/data.h>
+#include <cc/command_interpreter.h>
+#include <testutils/user_context_utils.h>
+#include <process/testutils/d_test_stubs.h>
+#include <agent/ca_cfg_mgr.h>
+#include <agent/parser_context.h>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#include "test_data_files_config.h"
+#include "test_callout_libraries.h"
+
+using namespace isc::agent;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::process;
+using namespace isc::test;
+
+namespace {
+
+/// @name How to generate the testdata/get_config.json file
+///
+/// Define GENERATE_ACTION and recompile. Run ca_unittests on
+/// CtrlAgentGetCfgTest redirecting the standard error to a temporary
+/// file, e.g. by
+/// @code
+/// ./ca_unittests --gtest_filter="CtrlAgentGetCfg*" > /dev/null 2> u
+/// @endcode
+///
+/// Update testdata/get_config.json using the temporary file content,
+/// (removing head comment and restoring hook library path),
+/// recompile without GENERATE_ACTION.
+
+/// @brief the generate action
+/// false means do nothing, true means unparse extracted configurations
+#ifdef GENERATE_ACTION
+const bool generate_action = true;
+#else
+const bool generate_action = false;
+#endif
+
+/// @brief Read a file into a string
+std::string
+readFile(const std::string& file_path) {
+ std::ifstream ifs(file_path);
+ if (!ifs.is_open()) {
+ ADD_FAILURE() << "readFile cannot open " << file_path;
+ isc_throw(isc::Unexpected, "readFile cannot open " << file_path);
+ }
+ std::string lines;
+ std::string line;
+ while (std::getline(ifs, line)) {
+ lines += line + "\n";
+ }
+ ifs.close();
+ return (lines);
+}
+
+/// @brief Runs parser in JSON mode
+ElementPtr
+parseJSON(const std::string& in, bool verbose = false) {
+ try {
+ ParserContext ctx;
+ return (ctx.parseString(in, ParserContext::PARSER_JSON));
+ } catch (const std::exception& ex) {
+ if (verbose) {
+ std::cout << "EXCEPTION: " << ex.what() << std::endl;
+ }
+ throw;
+ }
+}
+
+/// @brief Runs parser in AGENT mode
+ElementPtr
+parseAGENT(const std::string& in, bool verbose = false) {
+ try {
+ ParserContext ctx;
+ return (ctx.parseString(in, ParserContext::PARSER_AGENT));
+ } catch (const std::exception& ex) {
+ if (verbose) {
+ std::cout << "EXCEPTION: " << ex.what() << std::endl;
+ }
+ throw;
+ }
+}
+
+/// @brief Replace the library path
+void
+pathReplacer(ConstElementPtr ca_cfg) {
+ ConstElementPtr hooks_libs = ca_cfg->get("hooks-libraries");
+ if (!hooks_libs || hooks_libs->empty()) {
+ return;
+ }
+ ElementPtr first_lib = hooks_libs->getNonConst(0);
+ std::string lib_path(CALLOUT_LIBRARY);
+ first_lib->set("library", Element::create(lib_path));
+}
+
+/// @brief Replace the credential directory
+void
+dirReplacer(ConstElementPtr ca_cfg) {
+ ConstElementPtr auth = ca_cfg->get("authentication");
+ if (!auth || auth->empty()) {
+ return;
+ }
+ ElementPtr mutable_auth = boost::const_pointer_cast<Element>(auth);
+ std::string dir_path(CA_TEST_DATA_DIR);
+ mutable_auth->set("directory", Element::create(dir_path));
+}
+
+/// @brief Almost regular agent CfgMgr with internal parse method exposed.
+class NakedAgentCfgMgr : public CtrlAgentCfgMgr {
+public:
+ using CtrlAgentCfgMgr::parse;
+};
+
+}
+
+/// Test fixture class
+class CtrlAgentGetCfgTest : public ConfigParseTest {
+public:
+ CtrlAgentGetCfgTest()
+ : rcode_(-1) {
+ srv_.reset(new NakedAgentCfgMgr());
+ // Create fresh context.
+ resetConfiguration();
+ }
+
+ ~CtrlAgentGetCfgTest() {
+ resetConfiguration();
+ }
+
+ /// @brief Parse and Execute configuration
+ ///
+ /// Parses a configuration and executes a configuration of the server.
+ /// If the operation fails, the current test will register a failure.
+ ///
+ /// @param config Configuration to parse
+ /// @param operation Operation being performed. In the case of an error,
+ /// the error text will include the string "unable to <operation>.".
+ ///
+ /// @return true if the configuration succeeded, false if not.
+ bool
+ executeConfiguration(const std::string& config, const char* operation) {
+ // try JSON parser
+ ConstElementPtr json;
+ try {
+ json = parseJSON(config, true);
+ } catch (const std::exception& ex) {
+ ADD_FAILURE() << "invalid JSON for " << operation
+ << " failed with " << ex.what()
+ << " on\n" << config << "\n";
+ return (false);
+ }
+
+ // try AGENT parser
+ try {
+ json = parseAGENT(config, true);
+ } catch (...) {
+ ADD_FAILURE() << "parsing failed for " << operation
+ << " on\n" << prettyPrint(json) << "\n";
+ return (false);
+ }
+
+ // get Control-agent element
+ ConstElementPtr ca = json->get("Control-agent");
+ if (!ca) {
+ ADD_FAILURE() << "cannot get Control-agent for " << operation
+ << " on\n" << prettyPrint(json) << "\n";
+ return (false);
+ }
+
+ // update hooks-libraries
+ pathReplacer(ca);
+
+ // update authentication directory
+ dirReplacer(ca);
+
+ // try AGENT configure
+ ConstElementPtr status;
+ try {
+ status = srv_->parse(ca, true);
+ } catch (const std::exception& ex) {
+ ADD_FAILURE() << "configure for " << operation
+ << " failed with " << ex.what()
+ << " on\n" << prettyPrint(json) << "\n";
+ return (false);
+ }
+
+ // The status object must not be NULL
+ if (!status) {
+ ADD_FAILURE() << "configure for " << operation
+ << " returned null on\n"
+ << prettyPrint(json) << "\n";
+ return (false);
+ }
+
+ // Returned value should be 0 (configuration success)
+ comment_ = parseAnswer(rcode_, status);
+ if (rcode_ != 0) {
+ string reason = "";
+ if (comment_) {
+ reason = string(" (") + comment_->stringValue() + string(")");
+ }
+ ADD_FAILURE() << "configure for " << operation
+ << " returned error code "
+ << rcode_ << reason << " on\n"
+ << prettyPrint(json) << "\n";
+ return (false);
+ }
+ return (true);
+ }
+
+ /// @brief Reset configuration database.
+ ///
+ /// This function resets configuration data base by
+ /// removing control sockets and hooks. Reset must
+ /// be performed after each test to make sure that
+ /// contents of the database do not affect result of
+ /// subsequent tests.
+ void resetConfiguration() {
+ string config = "{ \"Control-agent\": {"
+ " \"http-host\": \"\","
+ " \"http-port\": 0 } }";
+ EXPECT_TRUE(executeConfiguration(config, "reset config"));
+ }
+
+ boost::scoped_ptr<NakedAgentCfgMgr> srv_; ///< CA server under test
+ int rcode_; ///< Return code from element parsing
+ ConstElementPtr comment_; ///< Reason for parse fail
+};
+
+/// Test a configuration
+TEST_F(CtrlAgentGetCfgTest, simple) {
+
+ // get the simple configuration
+ std::string simple_file = string(CFG_EXAMPLES) + "/" + "simple.json";
+ std::string config;
+ ASSERT_NO_THROW(config = readFile(simple_file));
+
+ // get the expected configuration
+ std::string expected_file =
+ std::string(CA_TEST_DATA_DIR) + "/" + "get_config.json";
+ std::string expected;
+ ASSERT_NO_THROW(expected = readFile(expected_file));
+
+ // execute the sample configuration
+ ASSERT_TRUE(executeConfiguration(config, "simple config"));
+
+ // unparse it
+ CtrlAgentCfgContextPtr context = srv_->getCtrlAgentCfgContext();
+ ConstElementPtr unparsed;
+ ASSERT_NO_THROW(unparsed = context->toElement());
+
+ // dump if wanted else check
+ if (generate_action) {
+ std::cerr << "// Generated Configuration (remove this line)\n";
+ ASSERT_NO_THROW(expected = prettyPrint(unparsed));
+ prettyPrint(unparsed, std::cerr, 0, 4);
+ std::cerr << "\n";
+ } else {
+ // get the expected config using the agent syntax parser
+ ElementPtr jsond;
+ ASSERT_NO_THROW(jsond = parseAGENT(expected, true));
+ // get the expected config using the generic JSON syntax parser
+ ElementPtr jsonj;
+ ASSERT_NO_THROW(jsonj = parseJSON(expected));
+ // the generic JSON parser does not handle comments
+ EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj)));
+ // replace the paths by their actual values
+ ConstElementPtr ca;
+ ASSERT_NO_THROW(ca = jsonj->get("Control-agent"));
+ ASSERT_TRUE(ca);
+ pathReplacer(ca);
+ dirReplacer(ca);
+ // check that unparsed and updated expected values match
+ EXPECT_TRUE(isEquivalent(unparsed, jsonj));
+ // check on pretty prints too
+ std::string current = prettyPrint(unparsed, 0, 4);
+ std::string expected2 = prettyPrint(jsonj, 0, 4);
+ EXPECT_EQ(expected2, current);
+ if (expected2 != current) {
+ expected = current + "\n";
+ }
+ }
+
+ // execute the control agent configuration
+ EXPECT_TRUE(executeConfiguration(expected, "unparsed config"));
+
+ // is it a fixed point?
+ CtrlAgentCfgContextPtr context2 = srv_->getCtrlAgentCfgContext();
+ ConstElementPtr unparsed2;
+ ASSERT_NO_THROW(unparsed2 = context2->toElement());
+ ASSERT_TRUE(unparsed2);
+ EXPECT_TRUE(isEquivalent(unparsed, unparsed2));
+}
diff --git a/src/bin/agent/tests/parser_unittests.cc b/src/bin/agent/tests/parser_unittests.cc
new file mode 100644
index 0000000..2b35ac6
--- /dev/null
+++ b/src/bin/agent/tests/parser_unittests.cc
@@ -0,0 +1,931 @@
+// Copyright (C) 2017-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 <agent/parser_context.h>
+#include <cc/data.h>
+#include <cc/dhcp_config_error.h>
+#include <testutils/io_utils.h>
+#include <testutils/log_utils.h>
+#include <testutils/user_context_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <set>
+
+#include <boost/algorithm/string.hpp>
+
+using namespace isc::data;
+using namespace isc::test;
+using namespace std;
+
+namespace isc {
+namespace agent {
+namespace test {
+
+/// @brief compares two JSON trees
+///
+/// If differences are discovered, gtest failure is reported (using EXPECT_EQ)
+///
+/// @param a first to be compared
+/// @param b second to be compared
+void compareJSON(ConstElementPtr a, ConstElementPtr b) {
+ ASSERT_TRUE(a);
+ ASSERT_TRUE(b);
+ EXPECT_EQ(a->str(), b->str());
+}
+
+/// @brief Tests if the input string can be parsed with specific parser
+///
+/// The input text will be passed to bison parser of specified type.
+/// Then the same input text is passed to legacy JSON parser and outputs
+/// from both parsers are compared. The legacy comparison can be disabled,
+/// if the feature tested is not supported by the old parser (e.g.
+/// new comment styles)
+///
+/// @param txt text to be compared
+/// @param parser_type bison parser type to be instantiated
+/// @param compare whether to compare the output with legacy JSON parser
+void testParser(const std::string& txt, ParserContext::ParserType parser_type,
+ bool compare = true) {
+ SCOPED_TRACE("\n=== tested config ===\n" + txt + "=====================");
+
+ ConstElementPtr test_json;
+ ASSERT_NO_THROW({
+ try {
+ ParserContext ctx;
+ test_json = ctx.parseString(txt, parser_type);
+ } catch (const std::exception &e) {
+ cout << "EXCEPTION: " << e.what() << endl;
+ throw;
+ }
+
+ });
+
+ if (!compare) {
+ return;
+ }
+
+ // Now compare if both representations are the same.
+ ElementPtr reference_json;
+ ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
+ compareJSON(reference_json, test_json);
+}
+
+TEST(ParserTest, mapInMap) {
+ string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, listInList) {
+ string txt = "[ [ \"Britain\", \"Wales\", \"Scotland\" ], "
+ "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ]";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, nestedMaps) {
+ string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, nestedLists) {
+ string txt = "[ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]]";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, listsInMaps) {
+ string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelgeuse\" ], "
+ "\"cygnus\": [ \"deneb\", \"albireo\"] } }";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, mapsInLists) {
+ string txt = "[ { \"body\": \"earth\", \"gravity\": 1.0 },"
+ " { \"body\": \"mars\", \"gravity\": 0.376 } ]";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, types) {
+ string txt = "{ \"string\": \"foo\","
+ "\"integer\": 42,"
+ "\"boolean\": true,"
+ "\"map\": { \"foo\": \"bar\" },"
+ "\"list\": [ 1, 2, 3 ],"
+ "\"null\": null }";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+TEST(ParserTest, keywordJSON) {
+ string txt = "{ \"name\": \"user\","
+ "\"type\": \"password\","
+ "\"user\": \"name\","
+ "\"password\": \"type\" }";
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+// This test checks if full config (with top level and Control-agent objects) can
+// be parsed with syntactic checking (and as pure JSON).
+TEST(ParserTest, keywordAgent) {
+ string txt = "{ \"Control-agent\": {\n"
+ " \"http-host\": \"localhost\",\n"
+ " \"http-port\": 8000,\n"
+ " \"control-sockets\": {"
+ " \"dhcp4\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/tmp/kea4-ctrl-socket\""
+ " },"
+ " \"dhcp6\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/tmp/kea6-ctrl-socket\""
+ " },"
+ " \"d2\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\""
+ " }"
+ " },"
+ " \"hooks-libraries\": ["
+ " {"
+ " \"library\": \"/opt/local/control-agent-commands.so\","
+ " \"parameters\": {"
+ " \"param1\": \"foo\""
+ " }"
+ " }"
+ " ]"
+ "} }";
+ // This is a full config, so we'll parse it as full config (PARSER_AGENT)
+ testParser(txt, ParserContext::PARSER_AGENT);
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+// This test checks if simplified config (without top level and Control-agent
+// objects) can be parsed with syntactic checking (and as pure JSON).
+TEST(ParserTest, keywordSubAgent) {
+
+ // This is similar to previous test, but note the lack of outer
+ // map and Control-agent.
+ string txt = "{\n"
+ " \"http-host\": \"localhost\",\n"
+ " \"http-port\": 8000,\n"
+ " \"control-sockets\": {"
+ " \"dhcp4\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/tmp/kea4-ctrl-socket\""
+ " },"
+ " \"dhcp6\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/tmp/kea6-ctrl-socket\""
+ " },"
+ " \"d2\": {"
+ " \"socket-type\": \"unix\","
+ " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\""
+ " }"
+ " },"
+ " \"hooks-libraries\": ["
+ " {"
+ " \"library\": \"/opt/local/control-agent-commands.so\","
+ " \"parameters\": {"
+ " \"param1\": \"foo\""
+ " }"
+ " }"
+ " ]"
+ "}";
+
+ // This is only a subset of full config, so we'll parse with PARSER_SUB_AGENT.
+ testParser(txt, ParserContext::PARSER_SUB_AGENT);
+ testParser(txt, ParserContext::PARSER_JSON);
+}
+
+// Tests if bash (#) comments are supported. That's the only comment type that
+// was supported by the old parser.
+TEST(ParserTest, bashComments) {
+ string txt= "{ \"Control-agent\": {"
+ " \"http-host\": \"localhost\","
+ " \"http-port\": 9000,\n"
+ " \"control-sockets\": {\n"
+ " \"d2\": {\n"
+ "# this is a comment\n"
+ "\"socket-type\": \"unix\", \n"
+ "# This socket is mine. I can name it whatever\n"
+ "# I like, ok?\n"
+ "\"socket-name\": \"Hector\" \n"
+ "} } } }";
+ testParser(txt, ParserContext::PARSER_AGENT);
+}
+
+// Tests if C++ (//) comments can start anywhere, not just in the first line.
+TEST(ParserTest, cppComments) {
+ string txt= "{ \"Control-agent\": {"
+ " \"http-host\": \"localhost\","
+ " \"http-port\": 9001, // the level is over 9000!\n"
+ " \"control-sockets\": {\n"
+ " // Let's try talking to D2. Sadly, it never talks"
+ " // to us back :( Maybe he doesn't like his name?\n"
+ " \"d2\": {"
+ "\"socket-type\": \"unix\", \n"
+ "\"socket-name\": \"Hector\" \n"
+ "} } } }";
+
+ testParser(txt, ParserContext::PARSER_AGENT, false);
+}
+
+// Tests if bash (#) comments can start anywhere, not just in the first line.
+TEST(ParserTest, bashCommentsInline) {
+ string txt= "{ \"Control-agent\": {"
+ " \"http-host\": \"localhost\","
+ " \"http-port\": 9000,\n"
+ " \"control-sockets\": {\n"
+ " \"d2\": {"
+ "\"socket-type\": \"unix\", # Maybe Hector is not really a \n"
+ "\"socket-name\": \"Hector\" # Unix process?\n"
+ "# Oh no! He's a windows one and just pretending!\n"
+ "} } } }";
+ testParser(txt, ParserContext::PARSER_AGENT, false);
+}
+
+// Tests if multi-line C style comments are handled correctly.
+TEST(ParserTest, multilineComments) {
+ string txt= "{ \"Control-agent\": {"
+ " \"http-host\": \"localhost\","
+ " \"http-port\": 9000,\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"socket-type\": \"unix\"\n"
+ " }\n"
+ " /* Ok, forget about it. If Hector doesn't want to talk,\n"
+ " we won't talk to him either. We now have quiet days. */\n"
+ " /* \"d2\": {"
+ " \"socket-type\": \"unix\",\n"
+ "\"socket-name\": \"Hector\"\n"
+ "}*/ } } }";
+ testParser(txt, ParserContext::PARSER_AGENT, false);
+}
+
+// Tests if embedded comments are handled correctly.
+TEST(ParserTest, embbededComments) {
+ string txt= "{ \"Control-agent\": {"
+ " \"comment\": \"a comment\","
+ " \"http-host\": \"localhost\","
+ " \"http-port\": 9000,\n"
+ " \"control-sockets\": {\n"
+ " \"dhcp4\": {\n"
+ " \"user-context\": { \"comment\": \"indirect\" },\n"
+ " \"socket-type\": \"unix\"\n"
+ " } },\n"
+ " \"user-context\": { \"compatible\": true }\n"
+ "} }";
+ testParser(txt, ParserContext::PARSER_AGENT, false);
+}
+
+/// @brief Loads specified example config file
+///
+/// This test loads specified example file twice: first, using the legacy
+/// JSON file and then second time using bison parser. Two created Element
+/// trees are then compared. The input is decommented before it is passed
+/// to legacy parser (as legacy support for comments is very limited).
+///
+/// @param fname name of the file to be loaded
+void testFile(const std::string& fname) {
+ ElementPtr json;
+ ElementPtr reference_json;
+ ConstElementPtr test_json;
+
+ string decommented = decommentJSONfile(fname);
+
+ cout << "Parsing file " << fname << "(" << decommented << ")" << endl;
+
+ EXPECT_NO_THROW(json = Element::fromJSONFile(decommented, true));
+ reference_json = moveComments(json);
+
+ // remove the temporary file
+ EXPECT_NO_THROW(::remove(decommented.c_str()));
+
+ EXPECT_NO_THROW(
+ try {
+ ParserContext ctx;
+ test_json = ctx.parseFile(fname, ParserContext::PARSER_AGENT);
+ } catch (const std::exception &x) {
+ cout << "EXCEPTION: " << x.what() << endl;
+ throw;
+ });
+
+ ASSERT_TRUE(reference_json);
+ ASSERT_TRUE(test_json);
+
+ compareJSON(reference_json, test_json);
+}
+
+// This test loads all available existing files. Each config is loaded
+// twice: first with the existing Element::fromJSONFile() and then
+// the second time with AgentParser. Both JSON trees are then compared.
+// Hopefully the list of example configs will grow over time.
+TEST(ParserTest, file) {
+ vector<string> configs;
+ configs.push_back("comments.json");
+ configs.push_back("https.json");
+ configs.push_back("simple.json");
+
+ for (int i = 0; i<configs.size(); i++) {
+ testFile(string(CFG_EXAMPLES) + "/" + configs[i]);
+ }
+}
+
+/// @brief Tests error conditions in AgentParser
+///
+/// @param txt text to be parsed
+/// @param parser_type type of the parser to be used in the test
+/// @param msg expected content of the exception
+void testError(const std::string& txt,
+ ParserContext::ParserType parser_type,
+ const std::string& msg) {
+ SCOPED_TRACE("\n=== tested config ===\n" + txt + "=====================");
+
+ try {
+ ParserContext ctx;
+ ConstElementPtr parsed = ctx.parseString(txt, parser_type);
+ FAIL() << "Expected ParseError but nothing was raised (expected: "
+ << msg << ")";
+ }
+ catch (const ParseError& ex) {
+ EXPECT_EQ(msg, ex.what());
+ }
+ catch (...) {
+ FAIL() << "Expected ParseError but something else was raised";
+ }
+}
+
+// Verify that error conditions are handled correctly.
+TEST(ParserTest, errors) {
+ // no input
+ testError("", ParserContext::PARSER_JSON,
+ "<string>:1.1: syntax error, unexpected end of file");
+ testError(" ", ParserContext::PARSER_JSON,
+ "<string>:1.2: syntax error, unexpected end of file");
+ testError("\n", ParserContext::PARSER_JSON,
+ "<string>:2.1: syntax error, unexpected end of file");
+ testError("\t", ParserContext::PARSER_JSON,
+ "<string>:1.2: syntax error, unexpected end of file");
+ testError("\r", ParserContext::PARSER_JSON,
+ "<string>:1.2: syntax error, unexpected end of file");
+
+ // comments
+ testError("# nothing\n",
+ ParserContext::PARSER_JSON,
+ "<string>:2.1: syntax error, unexpected end of file");
+ testError(" #\n",
+ ParserContext::PARSER_JSON,
+ "<string>:2.1: syntax error, unexpected end of file");
+ testError("// nothing\n",
+ ParserContext::PARSER_JSON,
+ "<string>:2.1: syntax error, unexpected end of file");
+ testError("/* nothing */\n",
+ ParserContext::PARSER_JSON,
+ "<string>:2.1: syntax error, unexpected end of file");
+ testError("/* no\nthing */\n",
+ ParserContext::PARSER_JSON,
+ "<string>:3.1: syntax error, unexpected end of file");
+ testError("/* no\nthing */\n\n",
+ ParserContext::PARSER_JSON,
+ "<string>:4.1: syntax error, unexpected end of file");
+ testError("/* nothing\n",
+ ParserContext::PARSER_JSON,
+ "Comment not closed. (/* in line 1");
+ testError("\n\n\n/* nothing\n",
+ ParserContext::PARSER_JSON,
+ "Comment not closed. (/* in line 4");
+ testError("{ /* */*/ }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3-8: Invalid character: *");
+ testError("{ /* // *// }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3-11: Invalid character: /");
+ testError("{ /* // */// }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:2.1: syntax error, unexpected end of file, "
+ "expecting }");
+
+ // includes
+ testError("<?\n",
+ ParserContext::PARSER_JSON,
+ "Directive not closed.");
+ testError("<?include\n",
+ ParserContext::PARSER_JSON,
+ "Directive not closed.");
+ string file = string(CFG_EXAMPLES) + "/" + "simple.json";
+ testError("<?include \"" + file + "\"\n",
+ ParserContext::PARSER_JSON,
+ "Directive not closed.");
+ testError("<?include \"/foo/bar\" ?>/n",
+ ParserContext::PARSER_JSON,
+ "Can't open include file /foo/bar");
+
+ // JSON keywords
+ testError("{ \"foo\": True }",
+ ParserContext::PARSER_JSON,
+ "<string>:1.10-13: JSON true reserved keyword is lower case only");
+ testError("{ \"foo\": False }",
+ ParserContext::PARSER_JSON,
+ "<string>:1.10-14: JSON false reserved keyword is lower case only");
+ testError("{ \"foo\": NULL }",
+ ParserContext::PARSER_JSON,
+ "<string>:1.10-13: JSON null reserved keyword is lower case only");
+ testError("{ \"foo\": Tru }",
+ ParserContext::PARSER_JSON,
+ "<string>:1.10: Invalid character: T");
+ testError("{ \"foo\": nul }",
+ ParserContext::PARSER_JSON,
+ "<string>:1.10: Invalid character: n");
+
+ // numbers
+ testError("123",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1-3: syntax error, unexpected integer, "
+ "expecting {");
+ testError("-456",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1-4: syntax error, unexpected integer, "
+ "expecting {");
+ testError("-0001",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1-5: syntax error, unexpected integer, "
+ "expecting {");
+ testError("1234567890123456789012345678901234567890",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-40: Failed to convert "
+ "1234567890123456789012345678901234567890"
+ " to an integer.");
+ testError("-3.14e+0",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1-8: syntax error, unexpected floating point, "
+ "expecting {");
+ testError("1e50000",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-7: Failed to convert 1e50000 "
+ "to a floating point.");
+
+ // strings
+ testError("\"aabb\"",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1-6: syntax error, unexpected constant string, "
+ "expecting {");
+ testError("{ \"aabb\"err",
+ ParserContext::PARSER_JSON,
+ "<string>:1.9: Invalid character: e");
+ testError("{ err\"aabb\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3: Invalid character: e");
+ testError("\"a\n\tb\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-6 (near 2): Invalid control in \"a\n\tb\"");
+ testError("\"a\n\\u12\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-8 (near 2): Invalid control in \"a\n\\u12\"");
+ testError("\"a\\n\\tb\"",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1-8: syntax error, unexpected constant string, "
+ "expecting {");
+ testError("\"a\\x01b\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-8 (near 3): Bad escape in \"a\\x01b\"");
+ testError("\"a\\u0162\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-9 (near 4): Unsupported unicode escape "
+ "in \"a\\u0162\"");
+ testError("\"a\\u062z\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-9 (near 3): Bad escape in \"a\\u062z\"");
+ testError("\"abc\\\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-6 (near 6): Overflow escape in \"abc\\\"");
+ testError("\"a\\u006\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-8 (near 3): Overflow unicode escape "
+ "in \"a\\u006\"");
+ testError("\"\\u\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-4 (near 2): Overflow unicode escape in \"\\u\"");
+ testError("\"\\u\x02\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-5 (near 2): Bad escape in \"\\u\x02\"");
+ testError("\"\\u\\\"foo\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-5 (near 2): Bad escape in \"\\u\\\"...");
+ testError("\"\x02\\u\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1-5 (near 1): Invalid control in \"\x02\\u\"");
+
+ // from data_unittest.c
+ testError("\\a",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1: Invalid character: \\");
+ testError("\\",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1: Invalid character: \\");
+ testError("\\\"\\\"",
+ ParserContext::PARSER_JSON,
+ "<string>:1.1: Invalid character: \\");
+
+ // want a map
+ testError("[]\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1: syntax error, unexpected [, "
+ "expecting {");
+ testError("[]\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.1: syntax error, unexpected [, "
+ "expecting {");
+ testError("{ 123 }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3-5: syntax error, unexpected integer, "
+ "expecting }");
+ testError("{ 123 }\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.3-5: syntax error, unexpected integer, "
+ "expecting Control-agent");
+ testError("{ \"foo\" }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.9: syntax error, unexpected }, "
+ "expecting :");
+ testError("{ \"foo\" }\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.3-7: syntax error, unexpected constant string, "
+ "expecting Control-agent");
+ testError("{ \"foo\":null }\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.3-7: syntax error, unexpected constant string, "
+ "expecting Control-agent");
+ testError("{ \"Logging\":null }\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.3-11: syntax error, unexpected constant string, "
+ "expecting Control-agent");
+ testError("{ \"Control-agent\" }\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.19: syntax error, unexpected }, "
+ "expecting :");
+ testError("{ \"Control-agent\":",
+ ParserContext::PARSER_AGENT,
+ "<string>:1.19: syntax error, unexpected end of file, "
+ "expecting {");
+ testError("{}{}\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3: syntax error, unexpected {, "
+ "expecting end of file");
+
+ // duplicate in map
+ testError("{ \"foo\": 1, \"foo\": true }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1:13: duplicate foo entries in "
+ "JSON map (previous at <string>:1:10)");
+
+ // bad commas
+ testError("{ , }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3: syntax error, unexpected \",\", "
+ "expecting }");
+ testError("{ , \"foo\":true }\n",
+ ParserContext::PARSER_JSON,
+ "<string>:1.3: syntax error, unexpected \",\", "
+ "expecting }");
+
+ // bad type
+ testError("{ \"Control-agent\":{\n"
+ " \"http-port\":false }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:2.15-19: syntax error, unexpected boolean, "
+ "expecting integer");
+
+ // unknown keyword
+ testError("{ \"Control-agent\":{\n"
+ " \"topping\": \"Mozzarella\" }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:2.2-10: got unexpected keyword "
+ "\"topping\" in Control-agent map.");
+
+ // user context and embedded comments
+ testError("{ \"Control-agent\":{\n"
+ " \"comment\": true,\n"
+ " \"http-port\": 9000 }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:2.14-17: syntax error, unexpected boolean, "
+ "expecting constant string");
+
+ testError("{ \"Control-agent\":{\n"
+ " \"user-context\": \"a comment\",\n"
+ " \"http-port\": 9000 }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:2.19-29: syntax error, unexpected constant string, "
+ "expecting {");
+
+ testError("{ \"Control-agent\":{\n"
+ " \"comment\": \"a comment\",\n"
+ " \"comment\": \"another one\",\n"
+ " \"http-port\": 9000 }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:3.3-11: duplicate user-context/comment entries "
+ "(previous at <string>:2:3)");
+
+ testError("{ \"Control-agent\":{\n"
+ " \"user-context\": { \"version\": 1 },\n"
+ " \"user-context\": { \"one\": \"only\" },\n"
+ " \"http-port\": 9000 }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:3.3-16: duplicate user-context entries "
+ "(previous at <string>:2:19)");
+
+ testError("{ \"Control-agent\":{\n"
+ " \"user-context\": { \"comment\": \"indirect\" },\n"
+ " \"comment\": \"a comment\",\n"
+ " \"http-port\": 9000 }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:3.3-11: duplicate user-context/comment entries "
+ "(previous at <string>:2:19)");
+
+ // duplicate Control-agent entries
+ testError("{ \"Control-agent\":{\n"
+ " \"comment\": \"first\" },\n"
+ " \"Control-agent\":{\n"
+ " \"comment\": \"second\" }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:3.3-17: syntax error, unexpected Control-agent, expecting \",\" or }");
+
+ // duplicate of not string entries
+ testError("{ \"Control-agent\":{\n"
+ " \"http-port\": 8000,\n"
+ " \"http-port\": 8001 }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:3:2: duplicate http-port entries in "
+ "Control-agent map (previous at <string>:2:15)");
+
+ // duplicate of string entries
+ testError("{ \"Control-agent\":{\n"
+ " \"http-host\": \"127.0.0.1\",\n"
+ " \"http-host\": \"::1\" }}\n",
+ ParserContext::PARSER_AGENT,
+ "<string>:3:2: duplicate http-host entries in "
+ "Control-agent map (previous at <string>:2:15)");
+}
+
+// Check unicode escapes
+TEST(ParserTest, unicodeEscapes) {
+ ConstElementPtr result;
+ string json;
+
+ // check we can reread output
+ for (char c = -128; c < 127; ++c) {
+ string ins(" ");
+ ins[1] = c;
+ ConstElementPtr e(new StringElement(ins));
+ json = e->str();
+ ASSERT_NO_THROW(
+ try {
+ ParserContext ctx;
+ result = ctx.parseString(json, ParserContext::PARSER_JSON);
+ } catch (const std::exception &x) {
+ cout << "EXCEPTION: " << x.what() << endl;
+ throw;
+ });
+ ASSERT_EQ(Element::string, result->getType());
+ EXPECT_EQ(ins, result->stringValue());
+ }
+}
+
+// This test checks that all representations of a slash is recognized properly.
+TEST(ParserTest, unicodeSlash) {
+ // check the 4 possible encodings of solidus '/'
+ ConstElementPtr result;
+ string json = "\"/\\/\\u002f\\u002F\"";
+ ASSERT_NO_THROW(
+ try {
+ ParserContext ctx;
+ result = ctx.parseString(json, ParserContext::PARSER_JSON);
+ } catch (const std::exception &x) {
+ cout << "EXCEPTION: " << x.what() << endl;
+ throw;
+ });
+ ASSERT_EQ(Element::string, result->getType());
+ EXPECT_EQ("////", result->stringValue());
+}
+
+/// @brief Load a file into a JSON element.
+///
+/// @param fname The name of the file to load.
+/// @param list The JSON element list to add the parsing result to.
+void loadFile(const string& fname, ElementPtr list) {
+ ParserContext ctx;
+ ElementPtr json;
+ EXPECT_NO_THROW(json = ctx.parseFile(fname, ParserContext::PARSER_AGENT));
+ ASSERT_TRUE(json);
+ list->add(json);
+}
+
+// This test checks that all map entries are in the sample file.
+TEST(ParserTest, mapEntries) {
+ // Type of keyword set.
+ typedef set<string> KeywordSet;
+
+ // Get keywords from the syntax file (agent_parser.yy).
+ ifstream syntax_file(SYNTAX_FILE);
+ EXPECT_TRUE(syntax_file.is_open());
+ string line;
+ KeywordSet syntax_keys = { "user-context" };
+ // Code setting the map entry.
+ const string pattern = "ctx.stack_.back()->set(\"";
+ while (getline(syntax_file, line)) {
+ // Skip comments.
+ size_t comment = line.find("//");
+ if (comment <= pattern.size()) {
+ continue;
+ }
+ if (comment != string::npos) {
+ line.resize(comment);
+ }
+ // Search for the code pattern.
+ size_t key_begin = line.find(pattern);
+ if (key_begin == string::npos) {
+ continue;
+ }
+ // Extract keywords.
+ line = line.substr(key_begin + pattern.size());
+ size_t key_end = line.find_first_of('"');
+ EXPECT_NE(string::npos, key_end);
+ string keyword = line.substr(0, key_end);
+ // Ignore result when adding the keyword to the syntax keyword set.
+ static_cast<void>(syntax_keys.insert(keyword));
+ }
+ syntax_file.close();
+
+ // Get keywords from the sample files
+ string sample_dir(CFG_EXAMPLES);
+ sample_dir += "/";
+ ElementPtr sample_json = Element::createList();
+ loadFile(sample_dir + "https.json", sample_json);
+ loadFile(sample_dir + "simple.json", sample_json);
+ KeywordSet sample_keys;
+ // Recursively extract keywords.
+ static void (*extract)(ConstElementPtr, KeywordSet&) =
+ [] (ConstElementPtr json, KeywordSet& set) {
+ if (json->getType() == Element::list) {
+ // Handle lists.
+ for (auto elem : json->listValue()) {
+ extract(elem, set);
+ }
+ } else if (json->getType() == Element::map) {
+ // Handle maps.
+ for (auto elem : json->mapValue()) {
+ static_cast<void>(set.insert(elem.first));
+ // Skip entries with free content.
+ if ((elem.first != "user-context") &&
+ (elem.first != "parameters")) {
+ extract(elem.second, set);
+ }
+ }
+ }
+ };
+ extract(sample_json, sample_keys);
+
+ // Compare.
+ EXPECT_EQ(syntax_keys, sample_keys);
+}
+
+/// @brief Tests a duplicate entry.
+///
+/// The entry was duplicated by adding a new <name>DDDD entry.
+/// An error is expected, usually it is a duplicate but there are
+/// a few syntax errors when the syntax allows only one parameter.
+///
+/// @param json the JSON configuration with the duplicate entry.
+void testDuplicate(ConstElementPtr json) {
+ string config = json->str();
+ size_t where = config.find("DDDD");
+ ASSERT_NE(string::npos, where);
+ string before = config.substr(0, where);
+ string after = config.substr(where + 4, string::npos);
+ ParserContext ctx;
+ EXPECT_THROW(ctx.parseString(before + after,
+ ParserContext::PARSER_AGENT),
+ ParseError) << "config: " << config;
+}
+
+// This test checks that duplicate entries make parsing to fail.
+TEST(ParserTest, duplicateMapEntries) {
+ // Get the config to work with from the sample file.
+ string sample_fname(CFG_EXAMPLES);
+ sample_fname += "/simple.json";
+ ParserContext ctx;
+ ElementPtr sample_json;
+ EXPECT_NO_THROW(sample_json =
+ ctx.parseFile(sample_fname, ParserContext::PARSER_AGENT));
+ ASSERT_TRUE(sample_json);
+
+ // Recursively check duplicates.
+ static void (*test)(ElementPtr, ElementPtr, size_t&) =
+ [] (ElementPtr config, ElementPtr json, size_t& cnt) {
+ if (json->getType() == Element::list) {
+ // Handle lists.
+ for (auto elem : json->listValue()) {
+ test(config, elem, cnt);
+ }
+ } else if (json->getType() == Element::map) {
+ // Handle maps.
+ for (auto elem : json->mapValue()) {
+ // Skip entries with free content.
+ if ((elem.first == "user-context") ||
+ (elem.first == "parameters")) {
+ continue;
+ }
+
+ // Perform tests.
+ string dup = elem.first + "DDDD";
+ json->set(dup, elem.second);
+ testDuplicate(config);
+ json->remove(dup);
+ ++cnt;
+
+ // Recursive call.
+ ElementPtr mutable_json =
+ boost::const_pointer_cast<Element>(elem.second);
+ ASSERT_TRUE(mutable_json);
+ test(config, mutable_json, cnt);
+ }
+ }
+ };
+ size_t cnt = 0;
+ test(sample_json, sample_json, cnt);
+ cout << "checked " << cnt << " duplicated map entries\n";
+}
+
+/// @brief Test fixture for trailing commas.
+class TrailingCommasTest : public isc::dhcp::test::LogContentTest {
+public:
+ /// @brief Add a log entry.
+ ///
+ /// @param loc Location of the trailing comma.
+ void addLog(const string& loc) {
+ string log = "CTRL_AGENT_CONFIG_SYNTAX_WARNING Control Agent ";
+ log += "configuration syntax warning: " + loc;
+ log += ": Extraneous comma. ";
+ log += "A piece of configuration may have been omitted.";
+ addString(log);
+ }
+};
+
+// Test that trailing commas are allowed.
+TEST_F(TrailingCommasTest, tests) {
+ string txt(R"({
+ "Control-agent": {
+ "control-sockets": {
+ "d2": {
+ "socket-name": "/tmp/kea-dhcp-ddns-ctrl.sock",
+ "socket-type": "unix",
+ },
+ "dhcp4": {
+ "socket-name": "/tmp/kea-dhcp4-ctrl.sock",
+ "socket-type": "unix",
+ },
+ "dhcp6": {
+ "socket-name": "/tmp/kea-dhcp6-ctrl.sock",
+ "socket-type": "unix",
+ },
+ },
+ "http-host": "10.1.0.2",
+ "http-port": 8080,
+ "loggers": [
+ {
+ "debuglevel": 99,
+ "name": "kea-ctrl-agent",
+ "output_options": [
+ {
+ "output": "stdout",
+ },
+ ],
+ "severity": "DEBUG",
+ },
+ ],
+ },
+})");
+ testParser(txt, ParserContext::PARSER_AGENT, false);
+
+ addLog("<string>:6.30");
+ addLog("<string>:10.30");
+ addLog("<string>:14.30");
+ addLog("<string>:15.8");
+ addLog("<string>:25.31");
+ addLog("<string>:26.12");
+ addLog("<string>:28.28");
+ addLog("<string>:29.8");
+ addLog("<string>:30.6");
+ addLog("<string>:31.4");
+ EXPECT_TRUE(checkFile());
+
+ // Test with many consecutive commas.
+ boost::replace_all(txt, ",", ",,,,");
+ testParser(txt, ParserContext::PARSER_AGENT, false);
+}
+
+} // namespace test
+} // namespace agent
+} // namespace isc
diff --git a/src/bin/agent/tests/test_basic_auth_libraries.h.in b/src/bin/agent/tests/test_basic_auth_libraries.h.in
new file mode 100644
index 0000000..afa3da3
--- /dev/null
+++ b/src/bin/agent/tests/test_basic_auth_libraries.h.in
@@ -0,0 +1,24 @@
+// Copyright (C) 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 AGENT_TEST_BASIC_AUTH_LIBRARIES_H
+#define AGENT_TEST_BASIC_AUTH_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+// Names of the libraries used in these tests. These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file. Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Basic HTTP authentication as a callout library.
+static const char* BASIC_AUTH_LIBRARY = "@abs_builddir@/.libs/libbasicauth.so";
+
+} // anonymous namespace
+
+#endif // TEST_BASIC_AUTH_LIBRARIES_H
diff --git a/src/bin/agent/tests/test_callout_libraries.h.in b/src/bin/agent/tests/test_callout_libraries.h.in
new file mode 100644
index 0000000..78f51c8
--- /dev/null
+++ b/src/bin/agent/tests/test_callout_libraries.h.in
@@ -0,0 +1,24 @@
+// Copyright (C) 2017-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 AGENT_TEST_CALLOUT_LIBRARIES_H
+#define AGENT_TEST_CALLOUT_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+// Names of the libraries used in these tests. These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file. Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Basic callout library with context_create and three "standard" callouts.
+static const char* CALLOUT_LIBRARY = "@abs_builddir@/.libs/libcallout.so";
+
+} // anonymous namespace
+
+#endif // TEST_LIBRARIES_H
diff --git a/src/bin/agent/tests/test_data_files_config.h.in b/src/bin/agent/tests/test_data_files_config.h.in
new file mode 100644
index 0000000..b1d1124
--- /dev/null
+++ b/src/bin/agent/tests/test_data_files_config.h.in
@@ -0,0 +1,9 @@
+// Copyright (C) 2017 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/.
+
+/// @brief Path to agent source dir
+#define CA_SRC_DIR "@abs_top_srcdir@/src/bin/agent"
+#define CA_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/agent/tests/testdata"
diff --git a/src/bin/agent/tests/testdata/get_config.json b/src/bin/agent/tests/testdata/get_config.json
new file mode 100644
index 0000000..ce3df54
--- /dev/null
+++ b/src/bin/agent/tests/testdata/get_config.json
@@ -0,0 +1,55 @@
+{
+ "Control-agent": {
+ "authentication": {
+ "clients": [
+ {
+ "password": "1234",
+ "user": "admin",
+ "user-context": {
+ "comment": "admin is authorized"
+ }
+ },
+ {
+ "password-file": "hiddenp",
+ "user-file": "hiddenu"
+ },
+ {
+ "password-file": "hiddens"
+ }
+ ],
+ "directory": "/tmp/kea-creds",
+ "realm": "kea-control-agent",
+ "type": "basic"
+ },
+ "control-sockets": {
+ "d2": {
+ "socket-name": "/tmp/kea-ddns-ctrl-socket",
+ "socket-type": "unix",
+ "user-context": {
+ "in-use": false
+ }
+ },
+ "dhcp4": {
+ "socket-name": "/tmp/kea4-ctrl-socket",
+ "socket-type": "unix",
+ "user-context": {
+ "comment": "socket to DHCP4 server"
+ }
+ },
+ "dhcp6": {
+ "socket-name": "/tmp/kea6-ctrl-socket",
+ "socket-type": "unix"
+ }
+ },
+ "hooks-libraries": [
+ {
+ "library": "/opt/local/control-agent-commands.so",
+ "parameters": {
+ "param1": "foo"
+ }
+ }
+ ],
+ "http-host": "127.0.0.1",
+ "http-port": 8000
+ }
+}
diff --git a/src/bin/agent/tests/testdata/hiddenp b/src/bin/agent/tests/testdata/hiddenp
new file mode 100644
index 0000000..e8b2395
--- /dev/null
+++ b/src/bin/agent/tests/testdata/hiddenp
@@ -0,0 +1 @@
+KeaTest \ No newline at end of file
diff --git a/src/bin/agent/tests/testdata/hiddens b/src/bin/agent/tests/testdata/hiddens
new file mode 100644
index 0000000..f52fb83
--- /dev/null
+++ b/src/bin/agent/tests/testdata/hiddens
@@ -0,0 +1 @@
+kea:test \ No newline at end of file
diff --git a/src/bin/agent/tests/testdata/hiddenu b/src/bin/agent/tests/testdata/hiddenu
new file mode 100644
index 0000000..801489a
--- /dev/null
+++ b/src/bin/agent/tests/testdata/hiddenu
@@ -0,0 +1 @@
+keatest \ No newline at end of file