1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
3 #include "cli/featureutility.hpp"
4 #include "base/logger.hpp"
5 #include "base/console.hpp"
6 #include "base/application.hpp"
7 #include "base/utility.hpp"
8 #include <boost/algorithm/string/join.hpp>
9 #include <boost/algorithm/string/replace.hpp>
13 using namespace icinga;
15 String FeatureUtility::GetFeaturesAvailablePath()
17 return Configuration::ConfigDir + "/features-available";
20 String FeatureUtility::GetFeaturesEnabledPath()
22 return Configuration::ConfigDir + "/features-enabled";
25 std::vector<String> FeatureUtility::GetFieldCompletionSuggestions(const String& word, bool enable)
27 std::vector<String> cache;
28 std::vector<String> suggestions;
30 GetFeatures(cache, enable);
32 std::sort(cache.begin(), cache.end());
34 for (const String& suggestion : cache) {
35 if (suggestion.Find(word) == 0)
36 suggestions.push_back(suggestion);
42 int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
44 String features_available_dir = GetFeaturesAvailablePath();
45 String features_enabled_dir = GetFeaturesEnabledPath();
47 if (!Utility::PathExists(features_available_dir) ) {
48 Log(LogCritical, "cli")
49 << "Cannot parse available features. Path '" << features_available_dir << "' does not exist.";
53 if (!Utility::PathExists(features_enabled_dir) ) {
54 Log(LogCritical, "cli")
55 << "Cannot enable features. Path '" << features_enabled_dir << "' does not exist.";
59 std::vector<std::string> errors;
61 for (const String& feature : features) {
62 String source = features_available_dir + "/" + feature + ".conf";
64 if (!Utility::PathExists(source) ) {
65 Log(LogCritical, "cli")
66 << "Cannot enable feature '" << feature << "'. Source file '" << source + "' does not exist.";
67 errors.push_back(feature);
71 String target = features_enabled_dir + "/" + feature + ".conf";
73 if (Utility::PathExists(target) ) {
74 Log(LogWarning, "cli")
75 << "Feature '" << feature << "' already enabled.";
79 std::cout << "Enabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
80 << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
83 String relativeSource = "../features-available/" + feature + ".conf";
85 if (symlink(relativeSource.CStr(), target.CStr()) < 0) {
86 Log(LogCritical, "cli")
87 << "Cannot enable feature '" << feature << "'. Linking source '" << relativeSource << "' to target file '" << target
88 << "' failed with error code " << errno << ", \"" << Utility::FormatErrorNumber(errno) << "\".";
89 errors.push_back(feature);
94 fp.open(target.CStr());
95 fp << "include \"../features-available/" << feature << ".conf\"" << std::endl;
99 Log(LogCritical, "cli")
100 << "Cannot enable feature '" << feature << "'. Failed to open file '" << target << "'.";
101 errors.push_back(feature);
107 if (!errors.empty()) {
108 Log(LogCritical, "cli")
109 << "Cannot enable feature(s): " << boost::algorithm::join(errors, " ");
117 int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
119 String features_enabled_dir = GetFeaturesEnabledPath();
121 if (!Utility::PathExists(features_enabled_dir) ) {
122 Log(LogCritical, "cli")
123 << "Cannot disable features. Path '" << features_enabled_dir << "' does not exist.";
127 std::vector<std::string> errors;
129 for (const String& feature : features) {
130 String target = features_enabled_dir + "/" + feature + ".conf";
132 if (!Utility::PathExists(target) ) {
133 Log(LogWarning, "cli")
134 << "Feature '" << feature << "' already disabled.";
138 if (unlink(target.CStr()) < 0) {
139 Log(LogCritical, "cli")
140 << "Cannot disable feature '" << feature << "'. Unlinking target file '" << target
141 << "' failed with error code " << errno << ", \"" + Utility::FormatErrorNumber(errno) << "\".";
142 errors.push_back(feature);
146 std::cout << "Disabling feature " << ConsoleColorTag(Console_ForegroundMagenta | Console_Bold) << feature
147 << ConsoleColorTag(Console_Normal) << ". Make sure to restart Icinga 2 for these changes to take effect.\n";
150 if (!errors.empty()) {
151 Log(LogCritical, "cli")
152 << "Cannot disable feature(s): " << boost::algorithm::join(errors, " ");
160 int FeatureUtility::ListFeatures(std::ostream& os)
162 std::vector<String> disabled_features;
163 std::vector<String> enabled_features;
165 if (!FeatureUtility::GetFeatures(disabled_features, true))
168 os << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal)
169 << boost::algorithm::join(disabled_features, " ") << "\n";
171 if (!FeatureUtility::GetFeatures(enabled_features, false))
174 os << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal)
175 << boost::algorithm::join(enabled_features, " ") << "\n";
180 bool FeatureUtility::GetFeatures(std::vector<String>& features, bool get_disabled)
182 /* request all disabled features */
184 /* disable = available-enabled */
185 String available_pattern = GetFeaturesAvailablePath() + "/*.conf";
186 std::vector<String> available;
187 Utility::Glob(available_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(available)), GlobFile);
189 String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf";
190 std::vector<String> enabled;
191 Utility::Glob(enabled_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(enabled)), GlobFile);
193 std::sort(available.begin(), available.end());
194 std::sort(enabled.begin(), enabled.end());
196 available.begin(), available.end(),
197 enabled.begin(), enabled.end(),
198 std::back_inserter(features)
201 /* all enabled features */
202 String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf";
204 Utility::Glob(enabled_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(features)), GlobFile);
210 bool FeatureUtility::CheckFeatureEnabled(const String& feature)
212 return CheckFeatureInternal(feature, false);
215 bool FeatureUtility::CheckFeatureDisabled(const String& feature)
217 return CheckFeatureInternal(feature, true);
220 bool FeatureUtility::CheckFeatureInternal(const String& feature, bool check_disabled)
222 std::vector<String> features;
224 if (!FeatureUtility::GetFeatures(features, check_disabled))
227 for (const String& check_feature : features) {
228 if (check_feature == feature)
235 void FeatureUtility::CollectFeatures(const String& feature_file, std::vector<String>& features)
237 String feature = Utility::BaseName(feature_file);
238 boost::algorithm::replace_all(feature, ".conf", "");
241 << "Adding feature: " << feature;
242 features.push_back(feature);