blob: ed57b87df6ff8d59a92eb64fb3c2a043f5e8c11c [file] [log] [blame]
/**
* @brief D-Bus signal watcher.
*
* This file is part of HostLogger project.
*
* Copyright (c) 2018 YADRO
*
* 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 "dbus_watch.hpp"
#include "config.hpp"
#include <chrono>
#include <set>
#include <string>
// D-Bus path to the host state object
#define DBUS_HOST_OBJECT_PATH "/xyz/openbmc_project/state/host0"
// Macro to normalize SDBus status code:
// positive code is not an error in the systemd dbus implementation.
#define DBUS_RC_TO_ERR(c) (c = (c <= 0 ? -c : 0))
DbusWatcher::DbusWatcher(LogManager& logManager, sdbusplus::bus::bus& bus) :
logManager_(logManager), bus_(bus)
{
}
int DbusWatcher::initialize()
{
int rc;
// Add IO callback for host's log stream socket
rc = sd_event_add_io(bus_.get_event(), NULL, logManager_.getHostLogFd(),
EPOLLIN, &DbusWatcher::ioCallback, this);
if (DBUS_RC_TO_ERR(rc))
{
fprintf(stderr, "Unable to add IO handler: %i %s\n", rc, strerror(rc));
return rc;
}
// Add flush handler
if (loggerConfig.flushPeriod == 0)
registerEventHandler();
else
rc = registerTimerHandler();
return rc;
}
void DbusWatcher::registerEventHandler()
{
conds_["xyz.openbmc_project.State.Host"] = {
.property = "RequestedHostTransition",
.values = {"xyz.openbmc_project.State.Host.Transition.On"}};
conds_["xyz.openbmc_project.State.OperatingSystem.Status"] = {
.property = "OperatingSystemState",
.values = {"xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
"BootComplete",
"xyz.openbmc_project.State.OperatingSystem.Status.OSStatus."
"Inactive"}};
for (auto& cond : conds_)
{
cond.second.match = std::make_unique<sdbusplus::bus::match_t>(
bus_,
sdbusplus::bus::match::rules::propertiesChanged(
DBUS_HOST_OBJECT_PATH, cond.first),
[this](auto& msg) { this->hostStateHandler(msg); });
}
}
int DbusWatcher::registerTimerHandler()
{
int rc;
sd_event_source* ev = NULL;
rc = sd_event_add_time(bus_.get_event(), &ev, CLOCK_MONOTONIC, UINT64_MAX,
0, &DbusWatcher::timerCallback, this);
if (DBUS_RC_TO_ERR(rc))
{
fprintf(stderr, "Unable to add timer handler: %i %s\n", rc,
strerror(rc));
return rc;
}
rc = sd_event_source_set_enabled(ev, SD_EVENT_ON);
if (DBUS_RC_TO_ERR(rc))
{
fprintf(stderr, "Unable to enable timer handler: %i %s\n", rc,
strerror(rc));
return rc;
}
return setupTimer(ev);
}
int DbusWatcher::setupTimer(sd_event_source* event)
{
// Get the current time and add the delta (flush period)
using namespace std::chrono;
auto now = steady_clock::now().time_since_epoch();
hours timeOut(loggerConfig.flushPeriod);
auto expireTime =
duration_cast<microseconds>(now) + duration_cast<microseconds>(timeOut);
// Set the time
int rc = sd_event_source_set_time(event, expireTime.count());
if (DBUS_RC_TO_ERR(rc))
fprintf(stderr, "Unable to set timer handler: %i %s\n", rc,
strerror(rc));
return rc;
}
void DbusWatcher::hostStateHandler(sdbusplus::message::message& msg)
{
std::map<std::string, std::variant<std::string>> properties;
std::string interface;
msg.read(interface, properties);
bool needFlush = false;
const auto itc = conds_.find(interface);
if (itc != conds_.end())
{
const auto itp = properties.find(itc->second.property);
if (itp != properties.end())
{
const auto& propVal = std::get<std::string>(itp->second);
needFlush =
itc->second.values.find(propVal) != itc->second.values.end();
}
}
if (needFlush)
logManager_.flush();
}
int DbusWatcher::ioCallback(sd_event_source* /*event*/, int /*fd*/,
uint32_t /*revents*/, void* data)
{
DbusWatcher* instance = static_cast<DbusWatcher*>(data);
instance->logManager_.handleHostLog();
return 0;
}
int DbusWatcher::timerCallback(sd_event_source* event, uint64_t /*usec*/,
void* data)
{
DbusWatcher* instance = static_cast<DbusWatcher*>(data);
instance->logManager_.flush();
return instance->setupTimer(event);
}