Use boost::program_options to parse arguments.

Fixes #3536
Fixes #3184
This commit is contained in:
Gunnar Beutner 2013-02-02 23:22:27 +01:00
parent f2e17bdd09
commit dbf762e771
11 changed files with 222 additions and 95 deletions

View File

@ -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)

View File

@ -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 \

View File

@ -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 <config-file> ...";
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<vector<String> >(), "load a library")
("include,I", po::value<vector<String> >(), "add include search directory")
("config,c", po::value<vector<String> >(), "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<vector<String> >()) {
Utility::LoadIcingaLibrary(libraryName, false);
}
}
ConfigCompiler::AddIncludeSearchDir(Application::GetPkgDataDir());
if (vm.count("include")) {
BOOST_FOREACH(const String& includePath, vm["include"].as<vector<String> >()) {
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<ConfigItem::Ptr> items;
vector<ConfigType::Ptr> types;
/* load config files */
BOOST_FOREACH(const String& configPath, vm["config"].as<vector<String> >()) {
String configFile = argv[2];
vector<ConfigItem::Ptr> items;
vector<ConfigType::Ptr> 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...");
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", "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...");
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();
}

View File

@ -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();

View File

@ -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<String>& 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);

View File

@ -137,6 +137,7 @@ using std::type_info;
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/program_options.hpp>
using boost::shared_ptr;
using boost::weak_ptr;

View File

@ -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);

View File

@ -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);
}

View File

@ -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<String>& 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<String>::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<String>& 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());

View File

@ -36,7 +36,7 @@ public:
IcingaApplication(const Dictionary::Ptr& serializedUpdate);
int Main(const vector<String>& args);
int Main(void);
static IcingaApplication::Ptr GetInstance(void);

View File

@ -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 <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_PROGRAM_OPTIONS_LIB)
#
# And sets:
#
# HAVE_BOOST_PROGRAM_OPTIONS
#
# LICENSE
#
# Copyright (c) 2009 Thomas Porschberg <thomas@randspringer.de>
#
# 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.hpp>
]],
[[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
])