1 /** 2 * Copyright © 2018 IBM 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 #include "config.h" 17 18 #include "manager.hpp" 19 20 #include "callout.hpp" 21 #include "policy_find.hpp" 22 23 #include <phosphor-logging/log.hpp> 24 25 namespace ibm 26 { 27 namespace logging 28 { 29 30 namespace fs = std::experimental::filesystem; 31 using namespace phosphor::logging; 32 using sdbusplus::exception::SdBusError; 33 34 Manager::Manager(sdbusplus::bus::bus& bus) : 35 bus(bus), 36 addMatch(bus, 37 sdbusplus::bus::match::rules::interfacesAdded() + 38 sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), 39 std::bind(std::mem_fn(&Manager::interfaceAdded), this, 40 std::placeholders::_1)), 41 removeMatch(bus, 42 sdbusplus::bus::match::rules::interfacesRemoved() + 43 sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), 44 std::bind(std::mem_fn(&Manager::interfaceRemoved), this, 45 std::placeholders::_1)) 46 #ifdef USE_POLICY_INTERFACE 47 , 48 policies(POLICY_JSON_PATH) 49 #endif 50 { 51 createAll(); 52 } 53 54 void Manager::createAll() 55 { 56 try 57 { 58 auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH); 59 60 for (const auto& object : objects) 61 { 62 const auto& interfaces = object.second; 63 64 auto propertyMap = interfaces.find(LOGGING_IFACE); 65 66 if (propertyMap != interfaces.end()) 67 { 68 createWithRestore(object.first, interfaces); 69 } 70 } 71 } 72 catch (const SdBusError& e) 73 { 74 log<level::ERR>("sdbusplus error getting logging managed objects", 75 entry("ERROR=%s", e.what())); 76 } 77 } 78 79 void Manager::createWithRestore(const std::string& objectPath, 80 const DbusInterfaceMap& interfaces) 81 { 82 createObject(objectPath, interfaces); 83 84 restoreCalloutObjects(objectPath, interfaces); 85 } 86 87 void Manager::create(const std::string& objectPath, 88 const DbusInterfaceMap& interfaces) 89 { 90 createObject(objectPath, interfaces); 91 92 createCalloutObjects(objectPath, interfaces); 93 } 94 95 void Manager::createObject(const std::string& objectPath, 96 const DbusInterfaceMap& interfaces) 97 { 98 #ifdef USE_POLICY_INTERFACE 99 auto logInterface = interfaces.find(LOGGING_IFACE); 100 createPolicyInterface(objectPath, logInterface->second); 101 #endif 102 } 103 104 void Manager::erase(EntryID id) 105 { 106 fs::remove_all(getSaveDir(id)); 107 childEntries.erase(id); 108 entries.erase(id); 109 } 110 111 void Manager::addInterface(const std::string& objectPath, InterfaceType type, 112 std::any& object) 113 { 114 auto id = getEntryID(objectPath); 115 auto entry = entries.find(id); 116 117 if (entry == entries.end()) 118 { 119 InterfaceMap interfaces; 120 interfaces.emplace(type, object); 121 entries.emplace(id, std::move(interfaces)); 122 } 123 else 124 { 125 entry->second.emplace(type, object); 126 } 127 } 128 129 void Manager::addChildInterface(const std::string& objectPath, 130 InterfaceType type, std::any& object) 131 { 132 auto id = getEntryID(objectPath); 133 auto entry = childEntries.find(id); 134 135 // childEntries is: 136 // A map of error log entry IDs to: 137 // a map of interface types to: 138 // a vector of interface objects 139 140 if (entry == childEntries.end()) 141 { 142 ObjectList objects{object}; 143 InterfaceMapMulti interfaces; 144 interfaces.emplace(type, std::move(objects)); 145 childEntries.emplace(id, std::move(interfaces)); 146 } 147 else 148 { 149 auto i = entry->second.find(type); 150 if (i == entry->second.end()) 151 { 152 ObjectList objects{objects}; 153 entry->second.emplace(type, objects); 154 } 155 else 156 { 157 i->second.emplace_back(object); 158 } 159 } 160 } 161 162 #ifdef USE_POLICY_INTERFACE 163 void Manager::createPolicyInterface(const std::string& objectPath, 164 const DbusPropertyMap& properties) 165 { 166 auto values = policy::find(policies, properties); 167 168 auto object = std::make_shared<PolicyObject>(bus, objectPath.c_str(), true); 169 170 object->eventID(std::get<policy::EIDField>(values)); 171 object->description(std::get<policy::MsgField>(values)); 172 173 object->emit_object_added(); 174 175 std::any anyObject = object; 176 177 addInterface(objectPath, InterfaceType::POLICY, anyObject); 178 } 179 #endif 180 181 void Manager::createCalloutObjects(const std::string& objectPath, 182 const DbusInterfaceMap& interfaces) 183 { 184 // Use the associations property in the org.openbmc.Associations 185 // interface to find any callouts. Then grab all properties on 186 // the Asset interface for that object in the inventory to use 187 // in our callout objects. 188 189 auto associations = interfaces.find(ASSOC_IFACE); 190 if (associations == interfaces.end()) 191 { 192 return; 193 } 194 195 const auto& properties = associations->second; 196 auto assocProperty = properties.find("associations"); 197 auto assocValue = 198 sdbusplus::message::variant_ns::get<AssociationsPropertyType>( 199 assocProperty->second); 200 201 auto id = getEntryID(objectPath); 202 auto calloutNum = 0; 203 DbusSubtree subtree; 204 205 for (const auto& association : assocValue) 206 { 207 try 208 { 209 if (std::get<forwardPos>(association) != "callout") 210 { 211 continue; 212 } 213 214 auto callout = std::get<endpointPos>(association); 215 216 if (subtree.empty()) 217 { 218 subtree = getSubtree(bus, "/", 0, ASSET_IFACE); 219 if (subtree.empty()) 220 { 221 break; 222 } 223 } 224 225 auto service = getService(callout, ASSET_IFACE, subtree); 226 if (service.empty()) 227 { 228 continue; 229 } 230 231 auto properties = 232 getAllProperties(bus, service, callout, ASSET_IFACE); 233 if (properties.empty()) 234 { 235 continue; 236 } 237 238 auto calloutPath = getCalloutObjectPath(objectPath, calloutNum); 239 240 auto object = std::make_shared<Callout>( 241 bus, calloutPath, callout, calloutNum, 242 getLogTimestamp(interfaces), properties); 243 244 auto dir = getCalloutSaveDir(id); 245 if (!fs::exists(dir)) 246 { 247 fs::create_directories(dir); 248 } 249 object->serialize(dir); 250 251 std::any anyObject = object; 252 addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); 253 calloutNum++; 254 } 255 catch (const SdBusError& e) 256 { 257 log<level::ERR>("sdbusplus exception", entry("ERROR=%s", e.what())); 258 } 259 } 260 } 261 262 void Manager::restoreCalloutObjects(const std::string& objectPath, 263 const DbusInterfaceMap& interfaces) 264 { 265 auto saveDir = getCalloutSaveDir(getEntryID(objectPath)); 266 267 if (!fs::exists(saveDir)) 268 { 269 return; 270 } 271 272 size_t id; 273 for (auto& f : fs::directory_iterator(saveDir)) 274 { 275 try 276 { 277 id = std::stoul(f.path().filename()); 278 } 279 catch (std::exception& e) 280 { 281 log<level::ERR>("Invalid IBM logging callout save file. Deleting", 282 entry("FILE=%s", f.path().c_str())); 283 fs::remove(f.path()); 284 continue; 285 } 286 287 auto path = getCalloutObjectPath(objectPath, id); 288 auto callout = std::make_shared<Callout>(bus, path, id, 289 getLogTimestamp(interfaces)); 290 if (callout->deserialize(saveDir)) 291 { 292 callout->emit_object_added(); 293 std::any anyObject = callout; 294 addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); 295 } 296 } 297 } 298 299 void Manager::interfaceAdded(sdbusplus::message::message& msg) 300 { 301 sdbusplus::message::object_path path; 302 DbusInterfaceMap interfaces; 303 304 msg.read(path, interfaces); 305 306 // Find the Logging.Entry interface with all of its properties 307 // to pass to create(). 308 if (interfaces.find(LOGGING_IFACE) != interfaces.end()) 309 { 310 create(path, interfaces); 311 } 312 } 313 314 uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces) 315 { 316 auto interface = interfaces.find(LOGGING_IFACE); 317 if (interface != interfaces.end()) 318 { 319 auto property = interface->second.find("Timestamp"); 320 if (property != interface->second.end()) 321 { 322 return sdbusplus::message::variant_ns::get<uint64_t>( 323 property->second); 324 } 325 } 326 327 return 0; 328 } 329 330 fs::path Manager::getSaveDir(EntryID id) 331 { 332 return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id); 333 } 334 335 fs::path Manager::getCalloutSaveDir(EntryID id) 336 { 337 return getSaveDir(id) / "callouts"; 338 } 339 340 std::string Manager::getCalloutObjectPath(const std::string& objectPath, 341 uint32_t calloutNum) 342 { 343 return fs::path{objectPath} / "callouts" / std::to_string(calloutNum); 344 } 345 346 void Manager::interfaceRemoved(sdbusplus::message::message& msg) 347 { 348 sdbusplus::message::object_path path; 349 DbusInterfaceList interfaces; 350 351 msg.read(path, interfaces); 352 353 // If the Logging.Entry interface was removed, then remove 354 // our object 355 356 auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE); 357 358 if (i != interfaces.end()) 359 { 360 erase(getEntryID(path)); 361 } 362 } 363 } // namespace logging 364 } // namespace ibm 365