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