diff --git a/lib/cli/featuredisablecommand.cpp b/lib/cli/featuredisablecommand.cpp index 54af35aa8..9c2253388 100644 --- a/lib/cli/featuredisablecommand.cpp +++ b/lib/cli/featuredisablecommand.cpp @@ -20,14 +20,6 @@ #include "cli/featuredisablecommand.hpp" #include "cli/featureutility.hpp" #include "base/logger.hpp" -#include "base/application.hpp" -#include "base/convert.hpp" -#include "base/console.hpp" -#include -#include -#include -#include -#include using namespace icinga; namespace po = boost::program_options; @@ -46,7 +38,7 @@ String FeatureDisableCommand::GetShortDescription(void) const std::vector FeatureDisableCommand::GetPositionalSuggestions(const String& word) const { - return FeatureUtility::GetFieldCompletionSuggestions(FeatureCommandDisable, word); + return FeatureUtility::GetFieldCompletionSuggestions(word, false); } /** @@ -56,49 +48,10 @@ std::vector FeatureDisableCommand::GetPositionalSuggestions(const String */ int FeatureDisableCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { - String features_enabled_dir = Application::GetSysconfDir() + "/icinga2/features-enabled"; - if (ap.empty()) { Log(LogCritical, "cli", "Cannot disable feature(s). Name(s) are missing!"); return 0; } - if (!Utility::PathExists(features_enabled_dir) ) { - Log(LogCritical, "cli") - << "Cannot disable features. Path '" << features_enabled_dir << "' does not exist."; - return 0; - } - - std::vector errors; - - BOOST_FOREACH(const String& feature, ap) { - String target = features_enabled_dir + "/" + feature + ".conf"; - - if (!Utility::PathExists(target) ) { - Log(LogCritical, "cli") - << "Cannot disable feature '" << feature << "'. Target file '" << target << "' does not exist."; - errors.push_back(feature); - continue; - } - - if (unlink(target.CStr()) < 0) { - Log(LogCritical, "cli") - << "Cannot disable feature '" << feature << "'. Unlinking target file '" << target - << "' failed with error code " << errno << ", \"" + Utility::FormatErrorNumber(errno) << "\"."; - errors.push_back(feature); - continue; - } - - std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature - << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n"; - } - - if (!errors.empty()) { - Log(LogCritical, "cli") - << "Cannot disable feature(s): " << boost::algorithm::join(errors, " "); - errors.clear(); - return 1; - } - - return 0; + return FeatureUtility::DisableFeatures(ap); } diff --git a/lib/cli/featureenablecommand.cpp b/lib/cli/featureenablecommand.cpp index 36f80167a..131629bde 100644 --- a/lib/cli/featureenablecommand.cpp +++ b/lib/cli/featureenablecommand.cpp @@ -20,13 +20,6 @@ #include "cli/featureenablecommand.hpp" #include "cli/featureutility.hpp" #include "base/logger.hpp" -#include "base/application.hpp" -#include "base/convert.hpp" -#include "base/console.hpp" -#include -#include -#include -#include using namespace icinga; namespace po = boost::program_options; @@ -45,7 +38,7 @@ String FeatureEnableCommand::GetShortDescription(void) const std::vector FeatureEnableCommand::GetPositionalSuggestions(const String& word) const { - return FeatureUtility::GetFieldCompletionSuggestions(FeatureCommandEnable, word); + return FeatureUtility::GetFieldCompletionSuggestions(word, true); } /** @@ -55,78 +48,12 @@ std::vector FeatureEnableCommand::GetPositionalSuggestions(const String& */ int FeatureEnableCommand::Run(const boost::program_options::variables_map& vm, const std::vector& ap) const { - String features_available_dir = Application::GetSysconfDir() + "/icinga2/features-available"; - String features_enabled_dir = Application::GetSysconfDir() + "/icinga2/features-enabled"; - if (ap.empty()) { Log(LogCritical, "cli", "Cannot enable feature(s). Name(s) are missing!"); return 0; } - if (!Utility::PathExists(features_available_dir) ) { - Log(LogCritical, "cli") - << "Cannot parse available features. Path '" << features_available_dir << "' does not exist."; - return 0; - } - - if (!Utility::PathExists(features_enabled_dir) ) { - Log(LogCritical, "cli") - << "Cannot enable features. Path '" << features_enabled_dir << "' does not exist."; - return 0; - } - - std::vector errors; - - BOOST_FOREACH(const String& feature, ap) { - String source = features_available_dir + "/" + feature + ".conf"; - - if (!Utility::PathExists(source) ) { - Log(LogCritical, "cli") - << "Cannot enable feature '" << feature << "'. Source file '" << source + "' does not exist."; - errors.push_back(feature); - continue; - } - - String target = features_enabled_dir + "/" + feature + ".conf"; - - if (Utility::PathExists(target) ) { - Log(LogWarning, "cli") - << "Feature '" << feature << "' already enabled."; - continue; - } - - std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature - << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n"; - -#ifndef _WIN32 - if (symlink(source.CStr(), target.CStr()) < 0) { - Log(LogCritical, "cli") - << "Cannot enable feature '" << feature << "'. Linking source '" << source << "' to target file '" << target - << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"."; - 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") - << "Cannot enable feature '" << feature << "'. Failed to open file '" << target << "'."; - errors.push_back(feature); - continue; - } -#endif /* _WIN32 */ - } - - if (!errors.empty()) { - Log(LogCritical, "cli") - << "Cannot enable feature(s): " << boost::algorithm::join(errors, " "); - errors.clear(); - return 1; - } + return FeatureUtility::EnableFeatures(ap); return 0; } diff --git a/lib/cli/featurelistcommand.cpp b/lib/cli/featurelistcommand.cpp index 76a25b870..7e0347fd6 100644 --- a/lib/cli/featurelistcommand.cpp +++ b/lib/cli/featurelistcommand.cpp @@ -53,18 +53,5 @@ int FeatureListCommand::Run(const boost::program_options::variables_map& vm, con << "Ignoring parameters: " << boost::algorithm::join(ap, " "); } - std::vector disabled_features; - std::vector enabled_features; - - if (!FeatureUtility::GetFeatures(FeaturesDisabled, disabled_features)) - return 1; - if (!FeatureUtility::GetFeatures(FeaturesEnabled, enabled_features)) - return 1; - - std::cout << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal) - << boost::algorithm::join(disabled_features, " ") << "\n"; - std::cout << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal) - << boost::algorithm::join(enabled_features, " ") << "\n"; - - return 0; + return FeatureUtility::ListFeatures(); } diff --git a/lib/cli/featureutility.cpp b/lib/cli/featureutility.cpp index d3be24668..a09445971 100644 --- a/lib/cli/featureutility.cpp +++ b/lib/cli/featureutility.cpp @@ -19,8 +19,10 @@ #include "cli/featureutility.hpp" #include "base/logger.hpp" +#include "base/console.hpp" #include "base/application.hpp" #include +#include #include using namespace icinga; @@ -35,19 +37,12 @@ String FeatureUtility::GetFeaturesEnabledPath(void) return Application::GetSysconfDir() + "/icinga2/features-enabled"; } - -std::vector FeatureUtility::GetFieldCompletionSuggestions(FeatureCommandType fctype, const String& word) +std::vector FeatureUtility::GetFieldCompletionSuggestions(const String& word, bool enable) { std::vector cache; std::vector suggestions; - if (fctype == FeatureCommandEnable) { - /* only suggest features not already enabled */ - GetFeatures(FeaturesDisabled, cache); - } else if (fctype == FeatureCommandDisable) { - /* suggest all enabled features */ - GetFeatures(FeaturesEnabled, cache); - } + GetFeatures(cache, enable); std::sort(cache.begin(), cache.end()); @@ -59,16 +54,169 @@ std::vector FeatureUtility::GetFieldCompletionSuggestions(FeatureCommand return suggestions; } -bool FeatureUtility::GetFeatures(FeatureType ftype, std::vector& features) +int FeatureUtility::EnableFeatures(const std::vector& features) { - String path = Application::GetSysconfDir() + "/icinga2/"; + String features_available_dir = GetFeaturesAvailablePath(); + String features_enabled_dir = GetFeaturesEnabledPath(); - /* disabled = available-enabled */ - if (ftype == FeaturesDisabled) { - std::vector enabled; + if (!Utility::PathExists(features_available_dir) ) { + Log(LogCritical, "cli") + << "Cannot parse available features. Path '" << features_available_dir << "' does not exist."; + return 1; + } + + if (!Utility::PathExists(features_enabled_dir) ) { + Log(LogCritical, "cli") + << "Cannot enable features. Path '" << features_enabled_dir << "' does not exist."; + return 1; + } + + std::vector errors; + + BOOST_FOREACH(const String& feature, features) { + String source = features_available_dir + "/" + feature + ".conf"; + + if (!Utility::PathExists(source) ) { + Log(LogCritical, "cli") + << "Cannot enable feature '" << feature << "'. Source file '" << source + "' does not exist."; + errors.push_back(feature); + continue; + } + + String target = features_enabled_dir + "/" + feature + ".conf"; + + if (Utility::PathExists(target) ) { + Log(LogWarning, "cli") + << "Feature '" << feature << "' already enabled."; + continue; + } + + std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature + << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n"; + +#ifndef _WIN32 + if (symlink(source.CStr(), target.CStr()) < 0) { + Log(LogCritical, "cli") + << "Cannot enable feature '" << feature << "'. Linking source '" << source << "' to target file '" << target + << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\"."; + 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") + << "Cannot enable feature '" << feature << "'. Failed to open file '" << target << "'."; + errors.push_back(feature); + continue; + } +#endif /* _WIN32 */ + } + + if (!errors.empty()) { + Log(LogCritical, "cli") + << "Cannot enable feature(s): " << boost::algorithm::join(errors, " "); + errors.clear(); + return 1; + } + + return 0; +} + +int FeatureUtility::DisableFeatures(const std::vector& features) +{ + String features_enabled_dir = GetFeaturesEnabledPath(); + + if (!Utility::PathExists(features_enabled_dir) ) { + Log(LogCritical, "cli") + << "Cannot disable features. Path '" << features_enabled_dir << "' does not exist."; + return 0; + } + + std::vector errors; + + BOOST_FOREACH(const String& feature, features) { + String target = features_enabled_dir + "/" + feature + ".conf"; + + if (!Utility::PathExists(target) ) { + Log(LogCritical, "cli") + << "Cannot disable feature '" << feature << "'. Target file '" << target << "' does not exist."; + errors.push_back(feature); + continue; + } + + if (unlink(target.CStr()) < 0) { + Log(LogCritical, "cli") + << "Cannot disable feature '" << feature << "'. Unlinking target file '" << target + << "' failed with error code " << errno << ", \"" + Utility::FormatErrorNumber(errno) << "\"."; + errors.push_back(feature); + continue; + } + + std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature + << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n"; + } + + if (!errors.empty()) { + Log(LogCritical, "cli") + << "Cannot disable feature(s): " << boost::algorithm::join(errors, " "); + errors.clear(); + return 1; + } + + return 0; +} + +int FeatureUtility::ListFeatures(void) +{ + std::vector disabled_features; + std::vector enabled_features; + + if (!FeatureUtility::GetFeatures(disabled_features, true)) + return 1; + + std::cout << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal) + << boost::algorithm::join(disabled_features, " ") << "\n"; + + if (!FeatureUtility::GetFeatures(enabled_features, false)) + return 1; + + std::cout << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal) + << boost::algorithm::join(enabled_features, " ") << "\n"; + + return 0; +} + +bool FeatureUtility::GetFeatures(std::vector& features, bool get_disabled) +{ + String path; + + /* request all disabled features */ + if (get_disabled) { + /* disable = available-enabled */ + String available_pattern = GetFeaturesAvailablePath() + "/*.conf"; std::vector available; - GetFeatures(FeaturesAvailable, available); - GetFeatures(FeaturesEnabled, enabled); + + if (!Utility::Glob(available_pattern, + boost::bind(&FeatureUtility::CollectFeatures, _1, boost::ref(available)), GlobFile)) { + Log(LogCritical, "cli") + << "Cannot access path '" << path << "'."; + return false; + } + + String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf"; + std::vector enabled; + + if (!Utility::Glob(enabled_pattern, + boost::bind(&FeatureUtility::CollectFeatures, _1, boost::ref(enabled)), GlobFile)) { + Log(LogCritical, "cli") + << "Cannot access path '" << path << "'."; + return false; + } std::sort(available.begin(), available.end()); std::sort(enabled.begin(), enabled.end()); @@ -77,19 +225,11 @@ bool FeatureUtility::GetFeatures(FeatureType ftype, std::vector& feature enabled.begin(), enabled.end(), std::back_inserter(features) ); - - return true; } else { - if (ftype == FeaturesAvailable) - path += "features-available/"; - else if (ftype == FeaturesEnabled) - path += "features-enabled/"; - else { - Log(LogCritical, "cli", "Unknown feature type passed. Bailing out."); - return false; - } + /* all enabled features */ + String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf"; - if (!Utility::Glob(path + "/*.conf", + if (!Utility::Glob(enabled_pattern, boost::bind(&FeatureUtility::CollectFeatures, _1, boost::ref(features)), GlobFile)) { Log(LogCritical, "cli") << "Cannot access path '" << path << "'."; diff --git a/lib/cli/featureutility.hpp b/lib/cli/featureutility.hpp index 11b06d598..05b9a3c0e 100644 --- a/lib/cli/featureutility.hpp +++ b/lib/cli/featureutility.hpp @@ -27,30 +27,23 @@ namespace icinga { -enum FeatureType -{ - FeaturesAvailable, - FeaturesEnabled, - FeaturesDisabled -}; - -enum FeatureCommandType -{ - FeatureCommandEnable, - FeatureCommandDisable -}; - /** * @ingroup cli */ class FeatureUtility { public: - static std::vector GetFieldCompletionSuggestions(FeatureCommandType fctype, const String& word); - static bool GetFeatures(FeatureType ftype, std::vector& features); static String GetFeaturesAvailablePath(void); static String GetFeaturesEnabledPath(void); + static std::vector GetFieldCompletionSuggestions(const String& word, bool enable); + + static int EnableFeatures(const std::vector& features); + static int DisableFeatures(const std::vector& features); + static int ListFeatures(void); + + static bool GetFeatures(std::vector& features, bool enable); + private: FeatureUtility(void); static void CollectFeatures(const String& feature_file, std::vector& features);