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