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();
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<std::string> 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<std::vector<std::string> >()) {
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<std::string>();
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<std::vector<std::string> >()) {
(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 << "<command>";
else
std::cout << cmdname;
std::cout << " [<arguments>]";
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 <https://dev.icinga.org/>" << 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);

View File

@ -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<std::string>& 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<String>& 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<po::option_description>& odesc, visibleDesc->options()) {

View File

@ -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<std::string>& ap) const = 0;
static CLICommand::Ptr GetByName(const std::vector<String>& name);
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,
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,
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.
*/
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);
@ -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));

View File

@ -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<std::string>& ap) const;
};
}

View File

@ -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<std::string>& 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";

View File

@ -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<std::string>& ap) const;
};

View File

@ -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<std::string>& 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<std::string>();
if (vm.count("certfile"))
certfile = vm["certfile"].as<std::string>();
MakeX509CSR(vm["cn"].as<std::string>(), vm["keyfile"].as<std::string>(), csrfile, certfile);
return 0;

View File

@ -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<std::string>& ap) const;
};