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 = std::get<AssociationsPropertyType>(assocProperty->second); 198 199 auto id = getEntryID(objectPath); 200 auto calloutNum = 0; 201 DbusSubtree subtree; 202 203 for (const auto& association : assocValue) 204 { 205 try 206 { 207 if (std::get<forwardPos>(association) != "callout") 208 { 209 continue; 210 } 211 212 auto callout = std::get<endpointPos>(association); 213 214 if (subtree.empty()) 215 { 216 subtree = getSubtree(bus, "/", 0, ASSET_IFACE); 217 if (subtree.empty()) 218 { 219 break; 220 } 221 } 222 223 auto service = getService(callout, ASSET_IFACE, subtree); 224 if (service.empty()) 225 { 226 continue; 227 } 228 229 auto properties = 230 getAllProperties(bus, service, callout, ASSET_IFACE); 231 if (properties.empty()) 232 { 233 continue; 234 } 235 236 auto calloutPath = getCalloutObjectPath(objectPath, calloutNum); 237 238 auto object = std::make_shared<Callout>( 239 bus, calloutPath, callout, calloutNum, 240 getLogTimestamp(interfaces), properties); 241 242 auto dir = getCalloutSaveDir(id); 243 if (!fs::exists(dir)) 244 { 245 fs::create_directories(dir); 246 } 247 object->serialize(dir); 248 249 std::any anyObject = object; 250 addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); 251 calloutNum++; 252 } 253 catch (const SdBusError& e) 254 { 255 log<level::ERR>("sdbusplus exception", entry("ERROR=%s", e.what())); 256 } 257 } 258 } 259 260 void Manager::restoreCalloutObjects(const std::string& objectPath, 261 const DbusInterfaceMap& interfaces) 262 { 263 auto saveDir = getCalloutSaveDir(getEntryID(objectPath)); 264 265 if (!fs::exists(saveDir)) 266 { 267 return; 268 } 269 270 size_t id; 271 for (auto& f : fs::directory_iterator(saveDir)) 272 { 273 try 274 { 275 id = std::stoul(f.path().filename()); 276 } 277 catch (std::exception& e) 278 { 279 log<level::ERR>("Invalid IBM logging callout save file. Deleting", 280 entry("FILE=%s", f.path().c_str())); 281 fs::remove(f.path()); 282 continue; 283 } 284 285 auto path = getCalloutObjectPath(objectPath, id); 286 auto callout = std::make_shared<Callout>(bus, path, id, 287 getLogTimestamp(interfaces)); 288 if (callout->deserialize(saveDir)) 289 { 290 callout->emit_object_added(); 291 std::any anyObject = callout; 292 addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); 293 } 294 } 295 } 296 297 void Manager::interfaceAdded(sdbusplus::message::message& msg) 298 { 299 sdbusplus::message::object_path path; 300 DbusInterfaceMap interfaces; 301 302 msg.read(path, interfaces); 303 304 // Find the Logging.Entry interface with all of its properties 305 // to pass to create(). 306 if (interfaces.find(LOGGING_IFACE) != interfaces.end()) 307 { 308 create(path, interfaces); 309 } 310 } 311 312 uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces) 313 { 314 auto interface = interfaces.find(LOGGING_IFACE); 315 if (interface != interfaces.end()) 316 { 317 auto property = interface->second.find("Timestamp"); 318 if (property != interface->second.end()) 319 { 320 return std::get<uint64_t>(property->second); 321 } 322 } 323 324 return 0; 325 } 326 327 fs::path Manager::getSaveDir(EntryID id) 328 { 329 return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id); 330 } 331 332 fs::path Manager::getCalloutSaveDir(EntryID id) 333 { 334 return getSaveDir(id) / "callouts"; 335 } 336 337 std::string Manager::getCalloutObjectPath(const std::string& objectPath, 338 uint32_t calloutNum) 339 { 340 return fs::path{objectPath} / "callouts" / std::to_string(calloutNum); 341 } 342 343 void Manager::interfaceRemoved(sdbusplus::message::message& msg) 344 { 345 sdbusplus::message::object_path path; 346 DbusInterfaceList interfaces; 347 348 msg.read(path, interfaces); 349 350 // If the Logging.Entry interface was removed, then remove 351 // our object 352 353 auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE); 354 355 if (i != interfaces.end()) 356 { 357 erase(getEntryID(path)); 358 } 359 } 360 } // namespace logging 361 } // namespace ibm 362