1 #include "configuration.hpp" 2 3 #include "../utils.hpp" 4 #include "perform_probe.hpp" 5 6 #include <nlohmann/json.hpp> 7 #include <valijson/adapters/nlohmann_json_adapter.hpp> 8 #include <valijson/schema.hpp> 9 #include <valijson/schema_parser.hpp> 10 #include <valijson/validator.hpp> 11 12 #include <filesystem> 13 #include <fstream> 14 #include <iostream> 15 #include <list> 16 #include <string> 17 #include <vector> 18 19 namespace configuration 20 { 21 // writes output files to persist data 22 bool writeJsonFiles(const nlohmann::json& systemConfiguration) 23 { 24 std::filesystem::create_directory(configurationOutDir); 25 std::ofstream output(currentConfiguration); 26 if (!output.good()) 27 { 28 return false; 29 } 30 output << systemConfiguration.dump(4); 31 output.close(); 32 return true; 33 } 34 35 // reads json files out of the filesystem 36 bool loadConfigurations(std::list<nlohmann::json>& configurations) 37 { 38 // find configuration files 39 std::vector<std::filesystem::path> jsonPaths; 40 if (!findFiles( 41 std::vector<std::filesystem::path>{configurationDirectory, 42 hostConfigurationDirectory}, 43 R"(.*\.json)", jsonPaths)) 44 { 45 std::cerr << "Unable to find any configuration files in " 46 << configurationDirectory << "\n"; 47 return false; 48 } 49 50 std::ifstream schemaStream( 51 std::string(schemaDirectory) + "/" + globalSchema); 52 if (!schemaStream.good()) 53 { 54 std::cerr 55 << "Cannot open schema file, cannot validate JSON, exiting\n\n"; 56 std::exit(EXIT_FAILURE); 57 return false; 58 } 59 nlohmann::json schema = 60 nlohmann::json::parse(schemaStream, nullptr, false, true); 61 if (schema.is_discarded()) 62 { 63 std::cerr 64 << "Illegal schema file detected, cannot validate JSON, exiting\n"; 65 std::exit(EXIT_FAILURE); 66 return false; 67 } 68 69 for (auto& jsonPath : jsonPaths) 70 { 71 std::ifstream jsonStream(jsonPath.c_str()); 72 if (!jsonStream.good()) 73 { 74 std::cerr << "unable to open " << jsonPath.string() << "\n"; 75 continue; 76 } 77 auto data = nlohmann::json::parse(jsonStream, nullptr, false, true); 78 if (data.is_discarded()) 79 { 80 std::cerr << "syntax error in " << jsonPath.string() << "\n"; 81 continue; 82 } 83 /* 84 * todo(james): reenable this once less things are in flight 85 * 86 if (!validateJson(schema, data)) 87 { 88 std::cerr << "Error validating " << jsonPath.string() << "\n"; 89 continue; 90 } 91 */ 92 93 if (data.type() == nlohmann::json::value_t::array) 94 { 95 for (auto& d : data) 96 { 97 configurations.emplace_back(d); 98 } 99 } 100 else 101 { 102 configurations.emplace_back(data); 103 } 104 } 105 return true; 106 } 107 108 // Iterate over new configuration and erase items from old configuration. 109 void deriveNewConfiguration(const nlohmann::json& oldConfiguration, 110 nlohmann::json& newConfiguration) 111 { 112 for (auto it = newConfiguration.begin(); it != newConfiguration.end();) 113 { 114 auto findKey = oldConfiguration.find(it.key()); 115 if (findKey != oldConfiguration.end()) 116 { 117 it = newConfiguration.erase(it); 118 } 119 else 120 { 121 it++; 122 } 123 } 124 } 125 126 // validates a given input(configuration) with a given json schema file. 127 bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input) 128 { 129 valijson::Schema schema; 130 valijson::SchemaParser parser; 131 valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile); 132 parser.populateSchema(schemaAdapter, schema); 133 valijson::Validator validator; 134 valijson::adapters::NlohmannJsonAdapter targetAdapter(input); 135 return validator.validate(schema, targetAdapter, nullptr); 136 } 137 138 // Extract the D-Bus interfaces to probe from the JSON config files. 139 std::set<std::string> getProbeInterfaces() 140 { 141 std::set<std::string> interfaces; 142 std::list<nlohmann::json> configurations; 143 if (!configuration::loadConfigurations(configurations)) 144 { 145 return interfaces; 146 } 147 148 for (auto it = configurations.begin(); it != configurations.end();) 149 { 150 auto findProbe = it->find("Probe"); 151 if (findProbe == it->end()) 152 { 153 std::cerr << "configuration file missing probe:\n " << *it << "\n"; 154 it++; 155 continue; 156 } 157 158 nlohmann::json probeCommand; 159 if ((*findProbe).type() != nlohmann::json::value_t::array) 160 { 161 probeCommand = nlohmann::json::array(); 162 probeCommand.push_back(*findProbe); 163 } 164 else 165 { 166 probeCommand = *findProbe; 167 } 168 169 for (const nlohmann::json& probeJson : probeCommand) 170 { 171 const std::string* probe = probeJson.get_ptr<const std::string*>(); 172 if (probe == nullptr) 173 { 174 std::cerr << "Probe statement wasn't a string, can't parse"; 175 continue; 176 } 177 // Skip it if the probe cmd doesn't contain an interface. 178 if (probe::findProbeType(*probe)) 179 { 180 continue; 181 } 182 183 // syntax requires probe before first open brace 184 auto findStart = probe->find('('); 185 if (findStart != std::string::npos) 186 { 187 std::string interface = probe->substr(0, findStart); 188 interfaces.emplace(interface); 189 } 190 } 191 it++; 192 } 193 194 return interfaces; 195 } 196 197 } // namespace configuration 198