1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 /// \file perform_probe.cpp 17 #include "entity_manager.hpp" 18 19 #include <boost/algorithm/string/replace.hpp> 20 21 #include <regex> 22 #include <utility> 23 24 constexpr const bool debug = false; 25 26 // probes dbus interface dictionary for a key with a value that matches a regex 27 // When an interface passes a probe, also save its D-Bus path with it. 28 bool probeDbus(const std::string& interfaceName, 29 const std::map<std::string, nlohmann::json>& matches, 30 FoundDevices& devices, const std::shared_ptr<PerformScan>& scan, 31 bool& foundProbe) 32 { 33 bool foundMatch = false; 34 foundProbe = false; 35 36 for (const auto& [path, interfaces] : scan->dbusProbeObjects) 37 { 38 auto it = interfaces.find(interfaceName); 39 if (it == interfaces.end()) 40 { 41 continue; 42 } 43 44 foundProbe = true; 45 46 bool deviceMatches = true; 47 const DBusInterface& interface = it->second; 48 49 for (const auto& [matchProp, matchJSON] : matches) 50 { 51 auto deviceValue = interface.find(matchProp); 52 if (deviceValue != interface.end()) 53 { 54 deviceMatches = deviceMatches && 55 matchProbe(matchJSON, deviceValue->second); 56 } 57 else 58 { 59 // Move on to the next DBus path 60 deviceMatches = false; 61 break; 62 } 63 } 64 if (deviceMatches) 65 { 66 if constexpr (debug) 67 { 68 std::cerr << "probeDBus: Found probe match on " << path << " " 69 << interfaceName << "\n"; 70 } 71 devices.emplace_back(interface, path); 72 foundMatch = true; 73 } 74 } 75 return foundMatch; 76 } 77 78 // default probe entry point, iterates a list looking for specific types to 79 // call specific probe functions 80 bool probe(const std::vector<std::string>& probeCommand, 81 const std::shared_ptr<PerformScan>& scan, FoundDevices& foundDevs) 82 { 83 const static std::regex command(R"(\((.*)\))"); 84 std::smatch match; 85 bool ret = false; 86 bool matchOne = false; 87 bool cur = true; 88 probe_type_codes lastCommand = probe_type_codes::FALSE_T; 89 bool first = true; 90 91 for (const auto& probe : probeCommand) 92 { 93 FoundProbeTypeT probeType = findProbeType(probe); 94 if (probeType) 95 { 96 switch ((*probeType)->second) 97 { 98 case probe_type_codes::FALSE_T: 99 { 100 cur = false; 101 break; 102 } 103 case probe_type_codes::TRUE_T: 104 { 105 cur = true; 106 break; 107 } 108 case probe_type_codes::MATCH_ONE: 109 { 110 // set current value to last, this probe type shouldn't 111 // affect the outcome 112 cur = ret; 113 matchOne = true; 114 break; 115 } 116 /*case probe_type_codes::AND: 117 break; 118 case probe_type_codes::OR: 119 break; 120 // these are no-ops until the last command switch 121 */ 122 case probe_type_codes::FOUND: 123 { 124 if (!std::regex_search(probe, match, command)) 125 { 126 std::cerr 127 << "found probe syntax error " << probe << "\n"; 128 return false; 129 } 130 std::string commandStr = *(match.begin() + 1); 131 boost::replace_all(commandStr, "'", ""); 132 cur = (std::find(scan->passedProbes.begin(), 133 scan->passedProbes.end(), commandStr) != 134 scan->passedProbes.end()); 135 break; 136 } 137 default: 138 { 139 break; 140 } 141 } 142 } 143 // look on dbus for object 144 else 145 { 146 if (!std::regex_search(probe, match, command)) 147 { 148 std::cerr << "dbus probe syntax error " << probe << "\n"; 149 return false; 150 } 151 std::string commandStr = *(match.begin() + 1); 152 // convert single ticks and single slashes into legal json 153 boost::replace_all(commandStr, "'", "\""); 154 boost::replace_all(commandStr, R"(\)", R"(\\)"); 155 auto json = nlohmann::json::parse(commandStr, nullptr, false, true); 156 if (json.is_discarded()) 157 { 158 std::cerr << "dbus command syntax error " << commandStr << "\n"; 159 return false; 160 } 161 // we can match any (string, variant) property. (string, string) 162 // does a regex 163 std::map<std::string, nlohmann::json> dbusProbeMap = 164 json.get<std::map<std::string, nlohmann::json>>(); 165 auto findStart = probe.find('('); 166 if (findStart == std::string::npos) 167 { 168 return false; 169 } 170 bool foundProbe = !!probeType; 171 std::string probeInterface = probe.substr(0, findStart); 172 cur = probeDbus(probeInterface, dbusProbeMap, foundDevs, scan, 173 foundProbe); 174 } 175 176 // some functions like AND and OR only take affect after the 177 // fact 178 if (lastCommand == probe_type_codes::AND) 179 { 180 ret = cur && ret; 181 } 182 else if (lastCommand == probe_type_codes::OR) 183 { 184 ret = cur || ret; 185 } 186 187 if (first) 188 { 189 ret = cur; 190 first = false; 191 } 192 lastCommand = probeType ? (*probeType)->second 193 : probe_type_codes::FALSE_T; 194 } 195 196 // probe passed, but empty device 197 if (ret && foundDevs.empty()) 198 { 199 foundDevs.emplace_back( 200 boost::container::flat_map<std::string, DBusValueVariant>{}, 201 std::string{}); 202 } 203 if (matchOne && ret) 204 { 205 // match the last one 206 auto last = foundDevs.back(); 207 foundDevs.clear(); 208 209 foundDevs.emplace_back(std::move(last)); 210 } 211 return ret; 212 } 213 214 PerformProbe::PerformProbe(nlohmann::json& recordRef, 215 const std::vector<std::string>& probeCommand, 216 std::string probeName, 217 std::shared_ptr<PerformScan>& scanPtr) : 218 recordRef(recordRef), _probeCommand(probeCommand), 219 probeName(std::move(probeName)), scan(scanPtr) 220 {} 221 PerformProbe::~PerformProbe() 222 { 223 FoundDevices foundDevs; 224 if (probe(_probeCommand, scan, foundDevs)) 225 { 226 scan->updateSystemConfiguration(recordRef, probeName, foundDevs); 227 } 228 } 229