Home | History | Annotate | Download | only in xmlGenerator
      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