CLI framework: Add support for unrecognized parameters

Required for feature enable command for example.

fixes #7371
This commit is contained in:
Michael Friedrich 2014-10-13 18:07:52 +02:00
parent 15bd96aa9a
commit 3513d1f2f9
9 changed files with 68 additions and 59 deletions

View File

@ -121,7 +121,7 @@ int Main(void)
LogSeverity logLevel = Logger::GetConsoleLogSeverity(); LogSeverity logLevel = Logger::GetConsoleLogSeverity();
Logger::SetConsoleLogSeverity(LogWarning); Logger::SetConsoleLogSeverity(LogWarning);
Utility::LoadExtensionLibrary("cli"); Utility::LoadExtensionLibrary("cli");
po::options_description visibleDesc("Global options"); po::options_description visibleDesc("Global options");
@ -138,13 +138,14 @@ int Main(void)
hiddenDesc.add_options() hiddenDesc.add_options()
("no-stack-rlimit", "used internally, do not specify manually"); ("no-stack-rlimit", "used internally, do not specify manually");
String cmdname; String cmdname;
CLICommand::Ptr command; CLICommand::Ptr command;
po::variables_map vm; po::variables_map vm;
std::vector<std::string> ap;
try { 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) { } catch (const std::exception& ex) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
msgbuf << "Error while parsing command-line options: " << ex.what(); msgbuf << "Error while parsing command-line options: " << ex.what();
@ -158,7 +159,7 @@ int Main(void)
ConfigCompilerContext::GetInstance()->Reset(); ConfigCompilerContext::GetInstance()->Reset();
ConfigCompiler::CompileFile(initconfig); ConfigCompiler::CompileFile(initconfig);
} }
if (vm.count("define")) { if (vm.count("define")) {
BOOST_FOREACH(const String& define, vm["define"].as<std::vector<std::string> >()) { BOOST_FOREACH(const String& define, vm["define"].as<std::vector<std::string> >()) {
String key, value; String key, value;
@ -201,7 +202,7 @@ int Main(void)
Log(LogCritical, "cli", msgbuf.str()); Log(LogCritical, "cli", msgbuf.str());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (setgid(gr->gr_gid) < 0) { if (setgid(gr->gr_gid) < 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
msgbuf << "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; msgbuf << "setgid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
@ -237,7 +238,7 @@ int Main(void)
Log(LogCritical, "cli", msgbuf.str()); Log(LogCritical, "cli", msgbuf.str());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (setuid(pw->pw_uid) < 0) { if (setuid(pw->pw_uid) < 0) {
std::ostringstream msgbuf; std::ostringstream msgbuf;
msgbuf << "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\""; msgbuf << "setuid() failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"";
@ -258,13 +259,13 @@ int Main(void)
ConfigCompiler::AddIncludeSearchDir(includePath); ConfigCompiler::AddIncludeSearchDir(includePath);
} }
} }
Logger::SetConsoleLogSeverity(logLevel); Logger::SetConsoleLogSeverity(logLevel);
if (!autocomplete) { if (!autocomplete) {
if (vm.count("log-level")) { if (vm.count("log-level")) {
String severity = vm["log-level"].as<std::string>(); String severity = vm["log-level"].as<std::string>();
LogSeverity logLevel = LogInformation; LogSeverity logLevel = LogInformation;
try { try {
logLevel = Logger::StringToSeverity(severity); logLevel = Logger::StringToSeverity(severity);
@ -272,42 +273,42 @@ int Main(void)
/* use the default */ /* use the default */
Log(LogWarning, "icinga", "Invalid log level set. Using default 'information'."); Log(LogWarning, "icinga", "Invalid log level set. Using default 'information'.");
} }
Logger::SetConsoleLogSeverity(logLevel); Logger::SetConsoleLogSeverity(logLevel);
} }
if (vm.count("library")) { if (vm.count("library")) {
BOOST_FOREACH(const String& libraryName, vm["library"].as<std::vector<std::string> >()) { BOOST_FOREACH(const String& libraryName, vm["library"].as<std::vector<std::string> >()) {
(void)Utility::LoadExtensionLibrary(libraryName); (void)Utility::LoadExtensionLibrary(libraryName);
} }
} }
if (!command || vm.count("help") || vm.count("version")) { if (!command || vm.count("help") || vm.count("version")) {
String appName = Utility::BaseName(Application::GetArgV()[0]); String appName = Utility::BaseName(Application::GetArgV()[0]);
if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-") if (appName.GetLength() > 3 && appName.SubStr(0, 3) == "lt-")
appName = appName.SubStr(3, appName.GetLength() - 3); appName = appName.SubStr(3, appName.GetLength() - 3);
std::cout << appName << " " << "- The Icinga 2 network monitoring daemon."; std::cout << appName << " " << "- The Icinga 2 network monitoring daemon.";
if (!command || vm.count("help")) { if (!command || vm.count("help")) {
std::cout << std::endl << std::endl std::cout << std::endl << std::endl
<< "Usage:" << std::endl << "Usage:" << std::endl
<< " " << argv[0] << " "; << " " << argv[0] << " ";
if (cmdname.IsEmpty()) if (cmdname.IsEmpty())
std::cout << "<command>"; std::cout << "<command>";
else else
std::cout << cmdname; std::cout << cmdname;
std::cout << " [<arguments>]"; std::cout << " [<arguments>]";
if (command) { if (command) {
std::cout << std::endl << std::endl std::cout << std::endl << std::endl
<< command->GetDescription(); << command->GetDescription();
} }
} }
if (vm.count("version")) { if (vm.count("version")) {
std::cout << " (Version: " << Application::GetVersion() << ")"; std::cout << " (Version: " << Application::GetVersion() << ")";
std::cout << std::endl 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 << "This is free software: you are free to change and redistribute it." << std::endl
<< "There is NO WARRANTY, to the extent permitted by law."; << "There is NO WARRANTY, to the extent permitted by law.";
} }
std::cout << std::endl; std::cout << std::endl;
if (vm.count("version")) { if (vm.count("version")) {
std::cout << std::endl; std::cout << std::endl;
Application::DisplayInfoMessage(true); Application::DisplayInfoMessage(true);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
} }
if (!command || vm.count("help")) { if (!command || vm.count("help")) {
if (!command) { if (!command) {
std::cout << std::endl; std::cout << std::endl;
CLICommand::ShowCommands(argc, argv, NULL); CLICommand::ShowCommands(argc, argv, NULL);
} }
std::cout << std::endl std::cout << std::endl
<< visibleDesc << std::endl << visibleDesc << std::endl
<< "Report bugs at <https://dev.icinga.org/>" << std::endl << "Report bugs at <https://dev.icinga.org/>" << std::endl
@ -348,7 +349,7 @@ int Main(void)
CLICommand::ShowCommands(argc, argv, &visibleDesc, &hiddenDesc, true, autoindex); CLICommand::ShowCommands(argc, argv, &visibleDesc, &hiddenDesc, true, autoindex);
rc = 0; rc = 0;
} else if (command) } else if (command)
rc = command->Run(vm); rc = command->Run(vm, ap);
#ifndef _DEBUG #ifndef _DEBUG
Application::Exit(rc); Application::Exit(rc);

View File

@ -65,7 +65,8 @@ RegisterCLICommandHelper::RegisterCLICommandHelper(const String& name, const CLI
bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& visibleDesc, bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& visibleDesc,
po::options_description& hiddenDesc, po::variables_map& vm, po::options_description& hiddenDesc, po::variables_map& vm,
String& cmdname, CLICommand::Ptr& command, bool autocomplete) std::vector<std::string>& ap, String& cmdname,
CLICommand::Ptr& command, bool autocomplete)
{ {
boost::mutex::scoped_lock lock(l_RegistryMutex); 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]) if (vname[i] != argv[k])
break; break;
if (i >= best_match.size()) if (i >= best_match.size())
best_match.push_back(vname[i]); best_match.push_back(vname[i]);
if (i == vname.size() - 1) { if (i == vname.size() - 1) {
cmdname = boost::algorithm::join(vname, " "); cmdname = boost::algorithm::join(vname, " ");
command = kv.second; command = kv.second;
@ -97,13 +98,13 @@ bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& vi
} }
} }
} }
found_command: found_command:
lock.unlock(); lock.unlock();
po::options_description vdesc("Command options"); po::options_description vdesc("Command options");
if (command) if (command)
command->InitParameters(vdesc, hiddenDesc); command->InitParameters(vdesc, hiddenDesc);
visibleDesc.add(vdesc); visibleDesc.add(vdesc);
@ -115,7 +116,13 @@ found_command:
adesc.add(visibleDesc); adesc.add(visibleDesc);
adesc.add(hiddenDesc); 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); po::notify(vm);
return true; return true;
@ -136,7 +143,7 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi
const std::vector<String>& vname = kv.first; const std::vector<String>& vname = kv.first;
arg_begin = 0; arg_begin = 0;
for (int i = 0, k = 1; i < vname.size() && k < argc; i++, k++) { 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) { if (strcmp(argv[k], "--no-stack-rlimit") == 0 || strcmp(argv[k], "--autocomplete") == 0) {
i--; i--;
@ -146,11 +153,11 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi
if (vname[i] != argv[k]) if (vname[i] != argv[k])
break; break;
if (i >= best_match.size()) { if (i >= best_match.size()) {
best_match.push_back(vname[i]); best_match.push_back(vname[i]);
} }
if (i == vname.size() - 1) { if (i == vname.size() - 1) {
command = kv.second; command = kv.second;
break; break;
@ -171,16 +178,16 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi
if (vname.size() < best_match.size()) if (vname.size() < best_match.size())
continue; continue;
bool match = true; bool match = true;
for (int i = 0; i < best_match.size(); i++) { for (int i = 0; i < best_match.size(); i++) {
if (vname[i] != best_match[i]) { if (vname[i] != best_match[i]) {
match = false; match = false;
break; break;
} }
} }
if (!match) if (!match)
continue; continue;
@ -199,10 +206,10 @@ void CLICommand::ShowCommands(int argc, char **argv, po::options_description *vi
if (command && autocomplete) { if (command && autocomplete) {
po::options_description vdesc("Command options"); po::options_description vdesc("Command options");
if (command) if (command)
command->InitParameters(vdesc, *hiddenDesc); command->InitParameters(vdesc, *hiddenDesc);
visibleDesc->add(vdesc); visibleDesc->add(vdesc);
BOOST_FOREACH(const shared_ptr<po::option_description>& odesc, visibleDesc->options()) { BOOST_FOREACH(const shared_ptr<po::option_description>& odesc, visibleDesc->options()) {

View File

@ -42,7 +42,7 @@ public:
virtual String GetDescription(void) const = 0; virtual String GetDescription(void) const = 0;
virtual String GetShortDescription(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 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<std::string>& ap) const = 0;
static CLICommand::Ptr GetByName(const std::vector<String>& name); static CLICommand::Ptr GetByName(const std::vector<String>& name);
static void Register(const std::vector<String>& name, const CLICommand::Ptr& command); static void Register(const std::vector<String>& name, const CLICommand::Ptr& command);
@ -50,7 +50,8 @@ public:
static bool ParseCommand(int argc, char **argv, boost::program_options::options_description& visibleDesc, static bool ParseCommand(int argc, char **argv, boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc, 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<std::string>& ap, String& cmdname,
CLICommand::Ptr& command, bool autocomplete);
static void ShowCommands(int argc, char **argv, boost::program_options::options_description *visibleDesc = NULL, 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); boost::program_options::options_description *hiddenDesc = NULL, bool autocomplete = false, int autoindex = -1);
}; };

View File

@ -299,7 +299,7 @@ void DaemonCommand::InitParameters(boost::program_options::options_description&
* *
* @returns An exit status. * @returns An exit status.
*/ */
int DaemonCommand::Run(const po::variables_map& vm) const int DaemonCommand::Run(const po::variables_map& vm, const std::vector<std::string>& ap) const
{ {
ScriptVariable::Set("UseVfork", true, false, true); ScriptVariable::Set("UseVfork", true, false, true);
@ -358,7 +358,7 @@ int DaemonCommand::Run(const po::variables_map& vm) const
SetDaemonIO(errorLog); SetDaemonIO(errorLog);
Logger::DisableConsoleLog(); Logger::DisableConsoleLog();
} }
#ifndef _WIN32 #ifndef _WIN32
struct sigaction sa; struct sigaction sa;
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));

View File

@ -36,12 +36,12 @@ class DaemonCommand : public CLICommand
{ {
public: public:
DECLARE_PTR_TYPEDEFS(DaemonCommand); DECLARE_PTR_TYPEDEFS(DaemonCommand);
virtual String GetDescription(void) const; virtual String GetDescription(void) const;
virtual String GetShortDescription(void) const; virtual String GetShortDescription(void) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc, virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const; 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<std::string>& ap) const;
}; };
} }

View File

@ -49,7 +49,7 @@ void PKINewCACommand::InitParameters(boost::program_options::options_description
* *
* @returns An exit status. * @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<std::string>& ap) const
{ {
String cadir = Application::GetLocalStateDir() + "/lib/icinga2/ca"; 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."); Log(LogCritical, "base", "CA directory '" + cadir + "' already exists.");
return 1; return 1;
} }
if (!Utility::MkDirP(cadir, 0700)) { if (!Utility::MkDirP(cadir, 0700)) {
Log(LogCritical, "base", "Could not create CA directory '" + cadir + "'."); Log(LogCritical, "base", "Could not create CA directory '" + cadir + "'.");
return 1; return 1;
} }
MakeX509CSR("Icinga CA", cadir + "/ca.key", String(), cadir + "/ca.crt", true); MakeX509CSR("Icinga CA", cadir + "/ca.key", String(), cadir + "/ca.crt", true);
String serialpath = cadir + "/serial.txt"; String serialpath = cadir + "/serial.txt";
Log(LogInformation, "cli", "Initializing serial file in '" + serialpath + "'."); Log(LogInformation, "cli", "Initializing serial file in '" + serialpath + "'.");
std::ofstream fp; std::ofstream fp;
fp.open(serialpath.CStr()); fp.open(serialpath.CStr());
fp << "01"; fp << "01";

View File

@ -35,12 +35,12 @@ class PKINewCACommand : public CLICommand
{ {
public: public:
DECLARE_PTR_TYPEDEFS(PKINewCACommand); DECLARE_PTR_TYPEDEFS(PKINewCACommand);
virtual String GetDescription(void) const; virtual String GetDescription(void) const;
virtual String GetShortDescription(void) const; virtual String GetShortDescription(void) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc, virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const; 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<std::string>& ap) const;
}; };

View File

@ -52,26 +52,26 @@ void PKINewCertCommand::InitParameters(boost::program_options::options_descripti
* *
* @returns An exit status. * @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<std::string>& ap) const
{ {
if (!vm.count("cn")) { if (!vm.count("cn")) {
Log(LogCritical, "cli", "Common name (--cn) must be specified."); Log(LogCritical, "cli", "Common name (--cn) must be specified.");
return 1; return 1;
} }
if (!vm.count("keyfile")) { if (!vm.count("keyfile")) {
Log(LogCritical, "cli", "Key file path (--keyfile) must be specified."); Log(LogCritical, "cli", "Key file path (--keyfile) must be specified.");
return 1; return 1;
} }
String csrfile, certfile; String csrfile, certfile;
if (vm.count("csrfile")) if (vm.count("csrfile"))
csrfile = vm["csrfile"].as<std::string>(); csrfile = vm["csrfile"].as<std::string>();
if (vm.count("certfile")) if (vm.count("certfile"))
certfile = vm["certfile"].as<std::string>(); certfile = vm["certfile"].as<std::string>();
MakeX509CSR(vm["cn"].as<std::string>(), vm["keyfile"].as<std::string>(), csrfile, certfile); MakeX509CSR(vm["cn"].as<std::string>(), vm["keyfile"].as<std::string>(), csrfile, certfile);
return 0; return 0;

View File

@ -35,12 +35,12 @@ class PKINewCertCommand : public CLICommand
{ {
public: public:
DECLARE_PTR_TYPEDEFS(PKINewCertCommand); DECLARE_PTR_TYPEDEFS(PKINewCertCommand);
virtual String GetDescription(void) const; virtual String GetDescription(void) const;
virtual String GetShortDescription(void) const; virtual String GetShortDescription(void) const;
virtual void InitParameters(boost::program_options::options_description& visibleDesc, virtual void InitParameters(boost::program_options::options_description& visibleDesc,
boost::program_options::options_description& hiddenDesc) const; 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<std::string>& ap) const;
}; };