#include "configuration.hpp" #include "perform_probe.hpp" #include "phosphor-logging/lg2.hpp" #include "utils.hpp" #include #include #include #include #include #include #include #include #include #include Configuration::Configuration() { loadConfigurations(); filterProbeInterfaces(); } void Configuration::loadConfigurations() { const auto start = std::chrono::steady_clock::now(); // find configuration files std::vector jsonPaths; if (!findFiles( std::vector{configurationDirectory, hostConfigurationDirectory}, R"(.*\.json)", jsonPaths)) { std::cerr << "Unable to find any configuration files in " << configurationDirectory << "\n"; return; } std::ifstream schemaStream( std::string(schemaDirectory) + "/" + globalSchema); if (!schemaStream.good()) { std::cerr << "Cannot open schema file, cannot validate JSON, exiting\n\n"; std::exit(EXIT_FAILURE); return; } nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false, true); if (schema.is_discarded()) { std::cerr << "Illegal schema file detected, cannot validate JSON, exiting\n"; std::exit(EXIT_FAILURE); return; } for (auto& jsonPath : jsonPaths) { std::ifstream jsonStream(jsonPath.c_str()); if (!jsonStream.good()) { std::cerr << "unable to open " << jsonPath.string() << "\n"; continue; } auto data = nlohmann::json::parse(jsonStream, nullptr, false, true); if (data.is_discarded()) { std::cerr << "syntax error in " << jsonPath.string() << "\n"; continue; } if (ENABLE_RUNTIME_VALIDATE_JSON && !validateJson(schema, data)) { std::cerr << "Error validating " << jsonPath.string() << "\n"; continue; } if (data.type() == nlohmann::json::value_t::array) { for (auto& d : data) { configurations.emplace_back(d); } } else { configurations.emplace_back(data); } } const auto duration = std::chrono::duration_cast( std::chrono::steady_clock::now() - start) .count(); lg2::debug("Finished loading json configuration in {MILLIS}ms", "MILLIS", duration); } // Iterate over new configuration and erase items from old configuration. void deriveNewConfiguration(const nlohmann::json& oldConfiguration, nlohmann::json& newConfiguration) { for (auto it = newConfiguration.begin(); it != newConfiguration.end();) { auto findKey = oldConfiguration.find(it.key()); if (findKey != oldConfiguration.end()) { it = newConfiguration.erase(it); } else { it++; } } } // validates a given input(configuration) with a given json schema file. bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input) { valijson::Schema schema; valijson::SchemaParser parser; valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile); parser.populateSchema(schemaAdapter, schema); valijson::Validator validator; valijson::adapters::NlohmannJsonAdapter targetAdapter(input); return validator.validate(schema, targetAdapter, nullptr); } // Extract the D-Bus interfaces to probe from the JSON config files. void Configuration::filterProbeInterfaces() { for (auto it = configurations.begin(); it != configurations.end();) { auto findProbe = it->find("Probe"); if (findProbe == it->end()) { std::cerr << "configuration file missing probe:\n " << *it << "\n"; it++; continue; } nlohmann::json probeCommand; if ((*findProbe).type() != nlohmann::json::value_t::array) { probeCommand = nlohmann::json::array(); probeCommand.push_back(*findProbe); } else { probeCommand = *findProbe; } for (const nlohmann::json& probeJson : probeCommand) { const std::string* probe = probeJson.get_ptr(); if (probe == nullptr) { std::cerr << "Probe statement wasn't a string, can't parse"; continue; } // Skip it if the probe cmd doesn't contain an interface. if (probe::findProbeType(*probe)) { continue; } // syntax requires probe before first open brace auto findStart = probe->find('('); if (findStart != std::string::npos) { std::string interface = probe->substr(0, findStart); probeInterfaces.emplace(interface); } } it++; } } bool writeJsonFiles(const nlohmann::json& systemConfiguration) { if (!EM_CACHE_CONFIGURATION) { return true; } std::error_code ec; std::filesystem::create_directory(configurationOutDir, ec); if (ec) { return false; } std::ofstream output(currentConfiguration); if (!output.good()) { return false; } output << systemConfiguration.dump(4); output.close(); return true; }