From: Gunnar Beutner Date: Sat, 2 Feb 2013 22:22:27 +0000 (+0100) Subject: Use boost::program_options to parse arguments. X-Git-Tag: v0.0.2~573 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dbf762e7711300f6ffabbc9f5831bf318709081d;p=icinga2 Use boost::program_options to parse arguments. Fixes #3536 Fixes #3184 --- diff --git a/configure.ac b/configure.ac index f4d50c9f6..283a7e905 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,7 @@ AX_BOOST_SIGNALS AX_BOOST_THREAD AX_BOOST_SYSTEM AX_BOOST_UNIT_TEST_FRAMEWORK +AX_BOOST_PROGRAM_OPTIONS AX_CHECK_OPENSSL([], [AC_MSG_ERROR([You need the OpenSSL headers and libraries in order to build this application])]) AC_CHECK_LIB(ssl, SSL_new) AC_CHECK_LIB(crypto, X509_NAME_oneline) diff --git a/icinga-app/Makefile.am b/icinga-app/Makefile.am index 9665a9f58..087edcc86 100644 --- a/icinga-app/Makefile.am +++ b/icinga-app/Makefile.am @@ -28,6 +28,7 @@ icinga2_LDADD = \ $(BOOST_SIGNALS_LIB) \ $(BOOST_THREAD_LIB) \ $(BOOST_SYSTEM_LIB) \ + $(BOOST_PROGRAM_OPTIONS_LIB) \ ${top_builddir}/lib/base/libbase.la \ ${top_builddir}/lib/config/libconfig.la \ ${top_builddir}/lib/remoting/libremoting.la \ diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index b117fb53b..2b3ae47c7 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -28,6 +28,7 @@ #endif /* _WIN32 */ using namespace icinga; +namespace po = boost::program_options; /** * Entry point for the Icinga application. @@ -75,56 +76,90 @@ int main(int argc, char **argv) #endif /* _WIN32 */ ); - if (argc < 3 || strcmp(argv[1], "-c") != 0) { - stringstream msgbuf; - msgbuf << "Syntax: " << argv[0] << " -c ..."; - Logger::Write(LogInformation, "icinga-app", msgbuf.str()); - return EXIT_FAILURE; + po::options_description desc("Supported options"); + desc.add_options() + ("help,h", "show this help message") + ("library,l", po::value >(), "load a library") + ("include,I", po::value >(), "add include search directory") + ("config,c", po::value >(), "parse a configuration file") + ("validate,v", "exit after validating the configuration") + ("debug", "enable debugging") + ("daemonize,d", "daemonize after reading the configuration files") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (vm.count("debug")) + Application::SetDebugging(true); + + if (vm.count("help")) { + std::cout << desc << "\n"; + + return EXIT_SUCCESS; } Component::AddSearchDir(Application::GetPkgLibDir()); Utility::LoadIcingaLibrary("icinga", false); + if (vm.count("library")) { + BOOST_FOREACH(const String& libraryName, vm["library"].as >()) { + Utility::LoadIcingaLibrary(libraryName, false); + } + } + ConfigCompiler::AddIncludeSearchDir(Application::GetPkgDataDir()); + if (vm.count("include")) { + BOOST_FOREACH(const String& includePath, vm["include"].as >()) { + ConfigCompiler::AddIncludeSearchDir(includePath); + } + } + + if (vm.count("config") == 0) { + Logger::Write(LogCritical, "icinga-app", "You need to specify at least one config file (using the --config option)."); + + return EXIT_FAILURE; + } + try { DynamicObject::BeginTx(); - /* load config file */ - String configFile = argv[2]; - vector items; - vector types; + /* load config files */ + BOOST_FOREACH(const String& configPath, vm["config"].as >()) { + String configFile = argv[2]; + vector items; + vector types; - ConfigCompiler::CompileFile(configFile, &items, &types); + ConfigCompiler::CompileFile(configFile, &items, &types); - Logger::Write(LogInformation, "icinga-app", "Registering config types..."); - - BOOST_FOREACH(const ConfigType::Ptr& type, types) { - type->Commit(); - } - - Logger::Write(LogInformation, "icinga-app", "Executing config items..."); - - BOOST_FOREACH(const ConfigItem::Ptr& item, items) { - item->Commit(); - } - - Logger::Write(LogInformation, "icinga-app", "Validating config items..."); + Logger::Write(LogInformation, "icinga-app", "Registering config types..."); + BOOST_FOREACH(const ConfigType::Ptr& type, types) { + type->Commit(); + } - DynamicType::Ptr type; - BOOST_FOREACH(tie(tuples::ignore, type), DynamicType::GetTypes()) { - ConfigType::Ptr ctype = ConfigType::GetByName(type->GetName()); - - if (!ctype) { - Logger::Write(LogWarning, "icinga-app", "No config type found for type '" + type->GetName() + "'"); - - continue; + Logger::Write(LogInformation, "icinga-app", "Executing config items..."); + BOOST_FOREACH(const ConfigItem::Ptr& item, items) { + item->Commit(); } + + Logger::Write(LogInformation, "icinga-app", "Validating config items..."); + DynamicType::Ptr type; + BOOST_FOREACH(tie(tuples::ignore, type), DynamicType::GetTypes()) { + ConfigType::Ptr ctype = ConfigType::GetByName(type->GetName()); - DynamicObject::Ptr object; - BOOST_FOREACH(tie(tuples::ignore, object), type->GetObjects()) { - ctype->ValidateObject(object); + if (!ctype) { + Logger::Write(LogWarning, "icinga-app", "No config type found for type '" + type->GetName() + "'"); + + continue; + } + + DynamicObject::Ptr object; + BOOST_FOREACH(tie(tuples::ignore, object), type->GetObjects()) { + ctype->ValidateObject(object); + } } } @@ -139,8 +174,16 @@ int main(int argc, char **argv) if (!app) throw_exception(runtime_error("Configuration must create an Application object.")); - /* The application class doesn't need to know about the "-c configFile" - * command-line arguments. */ - return app->Run(argc - 2, &(argv[2])); + if (vm.count("validate")) { + Logger::Write(LogInformation, "icinga-app", "Terminating as requested by --validate."); + return EXIT_SUCCESS; + } + + if (vm.count("daemonize")) { + Logger::Write(LogInformation, "icinga", "Daemonizing."); + Utility::Daemonize(); + } + + return app->Run(); } diff --git a/lib/base/application.cpp b/lib/base/application.cpp index e0a77022a..cd7c2cae6 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -246,6 +246,16 @@ String Application::GetExePath(const String& argv0) #endif /* _WIN32 */ } +/** + * Sets whether debugging is enabled. + * + * @param debug Whether to enable debugging. + */ +void Application::SetDebugging(bool debug) +{ + m_Debugging = debug; +} + /** * Retrieves the debugging mode of the application. * @@ -394,11 +404,9 @@ void Application::InstallExceptionHandlers(void) /** * Runs the application. * - * @param argc The number of arguments. - * @param argv The arguments that should be passed to the application. * @returns The application's exit code. */ -int Application::Run(int argc, char **argv) +int Application::Run(void) { int result; @@ -414,13 +422,9 @@ int Application::Run(int argc, char **argv) SetConsoleCtrlHandler(&Application::CtrlHandler, TRUE); #endif /* _WIN32 */ - m_Arguments.clear(); - for (int i = 0; i < argc; i++) - m_Arguments.push_back(String(argv[i])); - DynamicObject::BeginTx(); - result = Main(m_Arguments); + result = Main(); DynamicObject::FinishTx(); DynamicObject::DeactivateObjects(); diff --git a/lib/base/application.h b/lib/base/application.h index 974c9d82c..b75ce64a8 100644 --- a/lib/base/application.h +++ b/lib/base/application.h @@ -39,21 +39,21 @@ public: static Application::Ptr GetInstance(void); - int Run(int argc, char **argv); + int Run(void); /** * Starts the application. * - * @param args Arguments for the application. * @returns The exit code of the application. */ - virtual int Main(const vector& args) = 0; + virtual int Main(void) = 0; static void InstallExceptionHandlers(void); static void RequestShutdown(void); static void Terminate(int exitCode); + static void SetDebugging(bool debug); static bool IsDebugging(void); static bool IsMainThread(void); diff --git a/lib/base/i2-base.h b/lib/base/i2-base.h index 2b27f5148..c48b007ae 100644 --- a/lib/base/i2-base.h +++ b/lib/base/i2-base.h @@ -137,6 +137,7 @@ using std::type_info; #include #include #include +#include using boost::shared_ptr; using boost::weak_ptr; diff --git a/lib/base/logger.cpp b/lib/base/logger.cpp index 86871cdf6..95af4c214 100644 --- a/lib/base/logger.cpp +++ b/lib/base/logger.cpp @@ -119,7 +119,14 @@ void Logger::ForwardLogEntry(const LogEntry& entry) processed = true; } - if (!processed && entry.Severity >= LogInformation) { + LogSeverity defaultLogLevel; + + if (Application::IsDebugging()) + defaultLogLevel = LogDebug; + else + defaultLogLevel = LogInformation; + + if (!processed && entry.Severity >= defaultLogLevel) { static bool tty = StreamLogger::IsTty(std::cout); StreamLogger::ProcessLogEntry(std::cout, tty, entry); diff --git a/lib/config/configcompiler.cpp b/lib/config/configcompiler.cpp index a9b1a25ac..a4332b493 100644 --- a/lib/config/configcompiler.cpp +++ b/lib/config/configcompiler.cpp @@ -250,6 +250,8 @@ void ConfigCompiler::AddObject(const ConfigItem::Ptr& object) */ void ConfigCompiler::AddIncludeSearchDir(const String& dir) { + Logger::Write(LogInformation, "config", "Adding include search dir: " + dir); + m_IncludeSearchDirs.push_back(dir); } diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index 777180756..4b3e5edc8 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -48,50 +48,14 @@ IcingaApplication::IcingaApplication(const Dictionary::Ptr& serializedUpdate) /** * The entry point for the Icinga application. * - * @param args Command-line arguments. * @returns An exit status. */ -int IcingaApplication::Main(const vector& args) +int IcingaApplication::Main(void) { Logger::Write(LogInformation, "icinga", "In IcingaApplication::Main()"); m_StartTime = Utility::GetTime(); - if (args.size() == 1 && args[0] == "--help") { - stringstream msgbuf; - msgbuf << "Syntax: " << args[0] << " ... -d"; - Logger::Write(LogInformation, "icinga", msgbuf.str()); - return EXIT_FAILURE; - } - - bool daemonize = false; - bool parseOpts = true; - String configFile; - - /* TODO: clean up this mess; for now it will just have to do */ - vector::const_iterator it; - for (it = args.begin() + 1 ; it != args.end(); it++) { - String arg = *it; - - /* ignore empty arguments */ - if (arg.IsEmpty()) - continue; - - if (arg == "--") { - parseOpts = false; - continue; - } - - if (parseOpts && arg[0] == '-') { - if (arg == "-d") { - daemonize = true; - continue; - } else { - throw_exception(invalid_argument("Unknown option: " + arg)); - } - } - } - UpdatePidFile(GetPidPath()); if (!GetCertificateFile().IsEmpty() && !GetCAFile().IsEmpty()) { @@ -110,13 +74,6 @@ int IcingaApplication::Main(const vector& args) if (!GetService().IsEmpty()) EndpointManager::GetInstance()->AddListener(GetService()); - if (daemonize) { - Logger::Write(LogInformation, "icinga", "Daemonizing."); - ClosePidFile(); - Utility::Daemonize(); - UpdatePidFile(GetPidPath()); - } - /* restore the previous program state */ DynamicObject::RestoreObjects(GetStatePath()); diff --git a/lib/icinga/icingaapplication.h b/lib/icinga/icingaapplication.h index 01c1a920b..f17222afb 100644 --- a/lib/icinga/icingaapplication.h +++ b/lib/icinga/icingaapplication.h @@ -36,7 +36,7 @@ public: IcingaApplication(const Dictionary::Ptr& serializedUpdate); - int Main(const vector& args); + int Main(void); static IcingaApplication::Ptr GetInstance(void); diff --git a/m4/ax_boost_program_options.m4 b/m4/ax_boost_program_options.m4 new file mode 100644 index 000000000..38b9539ed --- /dev/null +++ b/m4/ax_boost_program_options.m4 @@ -0,0 +1,111 @@ +# ============================================================================ +# http://www.gnu.org/software/autoconf-archive/ax_boost_program_options.html +# ============================================================================ +# +# SYNOPSIS +# +# AX_BOOST_PROGRAM_OPTIONS +# +# DESCRIPTION +# +# Test for program options library from the Boost C++ libraries. The macro +# requires a preceding call to AX_BOOST_BASE. Further documentation is +# available at . +# +# This macro calls: +# +# AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) +# +# And sets: +# +# HAVE_BOOST_PROGRAM_OPTIONS +# +# LICENSE +# +# Copyright (c) 2009 Thomas Porschberg +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 22 + +AC_DEFUN([AX_BOOST_PROGRAM_OPTIONS], +[ + AC_ARG_WITH([boost-program-options], + AS_HELP_STRING([--with-boost-program-options@<:@=special-lib@:>@], + [use the program options library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-program-options=boost_program_options-gcc-mt-1_33_1 ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_program_options_lib="" + else + want_boost="yes" + ax_boost_user_program_options_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + export want_boost + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + AC_CACHE_CHECK([whether the Boost::Program_Options library is available], + ax_cv_boost_program_options, + [AC_LANG_PUSH(C++) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include + ]], + [[boost::program_options::options_description generic("Generic options"); + return 0;]])], + ax_cv_boost_program_options=yes, ax_cv_boost_program_options=no) + AC_LANG_POP([C++]) + ]) + if test "$ax_cv_boost_program_options" = yes; then + AC_DEFINE(HAVE_BOOST_PROGRAM_OPTIONS,,[define if the Boost::PROGRAM_OPTIONS library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + if test "x$ax_boost_user_program_options_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_program_options*.so* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.so.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.dylib* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.dylib.*$;\1;'` `ls $BOOSTLIBDIR/libboost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + if test "x$link_program_options" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_program_options*.dll* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.dll.*$;\1;'` `ls $BOOSTLIBDIR/boost_program_options*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_program_options.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + fi + else + for ax_lib in $ax_boost_user_program_options_lib boost_program_options-$ax_boost_user_program_options_lib; do + AC_CHECK_LIB($ax_lib, main, + [BOOST_PROGRAM_OPTIONS_LIB="-l$ax_lib"; AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB) link_program_options="yes"; break], + [link_program_options="no"]) + done + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_program_options" != "xyes"; then + AC_MSG_ERROR([Could not link against [$ax_lib] !]) + fi + fi + + ax_lib="" + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +])