2014-10-06 14:21:18 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* Icinga 2 *
|
|
|
|
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or *
|
|
|
|
* modify it under the terms of the GNU General Public License *
|
|
|
|
* as published by the Free Software Foundation; either version 2 *
|
|
|
|
* of the License, or (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program; if not, write to the Free Software Foundation *
|
|
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
#include "base/clicommand.hpp"
|
|
|
|
#include "base/logger_fwd.hpp"
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
|
|
|
#include <boost/algorithm/string/join.hpp>
|
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
#include <boost/program_options.hpp>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
using namespace icinga;
|
|
|
|
namespace po = boost::program_options;
|
|
|
|
|
|
|
|
boost::mutex l_RegistryMutex;
|
|
|
|
std::map<std::vector<String>, CLICommand::Ptr> l_Registry;
|
|
|
|
|
|
|
|
CLICommand::Ptr CLICommand::GetByName(const std::vector<String>& name)
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
|
|
|
|
|
|
|
std::map<std::vector<String>, CLICommand::Ptr>::const_iterator it = l_Registry.find(name);
|
|
|
|
|
|
|
|
if (it == l_Registry.end())
|
|
|
|
return CLICommand::Ptr();
|
|
|
|
|
|
|
|
return it->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLICommand::Register(const std::vector<String>& name, const CLICommand::Ptr& function)
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
|
|
|
l_Registry[name] = function;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CLICommand::Unregister(const std::vector<String>& name)
|
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
|
|
|
l_Registry.erase(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
RegisterCLICommandHelper::RegisterCLICommandHelper(const String& name, const CLICommand::Ptr& command)
|
|
|
|
{
|
|
|
|
std::vector<String> vname;
|
|
|
|
boost::algorithm::split(vname, name, boost::is_any_of("/"));
|
|
|
|
CLICommand::Register(vname, command);
|
|
|
|
}
|
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
bool CLICommand::ParseCommand(int argc, char **argv, po::options_description& visibleDesc,
|
|
|
|
po::options_description& hiddenDesc, po::variables_map& vm,
|
2014-10-10 10:07:56 +02:00
|
|
|
String& cmdname, CLICommand::Ptr& command, bool autocomplete)
|
2014-10-06 14:21:18 +02:00
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
|
|
|
|
|
|
|
typedef std::map<std::vector<String>, CLICommand::Ptr>::value_type CLIKeyValue;
|
|
|
|
|
|
|
|
std::vector<String> best_match;
|
|
|
|
int arg_end = 1;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const CLIKeyValue& kv, l_Registry) {
|
|
|
|
const std::vector<String>& vname = kv.first;
|
|
|
|
|
|
|
|
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--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
arg_end = k;
|
|
|
|
goto found_command;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
found_command:
|
|
|
|
lock.unlock();
|
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
po::options_description vdesc("Command options");
|
2014-10-06 14:21:18 +02:00
|
|
|
|
|
|
|
if (command)
|
2014-10-10 11:08:24 +02:00
|
|
|
command->InitParameters(vdesc, hiddenDesc);
|
2014-10-10 10:07:56 +02:00
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
visibleDesc.add(vdesc);
|
2014-10-10 10:07:56 +02:00
|
|
|
|
|
|
|
if (autocomplete)
|
|
|
|
return true;
|
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
po::options_description adesc;
|
|
|
|
adesc.add(visibleDesc);
|
|
|
|
adesc.add(hiddenDesc);
|
|
|
|
|
|
|
|
po::store(po::parse_command_line(argc - arg_end, argv + arg_end, adesc), vm);
|
2014-10-06 14:21:18 +02:00
|
|
|
po::notify(vm);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
void CLICommand::ShowCommands(int argc, char **argv, po::options_description *visibleDesc,
|
|
|
|
po::options_description *hiddenDesc, bool autocomplete, int autoindex)
|
2014-10-06 14:21:18 +02:00
|
|
|
{
|
|
|
|
boost::mutex::scoped_lock lock(l_RegistryMutex);
|
|
|
|
|
|
|
|
typedef std::map<std::vector<String>, CLICommand::Ptr>::value_type CLIKeyValue;
|
|
|
|
|
|
|
|
std::vector<String> best_match;
|
|
|
|
int arg_begin = 0;
|
|
|
|
CLICommand::Ptr command;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const CLIKeyValue& kv, l_Registry) {
|
|
|
|
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--;
|
|
|
|
arg_begin++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-10 10:07:56 +02:00
|
|
|
String aword;
|
|
|
|
|
|
|
|
if (autocomplete) {
|
|
|
|
if (autoindex < argc)
|
|
|
|
aword = argv[autoindex];
|
|
|
|
} else
|
2014-10-06 14:21:18 +02:00
|
|
|
std::cout << "Supported commands: " << std::endl;
|
2014-10-10 10:07:56 +02:00
|
|
|
|
2014-10-06 14:21:18 +02:00
|
|
|
BOOST_FOREACH(const CLIKeyValue& kv, l_Registry) {
|
|
|
|
const std::vector<String>& vname = kv.first;
|
2014-10-10 10:07:56 +02:00
|
|
|
|
2014-10-06 14:21:18 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
if (autocomplete) {
|
2014-10-10 10:07:56 +02:00
|
|
|
String cname;
|
|
|
|
|
|
|
|
if (autoindex - 1 < vname.size()) {
|
|
|
|
cname = vname[autoindex - 1];
|
|
|
|
|
|
|
|
if (cname.Find(aword) == 0)
|
|
|
|
std::cout << cname << "\n";
|
2014-10-06 14:21:18 +02:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
std::cout << " * " << boost::algorithm::join(vname, " ") << " (" << kv.second->GetShortDescription() << ")" << std::endl;
|
|
|
|
}
|
2014-10-10 10:07:56 +02:00
|
|
|
|
2014-10-06 14:21:18 +02:00
|
|
|
if (command && autocomplete) {
|
2014-10-10 11:08:24 +02:00
|
|
|
po::options_description vdesc("Command options");
|
2014-10-06 14:21:18 +02:00
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
if (command)
|
|
|
|
command->InitParameters(vdesc, *hiddenDesc);
|
2014-10-06 14:21:18 +02:00
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
visibleDesc->add(vdesc);
|
2014-10-06 14:21:18 +02:00
|
|
|
|
2014-10-10 11:08:24 +02:00
|
|
|
BOOST_FOREACH(const shared_ptr<po::option_description>& odesc, visibleDesc->options()) {
|
2014-10-06 14:21:18 +02:00
|
|
|
String cname = "--" + odesc->long_name();
|
2014-10-10 10:07:56 +02:00
|
|
|
|
|
|
|
if (cname.Find(aword) == 0)
|
|
|
|
std::cout << cname << "\n";
|
2014-10-06 14:21:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|