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 33 Manager::Manager(sdbusplus::bus_t& bus) : 34 bus(bus), 35 addMatch(bus, 36 sdbusplus::bus::match::rules::interfacesAdded() + 37 sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), 38 std::bind(std::mem_fn(&Manager::interfaceAdded), this, 39 std::placeholders::_1)), 40 removeMatch(bus, 41 sdbusplus::bus::match::rules::interfacesRemoved() + 42 sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), 43 std::bind(std::mem_fn(&Manager::interfaceRemoved), this, 44 std::placeholders::_1)) 45 #ifdef USE_POLICY_INTERFACE 46 , 47 policies(POLICY_JSON_PATH) 48 #endif 49 { 50 createAll(); 51 } 52 53 void Manager::createAll() 54 { 55 try 56 { 57 auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH); 58 59 for (const auto& object : objects) 60 { 61 const auto& interfaces = object.second; 62 63 auto propertyMap = interfaces.find(LOGGING_IFACE); 64 65 if (propertyMap != interfaces.end()) 66 { 67 createWithRestore(object.first, interfaces); 68 } 69 } 70 } 71 catch (const sdbusplus::exception_t& e) 72 { 73 log<level::ERR>("sdbusplus error getting logging managed objects", 74 entry("ERROR=%s", e.what())); 75 } 76 } 77 78 void Manager::createWithRestore(const std::string& objectPath, 79 const DbusInterfaceMap& interfaces) 80 { 81 createObject(objectPath, interfaces); 82 83 restoreCalloutObjects(objectPath, interfaces); 84 } 85 86 void Manager::create(const std::string& objectPath, 87 const DbusInterfaceMap& interfaces) 88 { 89 createObject(objectPath, interfaces); 90 91 createCalloutObjects(objectPath, interfaces); 92 } 93 94 void Manager::createObject(const std::string& objectPath, 95 const DbusInterfaceMap& interfaces) 96 { 97 #ifdef USE_POLICY_INTERFACE 98 auto logInterface = interfaces.find(LOGGING_IFACE); 99 createPolicyInterface(objectPath, logInterface->second); 100 #endif 101 } 102 103 void Manager::erase(EntryID id) 104 { 105 fs::remove_all(getSaveDir(id)); 106 childEntries.erase(id); 107 entries.erase(id); 108 } 109 110 void Manager::addInterface(const std::string& objectPath, InterfaceType type, 111 std::any& object) 112 { 113 auto id = getEntryID(objectPath); 114 auto entry = entries.find(id); 115 116 if (entry == entries.end()) 117 { 118 InterfaceMap interfaces; 119 interfaces.emplace(type, object); 120 entries.emplace(id, std::move(interfaces)); 121 } 122 else 123 { 124 entry->second.emplace(type, object); 125 } 126 } 127 128 void Manager::addChildInterface(const std::string& objectPath, 129 InterfaceType type, std::any& object) 130 { 131 auto id = getEntryID(objectPath); 132 auto entry = childEntries.find(id); 133 134 // childEntries is: 135 // A map of error log entry IDs to: 136 // a map of interface types to: 137 // a vector of interface objects 138 139 if (entry == childEntries.end()) 140 { 141 ObjectList objects{object}; 142 InterfaceMapMulti interfaces; 143 interfaces.emplace(type, std::move(objects)); 144 childEntries.emplace(id, std::move(interfaces)); 145 } 146 else 147 { 148 auto i = entry->second.find(type); 149 if (i == entry->second.end()) 150 { 151 ObjectList objects{object}; 152 entry->second.emplace(type, objects); 153 } 154 else 155 { 156 i->second.emplace_back(object); 157 } 158 } 159 } 160 161 #ifdef USE_POLICY_INTERFACE 162 void Manager::createPolicyInterface(const std::string& objectPath, 163 const DbusPropertyMap& properties) 164 { 165 auto values = policy::find(policies, properties); 166 167 auto object = std::make_shared<PolicyObject>( 168 bus, objectPath.c_str(), PolicyObject::action::defer_emit); 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 sdbusplus::exception_t& 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 (const 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_t& 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_t& 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