diff options
Diffstat (limited to 'src/bin/agent')
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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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 |