blob: e17f7f05bc4b6a95b2c92f0788c35e1bc35a16e9 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2020 YADRO
#include "buffer_service.hpp"
#include <phosphor-logging/log.hpp>
#include <vector>
using namespace phosphor::logging;
// clang-format off
/** @brief Host state monitor properties.
* Used for automatic flushing the log buffer to the persistent file.
* Contains a list of properties and a set of their values that trigger the
* flush operation.
* For example, the current log buffer will be saved to a file when the
* "OperatingSystemState" property obtains one of the
* listed values ("xyz.openbmc_project...BootComplete", "Inactive", etc).
*/
static const DbusLoop::WatchProperties watchProperties{
{"xyz.openbmc_project.State.Host", {{
"RequestedHostTransition", {
"xyz.openbmc_project.State.Host.Transition.On"}}}},
{"xyz.openbmc_project.State.OperatingSystem.Status", {{
"OperatingSystemState", {
"xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.BootComplete",
"xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive",
"Inactive",
"Standby"}}}}
};
// clang-format on
BufferService::BufferService(const Config& config, DbusLoop& dbusLoop,
HostConsole& hostConsole, LogBuffer& logBuffer,
FileStorage& fileStorage) :
config(config),
dbusLoop(&dbusLoop), hostConsole(&hostConsole), logBuffer(&logBuffer),
fileStorage(&fileStorage)
{}
void BufferService::run()
{
if (config.bufFlushFull)
{
logBuffer->setFullHandler([this]() { this->flush(); });
}
hostConsole->connect();
// Add SIGUSR1 signal handler for manual flushing
dbusLoop->addSignalHandler(SIGUSR1, [this]() { this->flush(); });
// Add SIGTERM signal handler for service shutdown
dbusLoop->addSignalHandler(SIGTERM, [this]() { this->dbusLoop->stop(0); });
// Register callback for socket IO
dbusLoop->addIoHandler(*hostConsole, [this]() { this->readConsole(); });
// Register host state watcher
if (*config.hostState)
{
dbusLoop->addPropertyHandler(config.hostState, watchProperties,
[this]() { this->flush(); });
}
if (!*config.hostState && !config.bufFlushFull)
{
log<level::WARNING>("Automatic flush disabled");
}
log<level::DEBUG>("Initialization complete",
entry("SocketId=%s", config.socketId),
entry("BufMaxSize=%lu", config.bufMaxSize),
entry("BufMaxTime=%lu", config.bufMaxTime),
entry("BufFlushFull=%s", config.bufFlushFull ? "y" : "n"),
entry("HostState=%s", config.hostState),
entry("OutDir=%s", config.outDir),
entry("MaxFiles=%lu", config.maxFiles));
// Run D-Bus event loop
const int rc = dbusLoop->run();
if (!logBuffer->empty())
{
flush();
}
if (rc < 0)
{
std::error_code ec(-rc, std::generic_category());
throw std::system_error(ec, "Error in event loop");
}
}
void BufferService::flush()
{
if (logBuffer->empty())
{
log<level::INFO>("Ignore flush: buffer is empty");
return;
}
try
{
const std::string fileName = fileStorage->save(*logBuffer);
logBuffer->clear();
std::string msg = "Host logs flushed to ";
msg += fileName;
log<level::INFO>(msg.c_str());
}
catch (const std::exception& ex)
{
log<level::ERR>(ex.what());
}
}
void BufferService::readConsole()
{
constexpr size_t bufSize = 128; // enough for most line-oriented output
std::vector<char> bufData(bufSize);
char* buf = bufData.data();
try
{
while (const size_t rsz = hostConsole->read(buf, bufSize))
{
logBuffer->append(buf, rsz);
}
}
catch (const std::system_error& ex)
{
log<level::ERR>(ex.what());
}
}