diff --git a/configure.ac b/configure.ac
index a583ae05b0..6ddd3f6dbe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1393,6 +1393,8 @@ AC_CONFIG_FILES([compatcheck/Makefile
src/bin/keactrl/keactrl.conf
src/bin/keactrl/tests/Makefile
src/bin/keactrl/tests/keactrl_tests.sh
+ src/bin/lfc/Makefile
+ src/bin/lfc/tests/Makefile
src/bin/perfdhcp/Makefile
src/bin/perfdhcp/tests/Makefile
src/bin/perfdhcp/tests/testdata/Makefile
diff --git a/doc/Doxyfile b/doc/Doxyfile
index a78375eef1..b27ce0eb31 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -650,6 +650,7 @@ INPUT = ../src/bin/d2 \
../src/bin/dhcp6 \
../src/bin/perfdhcp \
../src/bin/sockcreator \
+ ../src/bin/lfc \
../src/hooks/dhcp/user_chk \
../src/lib/asiolink \
../src/lib/cc \
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 372b59b4a0..c254189add 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,5 +1,5 @@
# The following build order must be maintained.
-SUBDIRS = dhcp4 dhcp6 d2 perfdhcp admin
+SUBDIRS = dhcp4 dhcp6 d2 perfdhcp admin lfc
if CONFIG_BACKEND_JSON
SUBDIRS += keactrl
diff --git a/src/bin/lfc/.gitignore b/src/bin/lfc/.gitignore
new file mode 100644
index 0000000000..1c52fb03e2
--- /dev/null
+++ b/src/bin/lfc/.gitignore
@@ -0,0 +1,5 @@
+/kea-lfc
+/kea-lfc.8
+/lfc_messages.cc
+/lfc_messages.h
+/s-messages
\ No newline at end of file
diff --git a/src/bin/lfc/Makefile.am b/src/bin/lfc/Makefile.am
new file mode 100644
index 0000000000..77ae8bfdd0
--- /dev/null
+++ b/src/bin/lfc/Makefile.am
@@ -0,0 +1,68 @@
+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)
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+if USE_CLANGPP
+# Disable unused parameter warning caused by some Boost headers when compiling with clang
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = lfc_messages.h lfc_messages.cc s-messages
+
+man_MANS = kea-lfc.8
+DISTCLEANFILES = $(man_MANS)
+EXTRA_DIST = $(man_MANS) kea-lfc.xml
+
+if GENERATE_DOCS
+kea-lfc.8: kea-lfc.xml
+ @XSLTPROC@ --novalid --xinclude --nonet -o $@ \
+ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl \
+ $(srcdir)/kea-lfc.xml
+
+else
+
+$(man_MANS):
+ @echo Man generation disabled. Creating dummy $@. Configure with --enable-generate-docs to enable it.
+ @echo Man generation disabled. Remove this file, configure with --enable-generate-docs, and rebuild Kea > $@
+
+endif
+
+lfc_messages.h lfc_messages.cc: s-messages
+
+s-messages: lfc_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/bin/lfc/lfc_messages.mes
+ touch $@
+
+BUILT_SOURCES = lfc_messages.h lfc_messages.cc
+
+# convenience archive
+
+noinst_LTLIBRARIES = liblfc.la
+
+liblfc_la_SOURCES =
+liblfc_la_SOURCES += lfc_controller.h
+liblfc_la_SOURCES += lfc_controller.cc
+
+nodist_liblfc_la_SOURCES = lfc_messages.h lfc_messages.cc
+EXTRA_DIST += lfc_messages.mes
+
+sbin_PROGRAMS = kea-lfc
+
+kea_lfc_SOURCES = main.cc
+
+kea_lfc_LDADD = liblfc.la
+kea_lfc_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+kea_lfc_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+kea_lfc_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+kea_lfc_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+kea_lfc_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+kea_lfc_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+
+kea_lfcdir = $(pkgdatadir)
diff --git a/src/bin/lfc/kea-lfc.xml b/src/bin/lfc/kea-lfc.xml
new file mode 100644
index 0000000000..d7cb9bb274
--- /dev/null
+++ b/src/bin/lfc/kea-lfc.xml
@@ -0,0 +1,184 @@
+]>
+
+
+
+
+
+ Feb 1, 2015
+
+
+
+ kea-lfc
+ 8
+ Kea
+
+
+
+ kea-lfc
+ Lease File Cleanup process in Kea
+
+
+
+
+ 2015
+ Internet Systems Consortium, Inc. ("ISC")
+
+
+
+
+
+ kea-lfc
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DESCRIPTION
+
+ The kea-lfc service process removes redundant
+ information for the files used to provide persistent storage for
+ the memfile data base backend. The service is written to run as
+ a stand alone process. While it can be started externally it
+ should be started by the Kea DHCP servers as desired and required.
+
+
+
+
+ ARGUMENTS
+
+ The arguments are as follows:
+
+
+
+
+
+
+ Verbose mode sets the logging level to debug. This is primarily
+ for development purposes in stand-alone mode.
+
+
+
+
+
+
+ version causes the version stamp to be printed.
+
+
+
+
+
+
+ Version causes a longer form of the version stamp to be printed.
+
+
+
+
+
+
+ Help causes the usage string to be printed.
+
+
+
+
+
+
+ The protocol version of the lease files, must be one of 4 or 6.
+
+
+
+
+
+
+ Configuration file including the configuration for
+ kea-lfc process. It may also
+ contain configuration entries for other Kea services.
+ Currently kea-lfc gets all of its arguments from
+ the comamnd line, in the future it will be extended to get some arguments
+ from the config file.
+
+
+
+
+
+
+ Previous lease file - When kea-lfc starts this
+ is the result of any previous run of kea-lfc.
+ When kea-lfc finishes it is the result of this run.
+ If kea-lfc is interrupted before compelting
+ this file may not exist.
+
+
+
+
+
+
+ Input or copy of lease file - Before the DHCP server invokes
+ kea-lfc it will copy the current lease file
+ here and then call kea-lfc with this file.
+
+
+
+
+
+
+ Output lease file - The temporary file kea-lfc
+ should use to write the leases. Upon completion of writing this
+ this file it will be moved to the finish file (see below).
+
+
+
+
+
+
+ Finish or completion file - Another temporary file
+ kea-lfc uses for bookkeeping. When
+ kea-lfc completes writing the output
+ file it moves it to this file name. After
+ kea-lfc finishes deleting the other
+ files (previous and input) it moves this file to previous
+ lease file. By moving the files in this fashion the
+ kea-lfc and the DHCP server processes
+ can determine the correct file to use even if one of the
+ processes was interrupted before completing its task.
+
+
+
+
+
+
+ HISTORY
+
+ The kea-lfc process was first coded in January
+ 2015 by the ISC Kea/DHCP team.
+
+
+
diff --git a/src/bin/lfc/lfc_controller.cc b/src/bin/lfc/lfc_controller.cc
new file mode 100644
index 0000000000..d18247898e
--- /dev/null
+++ b/src/bin/lfc/lfc_controller.cc
@@ -0,0 +1,219 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+namespace isc {
+namespace lfc {
+
+/// @brief Defines the application name, it may be used to locate
+/// configuration data and appears in log statements.
+const char* LFCController::lfc_app_name_ = "DhcpLFC";
+
+/// @brief Defines the executable name.
+const char* LFCController::lfc_bin_name_ = "kea-lfc";
+
+LFCController::LFCController()
+ : protocol_version_(0), verbose_(false), config_file_(""), previous_file_(""),
+ copy_file_(""), output_file_(""), finish_file_(""), pid_file_("") {
+}
+
+LFCController::~LFCController() {
+}
+
+void
+LFCController::launch(int argc, char* argv[]) {
+ try {
+ parseArgs(argc, argv);
+ } catch (const InvalidUsage& ex) {
+ usage(ex.what());
+ throw; // rethrow it
+ }
+}
+
+void
+LFCController::parseArgs(int argc, char* argv[]) {
+ int ch;
+
+ opterr = 0;
+ optind = 1;
+ while ((ch = getopt(argc, argv, ":46dvVp:i:o:c:f:")) != -1) {
+ switch (ch) {
+ case '4':
+ // Process DHCPv4 lease files.
+ protocol_version_ = 4;
+ break;
+
+ case '6':
+ // Process DHCPv6 lease files.
+ protocol_version_ = 6;
+ break;
+
+ case 'v':
+ // Print just Kea vesion and exit.
+ std::cout << getVersion(false) << std::endl;
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ // Print extended Kea vesion and exit.
+ std::cout << getVersion(true) << std::endl;
+ exit(EXIT_SUCCESS);
+
+ case 'd':
+ // Verbose output.
+ verbose_ = true;
+ break;
+
+ case 'p':
+ // Previous file name.
+ if (optarg == NULL) {
+ isc_throw(InvalidUsage, "Previous file name missing");
+ }
+ previous_file_ = optarg;
+ break;
+
+ case 'i':
+ // Copy file name.
+ if (optarg == NULL) {
+ isc_throw(InvalidUsage, "Copy file name missing");
+ }
+ copy_file_ = optarg;
+ break;
+
+ case 'o':
+ // Output file name.
+ if (optarg == NULL) {
+ isc_throw(InvalidUsage, "Output file name missing");
+ }
+ output_file_ = optarg;
+ break;
+
+ case 'f':
+ // Output file name.
+ if (optarg == NULL) {
+ isc_throw(InvalidUsage, "Finish file name missing");
+ }
+ finish_file_ = optarg;
+ break;
+
+ case 'c':
+ // Previous file name.
+ if (optarg == NULL) {
+ isc_throw(InvalidUsage, "Configuration file name missing");
+ }
+ config_file_ = optarg;
+ break;
+
+ case 'h':
+ usage("");
+ exit(EXIT_SUCCESS);
+
+ case '?':
+ // Unknown argument
+ isc_throw(InvalidUsage, "Unknown argument");
+
+ case ':':
+ // Missing option argument
+ isc_throw(InvalidUsage, "Missing option argument");
+
+ default:
+ // I don't think we should get here as the unknown arguments
+ // and missing options cases should cover everything else
+ isc_throw(InvalidUsage, "Invalid command line");
+ }
+ }
+
+ // Check for extraneous parameters.
+ if (argc > optind) {
+ isc_throw(InvalidUsage, "Extraneous parameters.");
+ }
+
+ if (protocol_version_ == 0) {
+ isc_throw(InvalidUsage, "DHCP version required");
+ }
+
+ if (previous_file_.empty()) {
+ isc_throw(InvalidUsage, "Previous file not specified");
+ }
+
+ if (copy_file_.empty()) {
+ isc_throw(InvalidUsage, "Copy file not specified");
+ }
+
+ if (output_file_.empty()) {
+ isc_throw(InvalidUsage, "Output file not specified");
+ }
+
+ if (finish_file_.empty()) {
+ isc_throw(InvalidUsage, "Finish file not specified");
+ }
+
+ if (config_file_.empty()) {
+ isc_throw(InvalidUsage, "Config file not specified");
+ }
+
+ // If verbose is set echo the input information
+ if (verbose_ == true) {
+ std::cerr << "Protocol version: DHCPv" << protocol_version_ << std::endl
+ << "Previous lease file: " << previous_file_ << std::endl
+ << "Copy lease file: " << copy_file_ << std::endl
+ << "Output lease file: " << output_file_ << std::endl
+ << "Finishn file: " << finish_file_ << std::endl
+ << "Config file: " << config_file_ << std::endl
+ << "PID file: " << pid_file_ << std::endl;
+ }
+}
+
+void
+LFCController::usage(const std::string& text) {
+ if (text != "") {
+ std::cerr << "Usage error: " << text << std::endl;
+ }
+
+ std::cerr << "Usage: " << lfc_bin_name_ << std::endl
+ << " [-4|-6] -p file -i file -o file -f file -c file" << std::endl
+ << " -4 or -6 clean a set of v4 or v6 lease files" << std::endl
+ << " -p : previous lease file" << std::endl
+ << " -i : copy of lease file" << std::endl
+ << " -o : output lease file" << std::endl
+ << " -f : finish file" << std::endl
+ << " -c : configuration file" << std::endl
+ << " -v: print version number and exit" << std::endl
+ << " -V: print extended version inforamtion and exit" << std::endl
+ << " -d: optional, verbose output " << std::endl
+ << " -h: print this message " << std::endl
+ << std::endl;
+}
+
+std::string
+LFCController::getVersion(const bool extended) const{
+ std::stringstream version_stream;
+
+ version_stream << VERSION;
+ if (extended) {
+ version_stream << std::endl << EXTENDED_VERSION;
+ }
+
+ return (version_stream.str());
+}
+
+}; // namespace isc::lfc
+}; // namespace isc
diff --git a/src/bin/lfc/lfc_controller.h b/src/bin/lfc/lfc_controller.h
new file mode 100644
index 0000000000..84f42d7f9f
--- /dev/null
+++ b/src/bin/lfc/lfc_controller.h
@@ -0,0 +1,166 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LFC_CONTROLLER_H
+#define LFC_CONTROLLER_H
+
+#include
+#include
+
+namespace isc {
+namespace lfc {
+
+/// @brief Exception thrown when the command line is invalid.
+class InvalidUsage : public isc::Exception {
+public:
+ InvalidUsage(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Process controller for LFC process
+///
+/// This class provides the LFC process functions. These are used to:
+/// manage the command line, check for already running instances,
+/// invoke the code to process the lease files and finally to rename
+/// the lease files as necessary.
+///
+/// @todo The current code simply processes the command line we still need to
+/// -# handle PID file manipulation
+/// -# invoke the code to read, process and write the lease files
+/// -# rename and delete the shell files as required
+class LFCController {
+public:
+ /// @brief Defines the application name, it may be used to locate
+ /// configuration data and appears in log statements.
+ static const char* lfc_app_name_;
+
+ /// @brief Defines the executable name, by convention this should match
+ /// the executable name.
+ static const char* lfc_bin_name_;
+
+ /// @brief Constructor
+ LFCController();
+
+ /// @brief Destructor
+ ~LFCController();
+
+ /// @brief Acts as the primary entry point to start execution
+ /// of the process. Provides the control logic:
+ ///
+ /// -# parse command line arguments
+ /// -# verifies that it is the only instance
+ /// -# creates pid file (TBD)
+ /// -# .... TBD
+ /// -# remove pid file (TBD)
+ /// -# exit to the caller
+ ///
+ /// @param argc Number of strings in the @c argv array.
+ /// @param argv Array of arguments passed in via the program's main function.
+ ///
+ /// @throw InvalidUsage if the command line parameters are invalid.
+ void launch(int argc, char* argv[]);
+
+ /// @brief Process the command line arguments. It is the first
+ /// step taken after the process has been launched.
+ ///
+ /// @param argc Number of strings in the @c argv array.
+ /// @param argv Array of arguments passed in via the program's main function.
+ ///
+ /// @throw InvalidUsage if the command line parameters are invalid.
+ void parseArgs(int argc, char* argv[]);
+
+ /// @brief Prints the program usage text to std error.
+ ///
+ /// @param text is a string message which will preceded the usage text.
+ /// This is intended to be used for specific usage violation messages.
+ void usage(const std::string& text);
+
+ /// @brief Gets the Kea version number for printing
+ ///
+ /// @param extended is a boolean indicating if the version string
+ /// should be short (false) or extended (true)
+ std::string getVersion(const bool extended) const;
+
+ /// @name Accessor methods mainly used for testing purposes
+ //@{
+
+ /// @brief Gets the protocol version of the leaes files
+ ///
+ /// @return Returns the value of the DHCP protocol version.
+ /// This can be 4 or 6 while in use and 0 before parsing
+ /// any arguments.
+ int getProtocolVersion() const {
+ return (protocol_version_);
+ }
+
+ /// @brief Gets the config file name
+ ///
+ /// @return Returns the path to the config file
+ std::string getConfigFile() const {
+ return (config_file_);
+ }
+
+ /// @brief Gets the prevous file name
+ ///
+ /// @return Returns the path to the previous file
+ std::string getPreviousFile() const {
+ return (previous_file_);
+ }
+
+ /// @brief Gets the copy file name
+ ///
+ /// @return Returns the path to the copy file
+ std::string getCopyFile() const {
+ return (copy_file_);
+ }
+
+ /// @brief Gets the output file name
+ ///
+ /// @return Returns the path to the output file
+ std::string getOutputFile() const {
+ return (output_file_);
+ }
+
+ /// @brief Gets the finish file name
+ ///
+ /// @return Returns the path to the finish file
+ std::string getFinishFile() const {
+ return (finish_file_);
+ }
+
+ /// @brief Gets the pid file name
+ ///
+ /// @return Returns the path to the pid file
+ std::string getPidFile() const {
+ return (pid_file_);
+ }
+ //@}
+
+private:
+ /// Version of the DHCP protocol used, i.e. 4 or 6.
+ int protocol_version_;
+ /// When true output the result of parsing the comamnd line
+ bool verbose_;
+ std::string config_file_; ///< The path to the config file
+ std::string previous_file_; ///< The path to the previous LFC file (if any)
+ std::string copy_file_; ///< The path to the copy of the lease file
+ std::string output_file_; ///< The path to the output file
+ std::string finish_file_; ///< The path to the finished output file
+ std::string pid_file_; ///< The path to the pid file
+};
+
+}; // namespace isc::lfc
+}; // namespace isc
+
+#endif // LFC_CONTROLLER_H
diff --git a/src/bin/lfc/lfc_messages.mes b/src/bin/lfc/lfc_messages.mes
new file mode 100644
index 0000000000..94c6f46373
--- /dev/null
+++ b/src/bin/lfc/lfc_messages.mes
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::lfc
+% LFC_TEST_MESSAGE test messages
+This is a test and placeholder debug message
diff --git a/src/bin/lfc/main.cc b/src/bin/lfc/main.cc
new file mode 100644
index 0000000000..c1da3e9ac8
--- /dev/null
+++ b/src/bin/lfc/main.cc
@@ -0,0 +1,46 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace isc::lfc;
+using namespace std;
+
+/// This file contains the entry point (main() function) for the
+/// standard LFC process, kea-lfc, component of the Kea software suite.
+/// It creates an instance of the LFCController class and invokes
+/// its launch method.
+/// The exit value of the program will be EXIT_SUCCESS if there were no
+/// errors, EXIT_FAILURE otherwise.
+int main(int argc, char* argv[]) {
+ int ret = EXIT_SUCCESS;
+ LFCController lfc_controller;
+
+ // Launch the controller passing in command line arguments.
+ // Exit program with the controller's return code.
+ try {
+ // 'false' value disables test mode.
+ lfc_controller.launch(argc, argv);
+ } catch (const isc::Exception& ex) {
+ std::cerr << "Service failed: " << ex.what() << std::endl;
+ ret = EXIT_FAILURE;
+ }
+
+ return (ret);
+}
diff --git a/src/bin/lfc/tests/.gitignore b/src/bin/lfc/tests/.gitignore
new file mode 100644
index 0000000000..bfcb578d00
--- /dev/null
+++ b/src/bin/lfc/tests/.gitignore
@@ -0,0 +1 @@
+/lfc_unittests
diff --git a/src/bin/lfc/tests/Makefile.am b/src/bin/lfc/tests/Makefile.am
new file mode 100644
index 0000000000..9e799a2d81
--- /dev/null
+++ b/src/bin/lfc/tests/Makefile.am
@@ -0,0 +1,63 @@
+SHTESTS =
+
+noinst_SCRIPTS =
+
+EXTRA_DIST =
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+ for shtest in $(SHTESTS) ; do \
+ echo Running test: $$shtest ; \
+ export KEA_LOCKFILE_DIR=$(abs_top_builddir); \
+ ${SHELL} $(abs_builddir)/$$shtest || exit ; \
+ done
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/asiolink
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
+AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/lfc/tests\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
+
+DISTCLEANFILES =
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+if USE_CLANGPP
+# Disable unused parameter warning caused by some Boost headers when compiling with clang
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+
+TESTS += lfc_unittests
+
+lfc_unittests_SOURCES = lfc_unittests.cc
+lfc_unittests_SOURCES += lfc_controller_unittests.cc
+
+lfc_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+lfc_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+lfc_unittests_LDADD = $(GTEST_LDADD)
+lfc_unittests_LDADD += $(top_builddir)/src/bin/lfc/liblfc.la
+lfc_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+lfc_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+lfc_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+lfc_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+lfc_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+lfc_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/lfc/tests/lfc_controller_unittests.cc b/src/bin/lfc/tests/lfc_controller_unittests.cc
new file mode 100644
index 0000000000..163a1606b0
--- /dev/null
+++ b/src/bin/lfc/tests/lfc_controller_unittests.cc
@@ -0,0 +1,156 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include
+#include
+
+using namespace isc::lfc;
+using namespace std;
+
+namespace {
+
+/// @brief Verify initial state of LFC controller.
+/// Create an instance of the controller and see that
+/// all of the initial values are empty as expected.
+TEST(LFCControllerTest, initialValues) {
+ LFCController lfc_controller;
+
+ // Verify that we start with all the private variables empty
+ EXPECT_EQ(lfc_controller.getProtocolVersion(), 0);
+ EXPECT_TRUE(lfc_controller.getConfigFile().empty());
+ EXPECT_TRUE(lfc_controller.getPreviousFile().empty());
+ EXPECT_TRUE(lfc_controller.getCopyFile().empty());
+ EXPECT_TRUE(lfc_controller.getOutputFile().empty());
+ EXPECT_TRUE(lfc_controller.getFinishFile().empty());
+ EXPECT_TRUE(lfc_controller.getPidFile().empty());
+}
+
+/// @brief Verify that parsing a full command line works.
+/// Parse a complete command line then verify the parsed
+/// and saved data matches our expectations.
+TEST(LFCControllerTest, fullCommandLine) {
+ LFCController lfc_controller;
+
+ // Verify that standard options can be parsed without error
+ char* argv[] = { const_cast("progName"),
+ const_cast("-4"),
+ const_cast("-p"),
+ const_cast("previous"),
+ const_cast("-i"),
+ const_cast("copy"),
+ const_cast("-o"),
+ const_cast("output"),
+ const_cast("-c"),
+ const_cast("config"),
+ const_cast("-f"),
+ const_cast("finish") };
+ int argc = 12;
+
+ ASSERT_NO_THROW(lfc_controller.parseArgs(argc, argv));
+
+ // Check all the parsed data from above to the known values
+ EXPECT_EQ(lfc_controller.getProtocolVersion(), 4);
+ EXPECT_EQ(lfc_controller.getConfigFile(), "config");
+ EXPECT_EQ(lfc_controller.getPreviousFile(), "previous");
+ EXPECT_EQ(lfc_controller.getCopyFile(), "copy");
+ EXPECT_EQ(lfc_controller.getOutputFile(), "output");
+ EXPECT_EQ(lfc_controller.getFinishFile(), "finish");
+}
+
+/// @brief Verify that parsing a correct but incomplete line fails.
+/// Parse a command line that is correctly formatted but isn't complete
+/// (doesn't include some options or an some option arguments). We
+/// expect that the parse will fail with an InvalidUsage exception.
+TEST(LFCControllerTest, notEnoughData) {
+ LFCController lfc_controller;
+
+ // Test the results if we don't include all of the required arguments
+ // This argument list is correct but we shall only suppy part of it
+ // to the parse routine via the argc variable.
+ char* argv[] = { const_cast("progName"),
+ const_cast("-4"),
+ const_cast("-p"),
+ const_cast("previous"),
+ const_cast("-i"),
+ const_cast("copy"),
+ const_cast("-o"),
+ const_cast("output"),
+ const_cast("-c"),
+ const_cast("config"),
+ const_cast("-f"),
+ const_cast("finish") };
+
+ int argc = 1;
+
+ for (; argc < 12; ++argc) {
+ EXPECT_THROW(lfc_controller.parseArgs(argc, argv), InvalidUsage)
+ << "test failed for argc = " << argc;
+ }
+
+ // Verify we can still parse the full string properly
+ ASSERT_NO_THROW(lfc_controller.parseArgs(argc, argv));
+}
+
+/// @brief Verify that extra arguments cause the parse to fail.
+/// Parse a full command line plus some extra arguments on the end
+/// to verify that we don't stop parsing when we find all of the
+/// required arguments. We exepct the parse to fail with an
+/// InvalidUsage exception.
+TEST(LFCControllerTest, tooMuchData) {
+ LFCController lfc_controller;
+
+ // The standard options plus some others
+
+ char* argv[] = { const_cast("progName"),
+ const_cast("-4"),
+ const_cast("-p"),
+ const_cast("previous"),
+ const_cast("-i"),
+ const_cast("copy"),
+ const_cast("-o"),
+ const_cast("output"),
+ const_cast("-c"),
+ const_cast("config"),
+ const_cast("-f"),
+ const_cast("finish"),
+ const_cast("some"),
+ const_cast("other"),
+ const_cast("args"),
+ };
+ int argc = 15;
+
+ // We expect an error as we have arguments that aren't valid
+ EXPECT_THROW(lfc_controller.parseArgs(argc, argv), InvalidUsage);
+}
+
+/// @brief Verify that unknown arguments cause the parse to fail.
+/// Parse some unknown arguments to verify that we generate the
+/// proper InvalidUsage exception.
+TEST(LFCControllerTest, someBadData) {
+ LFCController lfc_controller;
+
+ // Some random arguments
+
+ char* argv[] = { const_cast("progName"),
+ const_cast("some"),
+ const_cast("bad"),
+ const_cast("args"),
+ };
+ int argc = 4;
+
+ // We expect an error as the arguments aren't valid
+ EXPECT_THROW(lfc_controller.parseArgs(argc, argv), InvalidUsage);
+}
+
+} // end of anonymous namespace
diff --git a/src/bin/lfc/tests/lfc_unittests.cc b/src/bin/lfc/tests/lfc_unittests.cc
new file mode 100644
index 0000000000..a2bb34bc26
--- /dev/null
+++ b/src/bin/lfc/tests/lfc_unittests.cc
@@ -0,0 +1,30 @@
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include
+#include
+
+int
+main(int argc, char* argv[]) {
+
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // See the documentation of the KEA_* environment variables in
+ // src/lib/log/README for info on how to tweak logging
+ isc::log::initLogger();
+
+ int result = RUN_ALL_TESTS();
+
+ return (result);
+}