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: 49 void info(const std::string &log) override { std::cerr << "Info: " << log << std::endl; } 50 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 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 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 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 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 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 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> 254 static inline int normalizeExitCode(T code) 255 { 256 return int(std::min<T>(code, std::numeric_limits<uint8_t>::max())); 257 } 258 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