1e0017ebbSMatt Spinler /** 2e0017ebbSMatt Spinler * Copyright © 2018 IBM Corporation 3e0017ebbSMatt Spinler * 4e0017ebbSMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 5e0017ebbSMatt Spinler * you may not use this file except in compliance with the License. 6e0017ebbSMatt Spinler * You may obtain a copy of the License at 7e0017ebbSMatt Spinler * 8e0017ebbSMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 9e0017ebbSMatt Spinler * 10e0017ebbSMatt Spinler * Unless required by applicable law or agreed to in writing, software 11e0017ebbSMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 12e0017ebbSMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e0017ebbSMatt Spinler * See the License for the specific language governing permissions and 14e0017ebbSMatt Spinler * limitations under the License. 15e0017ebbSMatt Spinler */ 16*60f53d86SMatt Spinler #include <phosphor-logging/log.hpp> 1752ee71bdSMatt Spinler #include "callout.hpp" 18e0017ebbSMatt Spinler #include "config.h" 19e0017ebbSMatt Spinler #include "manager.hpp" 204a6ea6afSMatt Spinler #include "policy_find.hpp" 21e0017ebbSMatt Spinler 22e0017ebbSMatt Spinler namespace ibm 23e0017ebbSMatt Spinler { 24e0017ebbSMatt Spinler namespace logging 25e0017ebbSMatt Spinler { 26e0017ebbSMatt Spinler 2752ee71bdSMatt Spinler namespace fs = std::experimental::filesystem; 2852ee71bdSMatt Spinler 29e0017ebbSMatt Spinler Manager::Manager(sdbusplus::bus::bus& bus) : 30e0017ebbSMatt Spinler bus(bus), 31259e7277SMatt Spinler addMatch(bus, 32e0017ebbSMatt Spinler sdbusplus::bus::match::rules::interfacesAdded() + 33e0017ebbSMatt Spinler sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), 34259e7277SMatt Spinler std::bind(std::mem_fn(&Manager::interfaceAdded), this, 35055da3bdSMatt Spinler std::placeholders::_1)), 36055da3bdSMatt Spinler removeMatch(bus, 37055da3bdSMatt Spinler sdbusplus::bus::match::rules::interfacesRemoved() + 38055da3bdSMatt Spinler sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH), 39055da3bdSMatt Spinler std::bind(std::mem_fn(&Manager::interfaceRemoved), this, 40259e7277SMatt Spinler std::placeholders::_1)) 41743e5822SMatt Spinler #ifdef USE_POLICY_INTERFACE 42259e7277SMatt Spinler , 43259e7277SMatt Spinler policies(POLICY_JSON_PATH) 44743e5822SMatt Spinler #endif 45e0017ebbSMatt Spinler { 4654bfa7e7SMatt Spinler createAll(); 4754bfa7e7SMatt Spinler } 4854bfa7e7SMatt Spinler 4954bfa7e7SMatt Spinler void Manager::createAll() 5054bfa7e7SMatt Spinler { 51259e7277SMatt Spinler auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH); 5254bfa7e7SMatt Spinler 5354bfa7e7SMatt Spinler for (const auto& object : objects) 5454bfa7e7SMatt Spinler { 5554bfa7e7SMatt Spinler const auto& interfaces = object.second; 5654bfa7e7SMatt Spinler 57e6a51590SMatt Spinler auto propertyMap = interfaces.find(LOGGING_IFACE); 5854bfa7e7SMatt Spinler 5954bfa7e7SMatt Spinler if (propertyMap != interfaces.end()) 6054bfa7e7SMatt Spinler { 61e6a51590SMatt Spinler createWithRestore(object.first, interfaces); 6254bfa7e7SMatt Spinler } 6354bfa7e7SMatt Spinler } 6454bfa7e7SMatt Spinler } 6554bfa7e7SMatt Spinler 66e6a51590SMatt Spinler void Manager::createWithRestore(const std::string& objectPath, 67e6a51590SMatt Spinler const DbusInterfaceMap& interfaces) 68e6a51590SMatt Spinler { 69e6a51590SMatt Spinler createObject(objectPath, interfaces); 70e6a51590SMatt Spinler 71*60f53d86SMatt Spinler restoreCalloutObjects(objectPath, interfaces); 72e6a51590SMatt Spinler } 73e6a51590SMatt Spinler 74259e7277SMatt Spinler void Manager::create(const std::string& objectPath, 75e6a51590SMatt Spinler const DbusInterfaceMap& interfaces) 7654bfa7e7SMatt Spinler { 77e6a51590SMatt Spinler createObject(objectPath, interfaces); 784a6ea6afSMatt Spinler 7952ee71bdSMatt Spinler createCalloutObjects(objectPath, interfaces); 80e6a51590SMatt Spinler } 81e6a51590SMatt Spinler 82e6a51590SMatt Spinler void Manager::createObject(const std::string& objectPath, 83e6a51590SMatt Spinler const DbusInterfaceMap& interfaces) 84e6a51590SMatt Spinler { 854a6ea6afSMatt Spinler #ifdef USE_POLICY_INTERFACE 86e6a51590SMatt Spinler auto logInterface = interfaces.find(LOGGING_IFACE); 87e6a51590SMatt Spinler createPolicyInterface(objectPath, logInterface->second); 884a6ea6afSMatt Spinler #endif 89e0017ebbSMatt Spinler } 90e0017ebbSMatt Spinler 91a1390353SMatt Spinler void Manager::erase(EntryID id) 92a1390353SMatt Spinler { 9352ee71bdSMatt Spinler fs::remove_all(getSaveDir(id)); 94677143bbSMatt Spinler childEntries.erase(id); 95a1390353SMatt Spinler entries.erase(id); 96a1390353SMatt Spinler } 97a1390353SMatt Spinler 98491fc6f1SMatt Spinler void Manager::addInterface(const std::string& objectPath, InterfaceType type, 99491fc6f1SMatt Spinler std::experimental::any& object) 100491fc6f1SMatt Spinler { 101491fc6f1SMatt Spinler auto id = getEntryID(objectPath); 102491fc6f1SMatt Spinler auto entry = entries.find(id); 103491fc6f1SMatt Spinler 104491fc6f1SMatt Spinler if (entry == entries.end()) 105491fc6f1SMatt Spinler { 106491fc6f1SMatt Spinler InterfaceMap interfaces; 107491fc6f1SMatt Spinler interfaces.emplace(type, object); 108491fc6f1SMatt Spinler entries.emplace(id, std::move(interfaces)); 109491fc6f1SMatt Spinler } 110491fc6f1SMatt Spinler else 111491fc6f1SMatt Spinler { 112491fc6f1SMatt Spinler entry->second.emplace(type, object); 113491fc6f1SMatt Spinler } 114491fc6f1SMatt Spinler } 115491fc6f1SMatt Spinler 116677143bbSMatt Spinler void Manager::addChildInterface(const std::string& objectPath, 117677143bbSMatt Spinler InterfaceType type, 118677143bbSMatt Spinler std::experimental::any& object) 119677143bbSMatt Spinler { 120677143bbSMatt Spinler auto id = getEntryID(objectPath); 121677143bbSMatt Spinler auto entry = childEntries.find(id); 122677143bbSMatt Spinler 123677143bbSMatt Spinler // childEntries is: 124677143bbSMatt Spinler // A map of error log entry IDs to: 125677143bbSMatt Spinler // a map of interface types to: 126677143bbSMatt Spinler // a vector of interface objects 127677143bbSMatt Spinler 128677143bbSMatt Spinler if (entry == childEntries.end()) 129677143bbSMatt Spinler { 130677143bbSMatt Spinler ObjectList objects{object}; 131677143bbSMatt Spinler InterfaceMapMulti interfaces; 132677143bbSMatt Spinler interfaces.emplace(type, std::move(objects)); 133677143bbSMatt Spinler childEntries.emplace(id, std::move(interfaces)); 134677143bbSMatt Spinler } 135677143bbSMatt Spinler else 136677143bbSMatt Spinler { 137677143bbSMatt Spinler auto i = entry->second.find(type); 138677143bbSMatt Spinler if (i == entry->second.end()) 139677143bbSMatt Spinler { 140677143bbSMatt Spinler ObjectList objects{objects}; 141677143bbSMatt Spinler entry->second.emplace(type, objects); 142677143bbSMatt Spinler } 143677143bbSMatt Spinler else 144677143bbSMatt Spinler { 145677143bbSMatt Spinler i->second.emplace_back(object); 146677143bbSMatt Spinler } 147677143bbSMatt Spinler } 148677143bbSMatt Spinler } 149677143bbSMatt Spinler 1504a6ea6afSMatt Spinler #ifdef USE_POLICY_INTERFACE 151259e7277SMatt Spinler void Manager::createPolicyInterface(const std::string& objectPath, 1524a6ea6afSMatt Spinler const DbusPropertyMap& properties) 1534a6ea6afSMatt Spinler { 1544a6ea6afSMatt Spinler auto values = policy::find(policies, properties); 1554a6ea6afSMatt Spinler 156259e7277SMatt Spinler auto object = std::make_shared<PolicyObject>(bus, objectPath.c_str(), true); 1574a6ea6afSMatt Spinler 1584a6ea6afSMatt Spinler object->eventID(std::get<policy::EIDField>(values)); 1594a6ea6afSMatt Spinler object->description(std::get<policy::MsgField>(values)); 1604a6ea6afSMatt Spinler 1619bea4eafSMatt Spinler object->emit_object_added(); 1629bea4eafSMatt Spinler 163491fc6f1SMatt Spinler std::experimental::any anyObject = object; 1644a6ea6afSMatt Spinler 165491fc6f1SMatt Spinler addInterface(objectPath, InterfaceType::POLICY, anyObject); 1664a6ea6afSMatt Spinler } 1674a6ea6afSMatt Spinler #endif 1684a6ea6afSMatt Spinler 16952ee71bdSMatt Spinler void Manager::createCalloutObjects(const std::string& objectPath, 17052ee71bdSMatt Spinler const DbusInterfaceMap& interfaces) 17152ee71bdSMatt Spinler { 17252ee71bdSMatt Spinler // Use the associations property in the org.openbmc.Associations 17352ee71bdSMatt Spinler // interface to find any callouts. Then grab all properties on 17452ee71bdSMatt Spinler // the Asset interface for that object in the inventory to use 17552ee71bdSMatt Spinler // in our callout objects. 17652ee71bdSMatt Spinler 17752ee71bdSMatt Spinler auto associations = interfaces.find(ASSOC_IFACE); 17852ee71bdSMatt Spinler if (associations == interfaces.end()) 17952ee71bdSMatt Spinler { 18052ee71bdSMatt Spinler return; 18152ee71bdSMatt Spinler } 18252ee71bdSMatt Spinler 18352ee71bdSMatt Spinler const auto& properties = associations->second; 18452ee71bdSMatt Spinler auto assocProperty = properties.find("associations"); 18552ee71bdSMatt Spinler auto assocValue = assocProperty->second.get<AssociationsPropertyType>(); 18652ee71bdSMatt Spinler 18752ee71bdSMatt Spinler auto id = getEntryID(objectPath); 18852ee71bdSMatt Spinler auto calloutNum = 0; 18952ee71bdSMatt Spinler DbusSubtree subtree; 19052ee71bdSMatt Spinler 19152ee71bdSMatt Spinler for (const auto& association : assocValue) 19252ee71bdSMatt Spinler { 19352ee71bdSMatt Spinler if (std::get<forwardPos>(association) != "callout") 19452ee71bdSMatt Spinler { 19552ee71bdSMatt Spinler continue; 19652ee71bdSMatt Spinler } 19752ee71bdSMatt Spinler 19852ee71bdSMatt Spinler auto callout = std::get<endpointPos>(association); 19952ee71bdSMatt Spinler 20052ee71bdSMatt Spinler if (subtree.empty()) 20152ee71bdSMatt Spinler { 20252ee71bdSMatt Spinler subtree = getSubtree(bus, "/", 0, ASSET_IFACE); 20352ee71bdSMatt Spinler if (subtree.empty()) 20452ee71bdSMatt Spinler { 20552ee71bdSMatt Spinler break; 20652ee71bdSMatt Spinler } 20752ee71bdSMatt Spinler } 20852ee71bdSMatt Spinler 20952ee71bdSMatt Spinler auto service = getService(callout, ASSET_IFACE, subtree); 21052ee71bdSMatt Spinler if (service.empty()) 21152ee71bdSMatt Spinler { 21252ee71bdSMatt Spinler continue; 21352ee71bdSMatt Spinler } 21452ee71bdSMatt Spinler 21552ee71bdSMatt Spinler auto properties = getAllProperties(bus, service, callout, ASSET_IFACE); 21652ee71bdSMatt Spinler if (properties.empty()) 21752ee71bdSMatt Spinler { 21852ee71bdSMatt Spinler continue; 21952ee71bdSMatt Spinler } 22052ee71bdSMatt Spinler 22152ee71bdSMatt Spinler auto calloutPath = getCalloutObjectPath(objectPath, calloutNum); 22252ee71bdSMatt Spinler 22352ee71bdSMatt Spinler auto object = 22452ee71bdSMatt Spinler std::make_shared<Callout>(bus, calloutPath, callout, calloutNum, 22552ee71bdSMatt Spinler getLogTimestamp(interfaces), properties); 22652ee71bdSMatt Spinler 22752ee71bdSMatt Spinler auto dir = getCalloutSaveDir(id); 22852ee71bdSMatt Spinler if (!fs::exists(dir)) 22952ee71bdSMatt Spinler { 23052ee71bdSMatt Spinler fs::create_directories(dir); 23152ee71bdSMatt Spinler } 23252ee71bdSMatt Spinler object->serialize(dir); 23352ee71bdSMatt Spinler 23452ee71bdSMatt Spinler std::experimental::any anyObject = object; 23552ee71bdSMatt Spinler addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); 23652ee71bdSMatt Spinler calloutNum++; 23752ee71bdSMatt Spinler } 23852ee71bdSMatt Spinler } 23952ee71bdSMatt Spinler 240*60f53d86SMatt Spinler void Manager::restoreCalloutObjects(const std::string& objectPath, 241*60f53d86SMatt Spinler const DbusInterfaceMap& interfaces) 242*60f53d86SMatt Spinler { 243*60f53d86SMatt Spinler auto saveDir = getCalloutSaveDir(getEntryID(objectPath)); 244*60f53d86SMatt Spinler 245*60f53d86SMatt Spinler if (!fs::exists(saveDir)) 246*60f53d86SMatt Spinler { 247*60f53d86SMatt Spinler return; 248*60f53d86SMatt Spinler } 249*60f53d86SMatt Spinler 250*60f53d86SMatt Spinler size_t id; 251*60f53d86SMatt Spinler for (auto& f : fs::directory_iterator(saveDir)) 252*60f53d86SMatt Spinler { 253*60f53d86SMatt Spinler try 254*60f53d86SMatt Spinler { 255*60f53d86SMatt Spinler id = std::stoul(f.path().filename()); 256*60f53d86SMatt Spinler } 257*60f53d86SMatt Spinler catch (std::exception& e) 258*60f53d86SMatt Spinler { 259*60f53d86SMatt Spinler using namespace phosphor::logging; 260*60f53d86SMatt Spinler log<level::ERR>("Invalid IBM logging callout save file. Deleting", 261*60f53d86SMatt Spinler entry("FILE=%s", f.path().c_str())); 262*60f53d86SMatt Spinler fs::remove(f.path()); 263*60f53d86SMatt Spinler continue; 264*60f53d86SMatt Spinler } 265*60f53d86SMatt Spinler 266*60f53d86SMatt Spinler auto path = getCalloutObjectPath(objectPath, id); 267*60f53d86SMatt Spinler auto callout = std::make_shared<Callout>(bus, path, id, 268*60f53d86SMatt Spinler getLogTimestamp(interfaces)); 269*60f53d86SMatt Spinler if (callout->deserialize(saveDir)) 270*60f53d86SMatt Spinler { 271*60f53d86SMatt Spinler callout->emit_object_added(); 272*60f53d86SMatt Spinler std::experimental::any anyObject = callout; 273*60f53d86SMatt Spinler addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject); 274*60f53d86SMatt Spinler } 275*60f53d86SMatt Spinler } 276*60f53d86SMatt Spinler } 277*60f53d86SMatt Spinler 278e0017ebbSMatt Spinler void Manager::interfaceAdded(sdbusplus::message::message& msg) 279e0017ebbSMatt Spinler { 280e0017ebbSMatt Spinler sdbusplus::message::object_path path; 281e0017ebbSMatt Spinler DbusInterfaceMap interfaces; 282e0017ebbSMatt Spinler 283e0017ebbSMatt Spinler msg.read(path, interfaces); 284e0017ebbSMatt Spinler 285e0017ebbSMatt Spinler // Find the Logging.Entry interface with all of its properties 286e0017ebbSMatt Spinler // to pass to create(). 287e6a51590SMatt Spinler if (interfaces.find(LOGGING_IFACE) != interfaces.end()) 288e0017ebbSMatt Spinler { 289e6a51590SMatt Spinler create(path, interfaces); 290e0017ebbSMatt Spinler } 291e0017ebbSMatt Spinler } 292055da3bdSMatt Spinler 29352ee71bdSMatt Spinler uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces) 29452ee71bdSMatt Spinler { 29552ee71bdSMatt Spinler auto interface = interfaces.find(LOGGING_IFACE); 29652ee71bdSMatt Spinler if (interface != interfaces.end()) 29752ee71bdSMatt Spinler { 29852ee71bdSMatt Spinler auto property = interface->second.find("Timestamp"); 29952ee71bdSMatt Spinler if (property != interface->second.end()) 30052ee71bdSMatt Spinler { 30152ee71bdSMatt Spinler return property->second.get<uint64_t>(); 30252ee71bdSMatt Spinler } 30352ee71bdSMatt Spinler } 30452ee71bdSMatt Spinler 30552ee71bdSMatt Spinler return 0; 30652ee71bdSMatt Spinler } 30752ee71bdSMatt Spinler 30852ee71bdSMatt Spinler fs::path Manager::getSaveDir(EntryID id) 30952ee71bdSMatt Spinler { 31052ee71bdSMatt Spinler return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id); 31152ee71bdSMatt Spinler } 31252ee71bdSMatt Spinler 31352ee71bdSMatt Spinler fs::path Manager::getCalloutSaveDir(EntryID id) 31452ee71bdSMatt Spinler { 31552ee71bdSMatt Spinler return getSaveDir(id) / "callouts"; 31652ee71bdSMatt Spinler } 31752ee71bdSMatt Spinler 31852ee71bdSMatt Spinler std::string Manager::getCalloutObjectPath(const std::string& objectPath, 31952ee71bdSMatt Spinler uint32_t calloutNum) 32052ee71bdSMatt Spinler { 32152ee71bdSMatt Spinler return fs::path{objectPath} / "callouts" / std::to_string(calloutNum); 32252ee71bdSMatt Spinler } 32352ee71bdSMatt Spinler 324055da3bdSMatt Spinler void Manager::interfaceRemoved(sdbusplus::message::message& msg) 325055da3bdSMatt Spinler { 326055da3bdSMatt Spinler sdbusplus::message::object_path path; 327055da3bdSMatt Spinler DbusInterfaceList interfaces; 328055da3bdSMatt Spinler 329055da3bdSMatt Spinler msg.read(path, interfaces); 330055da3bdSMatt Spinler 331055da3bdSMatt Spinler // If the Logging.Entry interface was removed, then remove 332055da3bdSMatt Spinler // our object 333055da3bdSMatt Spinler 334055da3bdSMatt Spinler auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE); 335055da3bdSMatt Spinler 336055da3bdSMatt Spinler if (i != interfaces.end()) 337055da3bdSMatt Spinler { 338a1390353SMatt Spinler erase(getEntryID(path)); 339055da3bdSMatt Spinler } 340055da3bdSMatt Spinler } 341e0017ebbSMatt Spinler } 342e0017ebbSMatt Spinler } 343