]> granicus.if.org Git - icinga2/blob - lib/cli/featureutility.cpp
Merge pull request #7145 from Icinga/feature/dotnet-4.6
[icinga2] / lib / cli / featureutility.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
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>
10 #include <fstream>
11 #include <iostream>
12
13 using namespace icinga;
14
15 String FeatureUtility::GetFeaturesAvailablePath()
16 {
17         return Configuration::ConfigDir + "/features-available";
18 }
19
20 String FeatureUtility::GetFeaturesEnabledPath()
21 {
22         return Configuration::ConfigDir + "/features-enabled";
23 }
24
25 std::vector<String> FeatureUtility::GetFieldCompletionSuggestions(const String& word, bool enable)
26 {
27         std::vector<String> cache;
28         std::vector<String> suggestions;
29
30         GetFeatures(cache, enable);
31
32         std::sort(cache.begin(), cache.end());
33
34         for (const String& suggestion : cache) {
35                 if (suggestion.Find(word) == 0)
36                         suggestions.push_back(suggestion);
37         }
38
39         return suggestions;
40 }
41
42 int FeatureUtility::EnableFeatures(const std::vector<std::string>& features)
43 {
44         String features_available_dir = GetFeaturesAvailablePath();
45         String features_enabled_dir = GetFeaturesEnabledPath();
46
47         if (!Utility::PathExists(features_available_dir) ) {
48                 Log(LogCritical, "cli")
49                         << "Cannot parse available features. Path '" << features_available_dir << "' does not exist.";
50                 return 1;
51         }
52
53         if (!Utility::PathExists(features_enabled_dir) ) {
54                 Log(LogCritical, "cli")
55                         << "Cannot enable features. Path '" << features_enabled_dir << "' does not exist.";
56                 return 1;
57         }
58
59         std::vector<std::string> errors;
60
61         for (const String& feature : features) {
62                 String source = features_available_dir + "/" + feature + ".conf";
63
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);
68                         continue;
69                 }
70
71                 String target = features_enabled_dir + "/" + feature + ".conf";
72
73                 if (Utility::PathExists(target) ) {
74                         Log(LogWarning, "cli")
75                                 << "Feature '" << feature << "' already enabled.";
76                         continue;
77                 }
78
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";
81
82 #ifndef _WIN32
83                 String relativeSource = "../features-available/" + feature + ".conf";
84
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);
90                         continue;
91                 }
92 #else /* _WIN32 */
93                 std::ofstream fp;
94                 fp.open(target.CStr());
95                 fp << "include \"../features-available/" << feature << ".conf\"" << std::endl;
96                 fp.close();
97
98                 if (fp.fail()) {
99                         Log(LogCritical, "cli")
100                                 << "Cannot enable feature '" << feature << "'. Failed to open file '" << target << "'.";
101                         errors.push_back(feature);
102                         continue;
103                 }
104 #endif /* _WIN32 */
105         }
106
107         if (!errors.empty()) {
108                 Log(LogCritical, "cli")
109                         << "Cannot enable feature(s): " << boost::algorithm::join(errors, " ");
110                 errors.clear();
111                 return 1;
112         }
113
114         return 0;
115 }
116
117 int FeatureUtility::DisableFeatures(const std::vector<std::string>& features)
118 {
119         String features_enabled_dir = GetFeaturesEnabledPath();
120
121         if (!Utility::PathExists(features_enabled_dir) ) {
122                 Log(LogCritical, "cli")
123                         << "Cannot disable features. Path '" << features_enabled_dir << "' does not exist.";
124                 return 0;
125         }
126
127         std::vector<std::string> errors;
128
129         for (const String& feature : features) {
130                 String target = features_enabled_dir + "/" + feature + ".conf";
131
132                 if (!Utility::PathExists(target) ) {
133                         Log(LogWarning, "cli")
134                                 << "Feature '" << feature << "' already disabled.";
135                         continue;
136                 }
137
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);
143                         continue;
144                 }
145
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";
148         }
149
150         if (!errors.empty()) {
151                 Log(LogCritical, "cli")
152                         << "Cannot disable feature(s): " << boost::algorithm::join(errors, " ");
153                 errors.clear();
154                 return 1;
155         }
156
157         return 0;
158 }
159
160 int FeatureUtility::ListFeatures(std::ostream& os)
161 {
162         std::vector<String> disabled_features;
163         std::vector<String> enabled_features;
164
165         if (!FeatureUtility::GetFeatures(disabled_features, true))
166                 return 1;
167
168         os << ConsoleColorTag(Console_ForegroundRed | Console_Bold) << "Disabled features: " << ConsoleColorTag(Console_Normal)
169                 << boost::algorithm::join(disabled_features, " ") << "\n";
170
171         if (!FeatureUtility::GetFeatures(enabled_features, false))
172                 return 1;
173
174         os << ConsoleColorTag(Console_ForegroundGreen | Console_Bold) << "Enabled features: " << ConsoleColorTag(Console_Normal)
175                 << boost::algorithm::join(enabled_features, " ") << "\n";
176
177         return 0;
178 }
179
180 bool FeatureUtility::GetFeatures(std::vector<String>& features, bool get_disabled)
181 {
182         /* request all disabled features */
183         if (get_disabled) {
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);
188
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);
192
193                 std::sort(available.begin(), available.end());
194                 std::sort(enabled.begin(), enabled.end());
195                 std::set_difference(
196                         available.begin(), available.end(),
197                         enabled.begin(), enabled.end(),
198                         std::back_inserter(features)
199                 );
200         } else {
201                 /* all enabled features */
202                 String enabled_pattern = GetFeaturesEnabledPath() + "/*.conf";
203
204                 Utility::Glob(enabled_pattern, std::bind(&FeatureUtility::CollectFeatures, _1, std::ref(features)), GlobFile);
205         }
206
207         return true;
208 }
209
210 bool FeatureUtility::CheckFeatureEnabled(const String& feature)
211 {
212         return CheckFeatureInternal(feature, false);
213 }
214
215 bool FeatureUtility::CheckFeatureDisabled(const String& feature)
216 {
217         return CheckFeatureInternal(feature, true);
218 }
219
220 bool FeatureUtility::CheckFeatureInternal(const String& feature, bool check_disabled)
221 {
222         std::vector<String> features;
223
224         if (!FeatureUtility::GetFeatures(features, check_disabled))
225                 return false;
226
227         for (const String& check_feature : features) {
228                 if (check_feature == feature)
229                         return true;
230         }
231
232         return false;
233 }
234
235 void FeatureUtility::CollectFeatures(const String& feature_file, std::vector<String>& features)
236 {
237         String feature = Utility::BaseName(feature_file);
238         boost::algorithm::replace_all(feature, ".conf", "");
239
240         Log(LogDebug, "cli")
241                 << "Adding feature: " << feature;
242         features.push_back(feature);
243 }