1 /*
2 * Copyright (c) 2015, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <ParameterMgrFullConnector.h>
32 #include <Tokenizer.h>
33 #include <Utility.h>
34
35 #include <iostream>
36 #include <sstream>
37 #include <memory>
38 #include <string>
39 #include <limits>
40 #include <numeric>
41 #include <algorithm>
42 #include <stdexcept>
43
44 using std::string;
45
46 class MyLogger final : public CParameterMgrFullConnector::ILogger
47 {
48 public:
info(const std::string & log)49 void info(const std::string &log) override { std::cerr << "Info: " << log << std::endl; }
50
warning(const std::string & log)51 void warning(const std::string &log) override { std::cerr << "Warning: " << log << std::endl; }
52 };
53
54 class XmlGenerator
55 {
56 public:
57 using Exception = std::runtime_error;
58
XmlGenerator(const string & toplevelConfig,bool validate,bool verbose,string schemasDir)59 XmlGenerator(const string &toplevelConfig, bool validate, bool verbose, string schemasDir)
60 : mConnector(toplevelConfig), mCommandHandler(mConnector.createCommandHandler())
61 {
62 if (verbose) {
63 mLogger.reset(new MyLogger);
64 mConnector.setLogger(mLogger.get());
65 }
66
67 mConnector.setSchemaUri(schemasDir);
68 mConnector.setValidateSchemasOnStart(validate);
69
70 // Disable irrelevant failure conditions
71 mConnector.setFailureOnMissingSubsystem(false);
72 mConnector.setFailureOnFailedSettingsLoad(false);
73
74 // Disable the remote interface because we don't need it and it might
75 // get in the way (e.g. the port is already in use)
76 mConnector.setForceNoRemoteInterface(true);
77 }
78
79 /** Reads each line of the input stream and takes an action accordingly
80 *
81 * Returns when the input stream reaches end of file
82 *
83 * The commands are the usual PF tunning commands and some additional specials.
84 * Special commands:
85 * - `createSelectionCriterion inclusive|exclusive <name> <value> [value, ...]`
86 * Create a criterion with the given properties.
87 * - `start` start the Parameter Framework. All criteria must have been created.
88 *
89 * @param[in] input The input stream to read from
90 *
91 * @return the number of error that occurred
92 */
93 size_t parse(std::istream &input);
94
95 /** Check for elements belonging to several domains
96 *
97 * Prints conflicting elements, if any, on the error output.
98 *
99 * @returns true if there are conflicting elements, false otherwise
100 */
101 bool conflictingElements();
102
103 /** Prints the Parameter Framework's instance configuration
104 *
105 * @param[out] output The stream to which output the configuration
106 */
107 void exportDomains(std::ostream &output);
108
109 private:
110 void addCriteria(std::vector<string> &tokens);
111 void start();
112
113 CParameterMgrFullConnector mConnector;
114 std::unique_ptr<MyLogger> mLogger;
115 std::unique_ptr<CommandHandlerInterface> mCommandHandler;
116 };
117
addCriteria(std::vector<string> & tokens)118 void XmlGenerator::addCriteria(std::vector<string> &tokens)
119 {
120 if (tokens.size() < 3) {
121 throw Exception("Not enough arguments to criterion creation request");
122 }
123
124 auto inclusiveness = tokens.front() == "inclusive";
125 tokens.erase(begin(tokens));
126
127 auto name = tokens.front();
128 tokens.erase(begin(tokens));
129
130 auto criterionType = mConnector.createSelectionCriterionType(inclusiveness);
131 if (criterionType == nullptr) {
132 throw Exception("Failed to create an " + string(inclusiveness ? "inclusive" : "exclusive") +
133 " criterion type");
134 }
135
136 int index = 0;
137 for (const auto &literalValue : tokens) {
138 // inclusive criteria are bitfields
139 int numericalValue = inclusiveness ? 1 << index : index;
140 string error;
141 bool success = criterionType->addValuePair(numericalValue, literalValue, error);
142
143 if (not success) {
144 std::ostringstream message;
145 message << "Valuepair (" << numericalValue << ", '" << literalValue
146 << "') rejected for " << name << ": " << error;
147 throw Exception(message.str());
148 }
149 index++;
150 }
151
152 // We don't need to keep a reference to the criterion - no need to store
153 // the returned pointer.
154 if (mConnector.createSelectionCriterion(name, criterionType) == nullptr) {
155 throw Exception("Failed to create criterion '" + name + "'");
156 }
157 }
158
parse(std::istream & input)159 size_t XmlGenerator::parse(std::istream &input)
160 {
161 string line;
162 size_t errorNb = 0;
163 while (not input.eof()) {
164 std::getline(std::cin, line);
165
166 auto tokens = Tokenizer(line, string(1, '\0'), false).split();
167 if (tokens.empty()) {
168 continue;
169 }
170 auto command = tokens.front();
171 tokens.erase(begin(tokens)); // drop the command name
172
173 if (command == "createSelectionCriterion") {
174 addCriteria(tokens);
175 } else if (command == "start") {
176 start();
177 } else {
178 string output;
179 if (not mCommandHandler->process(command, tokens, output)) {
180 errorNb++;
181
182 std::cerr << accumulate(begin(tokens), end(tokens),
183 "Failed to executing command: `" + command + "'",
184 [](string l, string r) { return l + " `" + r + "'"; })
185 << std::endl
186 << output << std::endl;
187 }
188 }
189 }
190 return errorNb;
191 }
192
conflictingElements()193 bool XmlGenerator::conflictingElements()
194 {
195 string conflicting;
196 if (not mCommandHandler->process("listConflictingElements", {}, conflicting)) {
197 // Should not happen
198 throw Exception("Failed to list conflicting elements");
199 }
200
201 if (not conflicting.empty()) {
202 std::cerr << "There are conflicting elements:" << std::endl << conflicting;
203 return true;
204 }
205
206 return false;
207 }
208
start()209 void XmlGenerator::start()
210 {
211 string error;
212 if (not mConnector.start(error)) {
213 throw Exception("Start failed: " + error);
214 }
215
216 error.clear();
217 // Switch to tunning mode as the tunning commands
218 // are the only commands possible with this connector.
219 if (not mConnector.setTuningMode(true, error)) {
220 throw Exception("Failed to turn tuning mode on: " + error);
221 }
222 }
223
exportDomains(std::ostream & output)224 void XmlGenerator::exportDomains(std::ostream &output)
225 {
226 string error;
227 string domains;
228 if (not mConnector.exportDomainsXml(domains, true, false, error)) {
229 throw Exception("Export failed: " + error);
230 } else {
231 output << domains;
232 }
233 }
234
235 static const char *usage =
236 R"(Usage: domainGeneratorConnector <top-level config> <verbose> <validate> <path>
237
238 <verbose> 'verbose': verbose, else: terse
239 <validate> 'validate': validate, else: don't validate
240 <path> path to the schemas' directory
241
242 All arguments are mandatory. If no validation is required,
243 the path to the schemas can be an empty string.
244
245 Exit with the number of (recoverable or not error) that occured.
246
247 This program is not intended to be used standalone but rather called through
248 domainGenerator.py)";
249
250 /** On linux at least, a program can not exit with a value greater than 255.
251 * @return min(code, 255);
252 */
253 template <class T>
normalizeExitCode(T code)254 static inline int normalizeExitCode(T code)
255 {
256 return int(std::min<T>(code, std::numeric_limits<uint8_t>::max()));
257 }
258
main(int argc,char * argv[])259 int main(int argc, char *argv[])
260 {
261 using std::endl;
262
263 if (argc <= 4) {
264 std::cerr << usage << std::endl;
265 return 1;
266 }
267
268 string toplevelConfig = argv[1];
269 bool verbose = string(argv[2]) == "verbose";
270 bool validate = string(argv[3]) == "validate";
271 string schemasDir = argv[4];
272
273 if (verbose) {
274 std::cerr << "Domain generator config:" << endl
275 << " toplevelConfig=" << toplevelConfig << endl
276 << " verbose=" << verbose << endl
277 << " validate=" << validate << endl
278 << " schemasDir=" << schemasDir << endl;
279 }
280
281 try {
282 XmlGenerator xmlGenerator(toplevelConfig, validate, verbose, schemasDir);
283 auto errorNb = xmlGenerator.parse(std::cin);
284 if (xmlGenerator.conflictingElements()) {
285 errorNb++;
286 }
287 xmlGenerator.exportDomains(std::cout);
288
289 return normalizeExitCode(errorNb);
290 } catch (std::exception &e) {
291 std::cerr << e.what() << std::endl;
292 return 1;
293 }
294 }
295