| #include "configuration.hpp" |
| |
| #include "perform_probe.hpp" |
| #include "utils.hpp" |
| |
| #include <nlohmann/json.hpp> |
| #include <valijson/adapters/nlohmann_json_adapter.hpp> |
| #include <valijson/schema.hpp> |
| #include <valijson/schema_parser.hpp> |
| #include <valijson/validator.hpp> |
| |
| #include <filesystem> |
| #include <fstream> |
| #include <iostream> |
| #include <list> |
| #include <string> |
| #include <vector> |
| |
| namespace configuration |
| { |
| // writes output files to persist data |
| bool writeJsonFiles(const nlohmann::json& systemConfiguration) |
| { |
| std::filesystem::create_directory(configurationOutDir); |
| std::ofstream output(currentConfiguration); |
| if (!output.good()) |
| { |
| return false; |
| } |
| output << systemConfiguration.dump(4); |
| output.close(); |
| return true; |
| } |
| |
| // reads json files out of the filesystem |
| bool loadConfigurations(std::list<nlohmann::json>& configurations) |
| { |
| // find configuration files |
| std::vector<std::filesystem::path> jsonPaths; |
| if (!findFiles( |
| std::vector<std::filesystem::path>{configurationDirectory, |
| hostConfigurationDirectory}, |
| R"(.*\.json)", jsonPaths)) |
| { |
| std::cerr << "Unable to find any configuration files in " |
| << configurationDirectory << "\n"; |
| return false; |
| } |
| |
| 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 false; |
| } |
| 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 false; |
| } |
| |
| 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; |
| } |
| /* |
| * todo(james): reenable this once less things are in flight |
| * |
| if (!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); |
| } |
| } |
| return true; |
| } |
| |
| // 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. |
| std::set<std::string> getProbeInterfaces() |
| { |
| std::set<std::string> interfaces; |
| std::list<nlohmann::json> configurations; |
| if (!configuration::loadConfigurations(configurations)) |
| { |
| return interfaces; |
| } |
| |
| 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<const std::string*>(); |
| 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); |
| interfaces.emplace(interface); |
| } |
| } |
| it++; |
| } |
| |
| return interfaces; |
| } |
| |
| } // namespace configuration |