blob: a5610e400ce646f54ee821b963615faf5f966b6b [file] [log] [blame]
Artem Senicheve8837d52020-06-07 11:59:04 +03001// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2020 YADRO
3
4#include "service.hpp"
5
6#include <phosphor-logging/log.hpp>
7
8#include <vector>
9
10using namespace phosphor::logging;
11
12// clang-format off
Artem Senichev90608dc2021-04-07 15:32:24 +030013/** @brief Host state monitor properties.
14 * Used for automatic flushing the log buffer to the persistent file.
15 * Contains a list of properties and a set of their values that trigger the
16 * flush operation.
17 * For example, the current log buffer will be saved to a file when the
18 * "OperatingSystemState" property obtains one of the
19 * listed values ("xyz.openbmc_project...BootComplete", "Inactive", etc).
20 */
Artem Senicheve8837d52020-06-07 11:59:04 +030021static const DbusLoop::WatchProperties watchProperties{
22 {"xyz.openbmc_project.State.Host", {{
23 "RequestedHostTransition", {
24 "xyz.openbmc_project.State.Host.Transition.On"}}}},
25 {"xyz.openbmc_project.State.OperatingSystem.Status", {{
26 "OperatingSystemState", {
27 "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.BootComplete",
Artem Senichev90608dc2021-04-07 15:32:24 +030028 "xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive",
29 "Inactive",
30 "Standby"}}}}
Artem Senicheve8837d52020-06-07 11:59:04 +030031};
32// clang-format on
33
34Service::Service(const Config& config) :
35 config(config), hostConsole(config.socketId),
36 logBuffer(config.bufMaxSize, config.bufMaxTime),
37 fileStorage(config.outDir, config.socketId, config.maxFiles)
38{}
39
40void Service::run()
41{
42 if (config.bufFlushFull)
43 {
44 logBuffer.setFullHandler([this]() { this->flush(); });
45 }
46
47 hostConsole.connect();
48
49 // Add SIGUSR1 signal handler for manual flushing
50 dbusLoop.addSignalHandler(SIGUSR1, [this]() { this->flush(); });
51 // Add SIGTERM signal handler for service shutdown
52 dbusLoop.addSignalHandler(SIGTERM, [this]() { this->dbusLoop.stop(0); });
53
54 // Register callback for socket IO
55 dbusLoop.addIoHandler(hostConsole, [this]() { this->readConsole(); });
56
57 // Register host state watcher
58 if (*config.hostState)
59 {
60 dbusLoop.addPropertyHandler(config.hostState, watchProperties,
61 [this]() { this->flush(); });
62 }
63
64 if (!*config.hostState && !config.bufFlushFull)
65 {
66 log<level::WARNING>("Automatic flush disabled");
67 }
68
69 log<level::DEBUG>("Initialization complete",
70 entry("SocketId=%s", config.socketId),
71 entry("BufMaxSize=%lu", config.bufMaxSize),
72 entry("BufMaxTime=%lu", config.bufMaxTime),
73 entry("BufFlushFull=%s", config.bufFlushFull ? "y" : "n"),
74 entry("HostState=%s", config.hostState),
75 entry("OutDir=%s", config.outDir),
76 entry("MaxFiles=%lu", config.maxFiles));
77
78 // Run D-Bus event loop
79 const int rc = dbusLoop.run();
80 if (!logBuffer.empty())
81 {
82 flush();
83 }
84 if (rc < 0)
85 {
86 std::error_code ec(-rc, std::generic_category());
87 throw std::system_error(ec, "Error in event loop");
88 }
89}
90
91void Service::flush()
92{
93 if (logBuffer.empty())
94 {
95 log<level::INFO>("Ignore flush: buffer is empty");
96 return;
97 }
98 try
99 {
100 const std::string fileName = fileStorage.save(logBuffer);
101 logBuffer.clear();
102
103 std::string msg = "Host logs flushed to ";
104 msg += fileName;
105 log<level::INFO>(msg.c_str());
106 }
107 catch (const std::exception& ex)
108 {
109 log<level::ERR>(ex.what());
110 }
111}
112
113void Service::readConsole()
114{
115 constexpr size_t bufSize = 128; // enough for most line-oriented output
116 std::vector<char> bufData(bufSize);
117 char* buf = bufData.data();
118
119 try
120 {
121 while (const size_t rsz = hostConsole.read(buf, bufSize))
122 {
123 logBuffer.append(buf, rsz);
124 }
125 }
126 catch (const std::system_error& ex)
127 {
128 log<level::ERR>(ex.what());
129 }
130}