blob: fd421f0e2a686e803c9d45faa158c03dc4712745 [file] [log] [blame]
/**
* Copyright © 2018 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include "manager.hpp"
#include "callout.hpp"
#include "policy_find.hpp"
#include <phosphor-logging/log.hpp>
namespace ibm
{
namespace logging
{
namespace fs = std::experimental::filesystem;
using namespace phosphor::logging;
Manager::Manager(sdbusplus::bus_t& bus) :
bus(bus),
addMatch(bus,
sdbusplus::bus::match::rules::interfacesAdded() +
sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH),
std::bind(std::mem_fn(&Manager::interfaceAdded), this,
std::placeholders::_1)),
removeMatch(bus,
sdbusplus::bus::match::rules::interfacesRemoved() +
sdbusplus::bus::match::rules::path_namespace(LOGGING_PATH),
std::bind(std::mem_fn(&Manager::interfaceRemoved), this,
std::placeholders::_1))
#ifdef USE_POLICY_INTERFACE
,
policies(POLICY_JSON_PATH)
#endif
{
createAll();
}
void Manager::createAll()
{
try
{
auto objects = getManagedObjects(bus, LOGGING_BUSNAME, LOGGING_PATH);
for (const auto& object : objects)
{
const auto& interfaces = object.second;
auto propertyMap = interfaces.find(LOGGING_IFACE);
if (propertyMap != interfaces.end())
{
createWithRestore(object.first, interfaces);
}
}
}
catch (const sdbusplus::exception_t& e)
{
log<level::ERR>("sdbusplus error getting logging managed objects",
entry("ERROR=%s", e.what()));
}
}
void Manager::createWithRestore(const std::string& objectPath,
const DbusInterfaceMap& interfaces)
{
createObject(objectPath, interfaces);
restoreCalloutObjects(objectPath, interfaces);
}
void Manager::create(const std::string& objectPath,
const DbusInterfaceMap& interfaces)
{
createObject(objectPath, interfaces);
createCalloutObjects(objectPath, interfaces);
}
void Manager::createObject(const std::string& objectPath,
const DbusInterfaceMap& interfaces)
{
#ifdef USE_POLICY_INTERFACE
auto logInterface = interfaces.find(LOGGING_IFACE);
createPolicyInterface(objectPath, logInterface->second);
#endif
}
void Manager::erase(EntryID id)
{
fs::remove_all(getSaveDir(id));
childEntries.erase(id);
entries.erase(id);
}
void Manager::addInterface(const std::string& objectPath, InterfaceType type,
std::any& object)
{
auto id = getEntryID(objectPath);
auto entry = entries.find(id);
if (entry == entries.end())
{
InterfaceMap interfaces;
interfaces.emplace(type, object);
entries.emplace(id, std::move(interfaces));
}
else
{
entry->second.emplace(type, object);
}
}
void Manager::addChildInterface(const std::string& objectPath,
InterfaceType type, std::any& object)
{
auto id = getEntryID(objectPath);
auto entry = childEntries.find(id);
// childEntries is:
// A map of error log entry IDs to:
// a map of interface types to:
// a vector of interface objects
if (entry == childEntries.end())
{
ObjectList objects{object};
InterfaceMapMulti interfaces;
interfaces.emplace(type, std::move(objects));
childEntries.emplace(id, std::move(interfaces));
}
else
{
auto i = entry->second.find(type);
if (i == entry->second.end())
{
ObjectList objects{object};
entry->second.emplace(type, objects);
}
else
{
i->second.emplace_back(object);
}
}
}
#ifdef USE_POLICY_INTERFACE
void Manager::createPolicyInterface(const std::string& objectPath,
const DbusPropertyMap& properties)
{
auto values = policy::find(policies, properties);
auto object = std::make_shared<PolicyObject>(
bus, objectPath.c_str(), PolicyObject::action::defer_emit);
object->eventID(std::get<policy::EIDField>(values));
object->description(std::get<policy::MsgField>(values));
object->emit_object_added();
std::any anyObject = object;
addInterface(objectPath, InterfaceType::POLICY, anyObject);
}
#endif
void Manager::createCalloutObjects(const std::string& objectPath,
const DbusInterfaceMap& interfaces)
{
// Use the associations property in the org.openbmc.Associations
// interface to find any callouts. Then grab all properties on
// the Asset interface for that object in the inventory to use
// in our callout objects.
auto associations = interfaces.find(ASSOC_IFACE);
if (associations == interfaces.end())
{
return;
}
const auto& properties = associations->second;
auto assocProperty = properties.find("Associations");
auto assocValue = std::get<AssociationsPropertyType>(assocProperty->second);
auto id = getEntryID(objectPath);
auto calloutNum = 0;
DbusSubtree subtree;
for (const auto& association : assocValue)
{
try
{
if (std::get<forwardPos>(association) != "callout")
{
continue;
}
auto callout = std::get<endpointPos>(association);
if (subtree.empty())
{
subtree = getSubtree(bus, "/", 0, ASSET_IFACE);
if (subtree.empty())
{
break;
}
}
auto service = getService(callout, ASSET_IFACE, subtree);
if (service.empty())
{
continue;
}
auto properties =
getAllProperties(bus, service, callout, ASSET_IFACE);
if (properties.empty())
{
continue;
}
auto calloutPath = getCalloutObjectPath(objectPath, calloutNum);
auto object = std::make_shared<Callout>(
bus, calloutPath, callout, calloutNum,
getLogTimestamp(interfaces), properties);
auto dir = getCalloutSaveDir(id);
if (!fs::exists(dir))
{
fs::create_directories(dir);
}
object->serialize(dir);
std::any anyObject = object;
addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
calloutNum++;
}
catch (const sdbusplus::exception_t& e)
{
log<level::ERR>("sdbusplus exception", entry("ERROR=%s", e.what()));
}
}
}
void Manager::restoreCalloutObjects(const std::string& objectPath,
const DbusInterfaceMap& interfaces)
{
auto saveDir = getCalloutSaveDir(getEntryID(objectPath));
if (!fs::exists(saveDir))
{
return;
}
size_t id;
for (auto& f : fs::directory_iterator(saveDir))
{
try
{
id = std::stoul(f.path().filename());
}
catch (const std::exception& e)
{
log<level::ERR>("Invalid IBM logging callout save file. Deleting",
entry("FILE=%s", f.path().c_str()));
fs::remove(f.path());
continue;
}
auto path = getCalloutObjectPath(objectPath, id);
auto callout = std::make_shared<Callout>(bus, path, id,
getLogTimestamp(interfaces));
if (callout->deserialize(saveDir))
{
callout->emit_object_added();
std::any anyObject = callout;
addChildInterface(objectPath, InterfaceType::CALLOUT, anyObject);
}
}
}
void Manager::interfaceAdded(sdbusplus::message_t& msg)
{
sdbusplus::message::object_path path;
DbusInterfaceMap interfaces;
msg.read(path, interfaces);
// Find the Logging.Entry interface with all of its properties
// to pass to create().
if (interfaces.find(LOGGING_IFACE) != interfaces.end())
{
create(path, interfaces);
}
}
uint64_t Manager::getLogTimestamp(const DbusInterfaceMap& interfaces)
{
auto interface = interfaces.find(LOGGING_IFACE);
if (interface != interfaces.end())
{
auto property = interface->second.find("Timestamp");
if (property != interface->second.end())
{
return std::get<uint64_t>(property->second);
}
}
return 0;
}
fs::path Manager::getSaveDir(EntryID id)
{
return fs::path{ERRLOG_PERSIST_PATH} / std::to_string(id);
}
fs::path Manager::getCalloutSaveDir(EntryID id)
{
return getSaveDir(id) / "callouts";
}
std::string Manager::getCalloutObjectPath(const std::string& objectPath,
uint32_t calloutNum)
{
return fs::path{objectPath} / "callouts" / std::to_string(calloutNum);
}
void Manager::interfaceRemoved(sdbusplus::message_t& msg)
{
sdbusplus::message::object_path path;
DbusInterfaceList interfaces;
msg.read(path, interfaces);
// If the Logging.Entry interface was removed, then remove
// our object
auto i = std::find(interfaces.begin(), interfaces.end(), LOGGING_IFACE);
if (i != interfaces.end())
{
erase(getEntryID(path));
}
}
} // namespace logging
} // namespace ibm