From: Michael Friedrich Date: Mon, 13 Oct 2014 16:07:52 +0000 (+0200) Subject: CLI framework: Add support for unrecognized parameters X-Git-Tag: v2.2.0~399 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3513d1f2f948befb21579f244b3bd7b4e5aa44fa;p=icinga2 CLI framework: Add support for unrecognized parameters Required for feature enable command for example. fixes #7371 --- diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp index 5de35067c..a7fcafe9a 100644 --- a/icinga-app/icinga.cpp +++ b/icinga-app/icinga.cpp @@ -121,7 +121,7 @@ int Main(void) LogSeverity logLevel = Logger::GetConsoleLogSeverity(); Logger::SetConsoleLogSeverity(LogWarning); - + Utility::LoadExtensionLibrary("cli"); po::options_description visibleDesc("Global options"); @@ -138,13 +138,14 @@ int Main(void) hiddenDesc.add_options() ("no-stack-rlimit", "used internally, do not specify manually"); - + String cmdname; CLICommand::Ptr command; po::variables_map vm; + std::vector ap; try { - CLICommand::ParseCommand(argc, argv, visibleDesc, hiddenDesc, vm, cmdname, command, autocomplete); + CLICommand::ParseCommand(argc, argv, visibleDesc, hiddenDesc, vm, ap, cmdname, command, autocomplete); } catch (const std::exception& ex) { std::ostringstream msgbuf; msgbuf << "Error while parsing command-line options: " << ex.what(); @@ -158,7 +159,7 @@ int Main(void) ConfigCompilerContext::GetInstance()->Reset(); ConfigCompiler::CompileFile(initconfig); } - + if (vm.count("define")) { BOOST_FOREACH(const String& define, vm["define"].as >()) { String key, value; @@ -201,7 +202,7 @@ int Main(void) Log(LogCritical, "cli", msgbuf.str()); return EXIT_FAILURE; } - + if (setgid(gr->gr_gid) < 0) { std::ostringstream msgbuf; msgbuf << "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; @@ -237,7 +238,7 @@ int Main(void) Log(LogCritical, "cli", msgbuf.str()); return EXIT_FAILURE; } - + if (setuid(pw->pw_uid) < 0) { std::ostringstream msgbuf; msgbuf << "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; @@ -258,13 +259,13 @@ int Main(void) ConfigCompiler::AddIncludeSearchDir(includePath); } } - + Logger::SetConsoleLogSeverity(logLevel); if (!autocomplete) { if (vm.count("log-level")) { String severity = vm["log-level"].as(); - + LogSeverity logLevel = LogInformation; try { logLevel = Logger::StringToSeverity(severity); @@ -272,42 +273,42 @@ int Main(void) /* use the default */ Log(LogWarning, "icinga", "Invalid log level set. Using default 'information'."); } - + Logger::SetConsoleLogSeverity(logLevel); } - + if (vm.count("library")) { BOOST_FOREACH(const String& libraryName, vm["library"].as >()) { (void)Utility::LoadExtensionLibrary(libraryName); } } - + if (!command || vm.count("help") || vm.count("version")) { String appName = Utility::BaseName(Application::GetArgV()[0]); - + if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-") appName = appName.SubStr(3, appName.GetLength() - 3); - + std::cout << appName << " " << "- The Icinga 2 network monitoring daemon."; - + if (!command || vm.count("help")) { std::cout << std::endl << std::endl << "Usage:" << std::endl << " " << argv[0] << " "; - + if (cmdname.IsEmpty()) std::cout << ""; else std::cout << cmdname; - + std::cout << " []"; - + if (command) { std::cout << std::endl << std::endl << command->GetDescription(); } } - + if (vm.count("version")) { std::cout << " (Version: " << Application::GetVersion() << ")"; std::cout << std::endl @@ -316,24 +317,24 @@ int Main(void) << "This is free software: you are free to change and redistribute it." << std::endl << "There is NO WARRANTY, to the extent permitted by law."; } - + std::cout << std::endl; - + if (vm.count("version")) { std::cout << std::endl; - + Application::DisplayInfoMessage(true); - + return EXIT_SUCCESS; } } - + if (!command || vm.count("help")) { if (!command) { std::cout << std::endl; CLICommand::ShowCommands(argc, argv, NULL); } - + std::cout << std::endl << visibleDesc << std::endl << "Report bugs at " << std::endl @@ -348,7 +349,7 @@ int Main(void) CLICommand::ShowCommands(argc, argv, &visibleDesc, &hiddenDesc, true, autoindex); rc = 0; } else if (command) - rc = command->Run(vm); + rc = command->Run(vm, ap); #ifndef _DEBUG Application::Exit(rc); diff --git a/lib/base/clicommand.cpp b/lib/base/clicommand.cpp index ea227baec..b4dec7129 100644 --- a/lib/base/clicommand.cpp +++ b/lib/base/clicommand.cpp @@ -65,7 +65,8 @@ RegisterCLICommandHelper::RegisterCLICommandHelper(const String& name, const CLI bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& visibleDesc, po::options_description& hiddenDesc, po::variables_map& vm, - String& cmdname, CLICommand::Ptr& command, bool autocomplete) + std::vector& ap, String& cmdname, + CLICommand::Ptr& command, bool autocomplete) { boost::mutex::scoped_lock lock(l_RegistryMutex); @@ -85,10 +86,10 @@ bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& vi if (vname[i] != argv[k]) break; - + if (i >= best_match.size()) best_match.push_back(vname[i]); - + if (i == vname.size() - 1) { cmdname = boost::algorithm::join(vname, " "); command = kv.second; @@ -97,13 +98,13 @@ bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& vi } } } - + found_command: lock.unlock(); po::options_description vdesc("Command options"); - - if (command) + + if (command) command->InitParameters(vdesc, hiddenDesc); visibleDesc.add(vdesc); @@ -115,7 +116,13 @@ found_command: adesc.add(visibleDesc); adesc.add(hiddenDesc); - po::store(po::parse_command_line(argc - arg_end, argv + arg_end, adesc), vm); + po::parsed_options parsed = po::command_line_parser(argc - arg_end, argv + arg_end). + options(adesc).allow_unregistered().run(); + + ap = collect_unrecognized(parsed.options, + po::include_positional); + + po::store(parsed, vm); po::notify(vm); return true; @@ -136,7 +143,7 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi const std::vector& vname = kv.first; arg_begin = 0; - + for (int i = 0, k = 1; i < vname.size() && k < argc; i++, k++) { if (strcmp(argv[k], "--no-stack-rlimit") == 0 || strcmp(argv[k], "--autocomplete") == 0) { i--; @@ -146,11 +153,11 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi if (vname[i] != argv[k]) break; - + if (i >= best_match.size()) { best_match.push_back(vname[i]); } - + if (i == vname.size() - 1) { command = kv.second; break; @@ -171,16 +178,16 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi if (vname.size() < best_match.size()) continue; - + bool match = true; - + for (int i = 0; i < best_match.size(); i++) { if (vname[i] != best_match[i]) { match = false; break; } } - + if (!match) continue; @@ -199,10 +206,10 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi if (command && autocomplete) { po::options_description vdesc("Command options"); - + if (command) command->InitParameters(vdesc, *hiddenDesc); - + visibleDesc->add(vdesc); BOOST_FOREACH(const shared_ptr& odesc, visibleDesc->options()) { diff --git a/lib/base/clicommand.hpp b/lib/base/clicommand.hpp index dbe3ef97c..1ba306065 100644 --- a/lib/base/clicommand.hpp +++ b/lib/base/clicommand.hpp @@ -42,7 +42,7 @@ public: virtual String GetDescription(void) const = 0; virtual String GetShortDescription(void) const = 0; virtual void InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const = 0; - virtual int Run(const boost::program_options::variables_map& vm) const = 0; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const = 0; static CLICommand::Ptr GetByName(const std::vector& name); static void Register(const std::vector& name, const CLICommand::Ptr& command); @@ -50,7 +50,8 @@ public: static bool ParseCommand(int argc, char **argv, boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc, - boost::program_options::variables_map& vm, String& cmdname, CLICommand::Ptr& command, bool autocomplete); + boost::program_options::variables_map& vm, std::vector& ap, String& cmdname, + CLICommand::Ptr& command, bool autocomplete); static void ShowCommands(int argc, char **argv, boost::program_options::options_description *visibleDesc = NULL, boost::program_options::options_description *hiddenDesc = NULL, bool autocomplete = false, int autoindex = -1); }; diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index 00200253e..24286968a 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -299,7 +299,7 @@ void DaemonCommand::InitParameters(boost::program_options::options_description& * * @returns An exit status. */ -int DaemonCommand::Run(const po::variables_map& vm) const +int DaemonCommand::Run(const po::variables_map& vm, const std::vector& ap) const { ScriptVariable::Set("UseVfork", true, false, true); @@ -358,7 +358,7 @@ int DaemonCommand::Run(const po::variables_map& vm) const SetDaemonIO(errorLog); Logger::DisableConsoleLog(); } - + #ifndef _WIN32 struct sigaction sa; memset(&sa, 0, sizeof(sa)); diff --git a/lib/cli/daemoncommand.hpp b/lib/cli/daemoncommand.hpp index 713587fd0..6de3bf34f 100644 --- a/lib/cli/daemoncommand.hpp +++ b/lib/cli/daemoncommand.hpp @@ -36,12 +36,12 @@ class DaemonCommand : public CLICommand { public: DECLARE_PTR_TYPEDEFS(DaemonCommand); - + virtual String GetDescription(void) const; virtual String GetShortDescription(void) const; virtual void InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const; - virtual int Run(const boost::program_options::variables_map& vm) const; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; }; } diff --git a/lib/cli/pkinewcacommand.cpp b/lib/cli/pkinewcacommand.cpp index 8c295cff5..19b4aeed5 100644 --- a/lib/cli/pkinewcacommand.cpp +++ b/lib/cli/pkinewcacommand.cpp @@ -49,7 +49,7 @@ void PKINewCACommand::InitParameters(boost::program_options::options_description * * @returns An exit status. */ -int PKINewCACommand::Run(const boost::program_options::variables_map& vm) const +int PKINewCACommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { String cadir = Application::GetLocalStateDir() + "/lib/icinga2/ca"; @@ -57,18 +57,18 @@ int PKINewCACommand::Run(const boost::program_options::variables_map& vm) const Log(LogCritical, "base", "CA directory '" + cadir + "' already exists."); return 1; } - + if (!Utility::MkDirP(cadir, 0700)) { Log(LogCritical, "base", "Could not create CA directory '" + cadir + "'."); return 1; } - + MakeX509CSR("Icinga CA", cadir + "/ca.key", String(), cadir + "/ca.crt", true); - + String serialpath = cadir + "/serial.txt"; Log(LogInformation, "cli", "Initializing serial file in '" + serialpath + "'."); - + std::ofstream fp; fp.open(serialpath.CStr()); fp << "01"; diff --git a/lib/cli/pkinewcacommand.hpp b/lib/cli/pkinewcacommand.hpp index 150852579..b2434ddea 100644 --- a/lib/cli/pkinewcacommand.hpp +++ b/lib/cli/pkinewcacommand.hpp @@ -35,12 +35,12 @@ class PKINewCACommand : public CLICommand { public: DECLARE_PTR_TYPEDEFS(PKINewCACommand); - + virtual String GetDescription(void) const; virtual String GetShortDescription(void) const; virtual void InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const; - virtual int Run(const boost::program_options::variables_map& vm) const; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; }; diff --git a/lib/cli/pkinewcertcommand.cpp b/lib/cli/pkinewcertcommand.cpp index 10c29ff1d..4c36266f8 100644 --- a/lib/cli/pkinewcertcommand.cpp +++ b/lib/cli/pkinewcertcommand.cpp @@ -52,26 +52,26 @@ void PKINewCertCommand::InitParameters(boost::program_options::options_descripti * * @returns An exit status. */ -int PKINewCertCommand::Run(const boost::program_options::variables_map& vm) const +int PKINewCertCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { if (!vm.count("cn")) { Log(LogCritical, "cli", "Common name (--cn) must be specified."); return 1; } - + if (!vm.count("keyfile")) { Log(LogCritical, "cli", "Key file path (--keyfile) must be specified."); return 1; } String csrfile, certfile; - + if (vm.count("csrfile")) csrfile = vm["csrfile"].as(); if (vm.count("certfile")) certfile = vm["certfile"].as(); - + MakeX509CSR(vm["cn"].as(), vm["keyfile"].as(), csrfile, certfile); return 0; diff --git a/lib/cli/pkinewcertcommand.hpp b/lib/cli/pkinewcertcommand.hpp index 0a463e7bc..051d085ce 100644 --- a/lib/cli/pkinewcertcommand.hpp +++ b/lib/cli/pkinewcertcommand.hpp @@ -35,12 +35,12 @@ class PKINewCertCommand : public CLICommand { public: DECLARE_PTR_TYPEDEFS(PKINewCertCommand); - + virtual String GetDescription(void) const; virtual String GetShortDescription(void) const; virtual void InitParameters(boost::program_options::options_description& visibleDesc, boost::program_options::options_description& hiddenDesc) const; - virtual int Run(const boost::program_options::variables_map& vm) const; + virtual int Run(const boost::program_options::variables_map& vm, const std::vector& ap) const; };