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