2014-10-18 19:31:52 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* Icinga 2 *
|
2017-01-10 15:54:22 +01:00
|
|
|
* Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
|
2014-10-18 19:31:52 +02:00
|
|
|
* *
|
|
|
|
* 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 "cli/featureutility.hpp"
|
2014-10-19 14:21:12 +02:00
|
|
|
#include "base/logger.hpp"
|
2014-10-21 18:35:43 +02:00
|
|
|
#include "base/console.hpp"
|
2014-10-18 19:31:52 +02:00
|
|
|
#include "base/application.hpp"
|
2014-10-21 18:35:43 +02:00
|
|
|
#include <boost/algorithm/string/join.hpp>
|
2014-10-18 19:31:52 +02:00
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
2014-10-22 08:06:06 +02:00
|
|
|
#include <fstream>
|
2014-10-24 07:45:01 +02:00
|
|
|
#include <iostream>
|
2014-10-18 19:31:52 +02:00
|
|
|
|
|
|
|
using namespace icinga;
|
|
|
|
|
2014-10-21 18:12:17 +02:00
|
|
|
String FeatureUtility::GetFeaturesAvailablePath(void)
|
|
|
|
{
|
|
|
|
return Application::GetSysconfDir() + "/icinga2/features-available";
|
|
|
|
}
|
|
|
|
|
|
|
|
String FeatureUtility::GetFeaturesEnabledPath(void)
|
|
|
|
{
|
|
|
|
return Application::GetSysconfDir() + "/icinga2/features-enabled";
|
|
|
|
}
|
|
|
|
|
2014-10-21 18:35:43 +02:00
|
|
|
std::vector<String> FeatureUtility::GetFieldCompletionSuggestions(const String& word, bool enable)
|
2014-10-18 19:31:52 +02:00
|
|
|
{
|
|
|
|
std::vector<String> cache;
|
|
|
|
std::vector<String> suggestions;
|
|
|
|
|
2014-10-21 18:35:43 +02:00
|
|
|
GetFeatures(cache, enable);
|
2014-10-18 19:31:52 +02:00
|
|
|
|
|
|
|
std::sort(cache.begin(), cache.end());
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& suggestion : cache) {
|
2014-10-18 19:31:52 +02:00
|
|
|
if (suggestion.Find(word) == 0)
|
|
|
|
suggestions.push_back(suggestion);
|
|
|
|
}
|
|
|
|
|
|
|
|
return suggestions;
|
|
|
|
}
|
|
|
|
|
2014-10-21 18:35:43 +02:00
|
|
|
int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
|
2014-10-18 19:31:52 +02:00
|
|
|
{
|
2014-10-21 18:35:43 +02:00
|
|
|
String features_available_dir = GetFeaturesAvailablePath();
|
|
|
|
String features_enabled_dir = GetFeaturesEnabledPath();
|
2014-10-18 19:31:52 +02:00
|
|
|
|
2014-10-21 18:35:43 +02:00
|
|
|
if (!Utility::PathExists(features_available_dir) ) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot parse available features. Path '" << features_available_dir << "' does not exist.";
|
2014-10-21 18:35:43 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Utility::PathExists(features_enabled_dir) ) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot enable features. Path '" << features_enabled_dir << "' does not exist.";
|
2014-10-21 18:35:43 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> errors;
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& feature : features) {
|
2014-10-21 18:35:43 +02:00
|
|
|
String source = features_available_dir + "/" + feature + ".conf";
|
|
|
|
|
|
|
|
if (!Utility::PathExists(source) ) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot enable feature '" << feature << "'. Source file '" << source + "' does not exist.";
|
2014-10-21 18:35:43 +02:00
|
|
|
errors.push_back(feature);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
String target = features_enabled_dir + "/" + feature + ".conf";
|
|
|
|
|
|
|
|
if (Utility::PathExists(target) ) {
|
|
|
|
Log(LogWarning, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Feature '" << feature << "' already enabled.";
|
2014-10-21 18:35:43 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
|
2017-12-19 15:50:05 +01:00
|
|
|
<< ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
|
2014-10-21 18:35:43 +02:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
2014-12-20 08:39:12 +01:00
|
|
|
String relativeSource = "../features-available/" + feature + ".conf";
|
|
|
|
|
|
|
|
if (symlink(relativeSource.CStr(), target.CStr()) < 0) {
|
2014-10-21 18:35:43 +02:00
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot enable feature '" << feature << "'. Linking source '" << relativeSource << "' to target file '" << target
|
|
|
|
<< "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\".";
|
2014-10-21 18:35:43 +02:00
|
|
|
errors.push_back(feature);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#else /* _WIN32 */
|
|
|
|
std::ofstream fp;
|
|
|
|
fp.open(target.CStr());
|
|
|
|
fp << "include \"../features-available/" << feature << ".conf\"" << std::endl;
|
|
|
|
fp.close();
|
|
|
|
|
|
|
|
if (fp.fail()) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot enable feature '" << feature << "'. Failed to open file '" << target << "'.";
|
2014-10-21 18:35:43 +02:00
|
|
|
errors.push_back(feature);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!errors.empty()) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot enable feature(s): " << boost::algorithm::join(errors, " ");
|
2014-10-21 18:35:43 +02:00
|
|
|
errors.clear();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
|
|
|
|
{
|
|
|
|
String features_enabled_dir = GetFeaturesEnabledPath();
|
|
|
|
|
|
|
|
if (!Utility::PathExists(features_enabled_dir) ) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot disable features. Path '" << features_enabled_dir << "' does not exist.";
|
2014-10-21 18:35:43 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> errors;
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& feature : features) {
|
2014-10-21 18:35:43 +02:00
|
|
|
String target = features_enabled_dir + "/" + feature + ".conf";
|
|
|
|
|
|
|
|
if (!Utility::PathExists(target) ) {
|
2017-01-11 19:14:46 +01:00
|
|
|
Log(LogWarning, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Feature '" << feature << "' already disabled.";
|
2014-10-21 18:35:43 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlink(target.CStr()) < 0) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot disable feature '" << feature << "'. Unlinking target file '" << target
|
|
|
|
<< "' failed with error code " << errno << ", \"" + Utility::FormatErrorNumber(errno) << "\".";
|
2014-10-21 18:35:43 +02:00
|
|
|
errors.push_back(feature);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
|
2017-12-19 15:50:05 +01:00
|
|
|
<< ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
|
2014-10-21 18:35:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!errors.empty()) {
|
|
|
|
Log(LogCritical, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Cannot disable feature(s): " << boost::algorithm::join(errors, " ");
|
2014-10-21 18:35:43 +02:00
|
|
|
errors.clear();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-22 12:10:32 +01:00
|
|
|
int FeatureUtility::ListFeatures(std::ostream& os)
|
2014-10-21 18:35:43 +02:00
|
|
|
{
|
|
|
|
std::vector<String> disabled_features;
|
|
|
|
std::vector<String> enabled_features;
|
|
|
|
|
|
|
|
if (!FeatureUtility::GetFeatures(disabled_features, true))
|
|
|
|
return 1;
|
|
|
|
|
2015-01-22 12:10:32 +01:00
|
|
|
os << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal)
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::algorithm::join(disabled_features, " ") << "\n";
|
2014-10-21 18:35:43 +02:00
|
|
|
|
|
|
|
if (!FeatureUtility::GetFeatures(enabled_features, false))
|
|
|
|
return 1;
|
|
|
|
|
2015-01-22 12:10:32 +01:00
|
|
|
os << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal)
|
2017-12-19 15:50:05 +01:00
|
|
|
<< boost::algorithm::join(enabled_features, " ") << "\n";
|
2014-10-21 18:35:43 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FeatureUtility::GetFeatures(std::vector<String>& features, bool get_disabled)
|
|
|
|
{
|
|
|
|
/* request all disabled features */
|
|
|
|
if (get_disabled) {
|
|
|
|
/* disable = available-enabled */
|
|
|
|
String available_pattern = GetFeaturesAvailablePath() + "/*.conf";
|
2014-10-18 19:31:52 +02:00
|
|
|
std::vector<String> available;
|
2017-11-23 06:51:48 +01:00
|
|
|
Utility::Glob(available_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(available)), GlobFile);
|
2014-10-21 18:35:43 +02:00
|
|
|
|
|
|
|
String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf";
|
|
|
|
std::vector<String> enabled;
|
2017-11-23 06:51:48 +01:00
|
|
|
Utility::Glob(enabled_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(enabled)), GlobFile);
|
2014-10-18 19:31:52 +02:00
|
|
|
|
|
|
|
std::sort(available.begin(), available.end());
|
|
|
|
std::sort(enabled.begin(), enabled.end());
|
|
|
|
std::set_difference(
|
|
|
|
available.begin(), available.end(),
|
|
|
|
enabled.begin(), enabled.end(),
|
|
|
|
std::back_inserter(features)
|
|
|
|
);
|
|
|
|
} else {
|
2014-10-21 18:35:43 +02:00
|
|
|
/* all enabled features */
|
|
|
|
String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf";
|
2014-10-18 19:31:52 +02:00
|
|
|
|
2017-11-23 06:51:48 +01:00
|
|
|
Utility::Glob(enabled_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(features)), GlobFile);
|
2014-10-18 19:31:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-25 10:14:19 +02:00
|
|
|
bool FeatureUtility::CheckFeatureEnabled(const String& feature)
|
|
|
|
{
|
|
|
|
return CheckFeatureInternal(feature, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FeatureUtility::CheckFeatureDisabled(const String& feature)
|
|
|
|
{
|
|
|
|
return CheckFeatureInternal(feature, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FeatureUtility::CheckFeatureInternal(const String& feature, bool check_disabled)
|
|
|
|
{
|
|
|
|
std::vector<String> features;
|
|
|
|
|
|
|
|
if (!FeatureUtility::GetFeatures(features, check_disabled))
|
|
|
|
return false;
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const String& check_feature : features) {
|
2015-06-25 10:14:19 +02:00
|
|
|
if (check_feature == feature)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-10-18 19:31:52 +02:00
|
|
|
void FeatureUtility::CollectFeatures(const String& feature_file, std::vector<String>& features)
|
|
|
|
{
|
|
|
|
String feature = Utility::BaseName(feature_file);
|
|
|
|
boost::algorithm::replace_all(feature, ".conf", "");
|
|
|
|
|
2014-10-19 17:52:17 +02:00
|
|
|
Log(LogDebug, "cli")
|
2017-12-19 15:50:05 +01:00
|
|
|
<< "Adding feature: " << feature;
|
2014-10-18 19:31:52 +02:00
|
|
|
features.push_back(feature);
|
|
|
|
}
|