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