blob: 95fe8ed9bc51eed13f98409c9387970376a78fca [file] [log] [blame]
Dhruvaraj Subhashchandran4a959842021-09-30 02:55:52 -05001#pragma once
2
3#include "dump-extensions/openpower-dumps/openpower_dumps_config.h"
4
5#include "dump_manager_bmcstored.hpp"
6#include "dump_utils.hpp"
7#include "host_dump_entry.hpp"
8#include "op_dump_util.hpp"
9#include "watch.hpp"
10#include "xyz/openbmc_project/Common/error.hpp"
11#include "xyz/openbmc_project/Dump/Create/error.hpp"
12#include "xyz/openbmc_project/Dump/NewDump/server.hpp"
13
14#include <fmt/core.h>
15#include <sys/inotify.h>
16#include <unistd.h>
17
18#include <com/ibm/Dump/Create/server.hpp>
19#include <phosphor-logging/elog-errors.hpp>
20#include <phosphor-logging/elog.hpp>
21#include <sdeventplus/exception.hpp>
22#include <sdeventplus/source/base.hpp>
23#include <sdeventplus/source/child.hpp>
24#include <xyz/openbmc_project/Dump/Create/server.hpp>
25
26#include <ctime>
27#include <filesystem>
28#include <regex>
29
30namespace openpower
31{
32namespace dump
33{
34namespace hostdump
35{
36
37using namespace sdbusplus::xyz::openbmc_project::Common::Error;
38using namespace phosphor::logging;
39
40constexpr auto INVALID_DUMP_SIZE = 0;
41constexpr auto HOST_DUMP_COMMON_FILENAME_PART =
42 "_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)";
43
44using CreateIface = sdbusplus::server::object::object<
45 sdbusplus::xyz::openbmc_project::Dump::server::Create,
46 sdbusplus::com::ibm::Dump::server::Create,
47 sdbusplus::xyz::openbmc_project::Dump::server::NewDump>;
48
49using UserMap = phosphor::dump::inotify::UserMap;
50
51using Watch = phosphor::dump::inotify::Watch;
52using ::sdeventplus::source::Child;
53
54using originatorTypes = sdbusplus::xyz::openbmc_project::Common::server::
55 OriginatedBy::OriginatorTypes;
56
57/** @class Manager
58 * @brief Host Dump manager implementation.
59 * @details A concrete implementation for the
60 * xyz.openbmc_project.Dump.Create
61 * com::ibm::Dump::Create and
62 * xyz::openbmc_project::Dump::NewDump D-Bus APIs
63 */
64template <typename T>
65class Manager :
66 virtual public CreateIface,
67 public phosphor::dump::bmc_stored::Manager
68{
69 public:
70 Manager() = delete;
71 Manager(const Manager&) = default;
72 Manager& operator=(const Manager&) = delete;
73 Manager(Manager&&) = delete;
74 Manager& operator=(Manager&&) = delete;
75 virtual ~Manager() = default;
76
77 /** @brief Constructor to put object onto bus at a dbus path.
78 * @param[in] bus - Bus to attach to.
79 * @param[in] event - Dump manager sd_event loop.
80 * @param[in] path - Path to attach at.
81 * @param[in] baseEntryPath - Base path for dump entry.
82 * @param[in] filePath - Path where the dumps are stored.
83 * @param[in] dumpNamePrefix - Prefix to the dump filename
84 * @param[in] dumpTempFileDir - Temporary location of dump files
85 * @param[in] maxDumpSize - Maximum allowed size of dump file
86 * @param[in] minDumpSize - Minimum size of a usable dump
87 * @param[in] allocatedSize - Total allocated space for the dump.
88 */
89 Manager(sdbusplus::bus::bus& bus, const phosphor::dump::EventPtr& event,
90 const char* path, const std::string& baseEntryPath,
91 const char* filePath, const std::string dumpNamePrefix,
92 const std::string dumpTempFileDir, const uint64_t maxDumpSize,
93 const uint64_t minDumpSize, const uint64_t allocatedSize) :
94 CreateIface(bus, path),
95 phosphor::dump::bmc_stored::Manager(
96 bus, event, path, baseEntryPath, filePath,
97 dumpNamePrefix + HOST_DUMP_COMMON_FILENAME_PART, maxDumpSize,
98 minDumpSize, allocatedSize),
99 dumpNamePrefix(dumpNamePrefix), dumpTempFileDir(dumpTempFileDir)
100 {}
101
102 /** @brief Implementation for CreateDump
103 * Method to create a host dump entry when user requests for a
104 * new host dump
105 *
106 * @return object_path - The object path of the new dump entry.
107 */
108 sdbusplus::message::object_path
109 createDump(phosphor::dump::DumpCreateParams params) override
110 {
111 using InvalidArgument =
112 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
113 using Argument = xyz::openbmc_project::Common::InvalidArgument;
114 if (!params.empty())
115 {
116 log<level::ERR>(fmt::format("Dump type({}) accepts no additional "
117 "parameters, number of parameters({})",
118 dumpNamePrefix, params.size())
119 .c_str());
120 elog<InvalidArgument>(
121 Argument::ARGUMENT_NAME("NO_PARAMETERS_NEEDED"),
122 Argument::ARGUMENT_VALUE("INVALID_PARAMETERS"));
123 }
124
125 // Check dump policy
126 util::isOPDumpsEnabled();
127
128 auto size = getAllowedSize();
129
130 uint32_t id = ++lastEntryId;
131
132 // Entry Object path.
133 auto objPath =
134 std::filesystem::path(baseEntryPath) / std::to_string(id);
135
136 log<level::INFO>(fmt::format("Create dump type({}) with id({}) "
137 "available space: ({}) kilobytes",
138 dumpNamePrefix, id, size)
139 .c_str());
140
141 std::time_t timeStamp = std::time(nullptr);
142 createEntry(id, objPath, timeStamp, 0, std::string(),
143 phosphor::dump::OperationStatus::InProgress, std::string(),
144 originatorTypes::Internal);
145
146 return objPath.string();
147 }
148
149 /** @brief Notify the host dump manager about creation of a new dump.
150 * @param[in] dumpId - Id from the source of the dump.
151 * @param[in] size - Size of the dump.
152 */
153 void notify(uint32_t dumpId, uint64_t) override
154 {
155 try
156 {
157 captureDump(dumpId);
158 }
159 catch (std::exception& e)
160 {
161 log<level::ERR>(
162 fmt::format("Failed to package dump({}): id({}) errorMsg({})",
163 dumpNamePrefix, dumpId, e.what())
164 .c_str());
165 throw std::runtime_error("Failed to package dump");
166 }
167 }
168 /** @brief Create a Dump Entry Object
169 * @param[in] id - Id of the dump
170 * @param[in] objPath - Object path to attach to
171 * @param[in] ms - Dump creation timestamp since the epoch.
172 * @param[in] fileSize - Dump file size in bytes.
173 * @param[in] file - Name of dump file.
174 * @param[in] status - status of the dump.
175 * @param[in] originatorId - Id of the originator of the dump
176 * @param[in] originatorType - Originator type
177 */
178
179 virtual void createEntry(const uint32_t id, const std::string objPath,
180 const uint64_t ms, uint64_t fileSize,
181 const std::filesystem::path& file,
182 phosphor::dump::OperationStatus status,
183 std::string originatorId,
184 originatorTypes originatorType) override
185 {
186 try
187 {
188 entries.insert(std::make_pair(
189 id, std::make_unique<openpower::dump::hostdump::Entry<T>>(
190 bus, objPath.c_str(), id, ms, fileSize, file, status,
191 originatorId, originatorType, *this)));
192 }
193 catch (const std::invalid_argument& e)
194 {
195 log<level::ERR>(fmt::format("Error in creating host dump entry, "
196 "errormsg({}), OBJECTPATH({}), ID({})",
197 e.what(), objPath.c_str(), id)
198 .c_str());
199 throw std::runtime_error("Error in creating host dump entry");
200 }
201 }
202
203 private:
204 std::string dumpNamePrefix;
205 std::string dumpTempFileDir;
206
207 void captureDump(uint32_t dumpId)
208 {
209 std::string idStr;
210 try
211 {
212 idStr = std::to_string(dumpId);
213 }
214 catch (std::exception& e)
215 {
216 log<level::ERR>("Dump capture: Error converting idto string");
217 throw std::runtime_error(
218 "Dump capture: Error converting dump id to string");
219 }
220
221 // Get Dump size.
222 // TODO #ibm-openbmc/issues/3061
223 // Dump request will be rejected if there is not enough space for
224 // one complete dump, change this behavior to crate a partial dump
225 // with available space.
226 auto size = getAllowedSize();
227
228 auto dumpTempPath = std::filesystem::path(dumpTempFileDir) / idStr;
229
230 pid_t pid = fork();
231 if (pid == 0)
232 {
233 std::filesystem::path dumpPath(dumpDir);
234 dumpPath /= idStr;
235 execl("/usr/bin/opdreport", "opdreport", "-d", dumpPath.c_str(),
236 "-i", idStr.c_str(), "-s", std::to_string(size).c_str(), "-q",
237 "-v", "-p", dumpTempPath.c_str(), "-n",
238 dumpNamePrefix.c_str(), nullptr);
239
240 // opdreport script execution is failed.
241 auto error = errno;
242 log<level::ERR>(
243 fmt::format(
244 "Dump capture: Error occurred during "
245 "opdreport function execution, errno({}), dumpPrefix({}), "
246 "dumpPath({}), dumpSourcePath({}), allowedSize({})",
247 error, dumpNamePrefix.c_str(), dumpPath.c_str(),
248 dumpTempPath.c_str(), size)
249 .c_str());
250 throw std::runtime_error("Dump capture: Error occured during "
251 "opdreport script execution");
252 }
253 else if (pid > 0)
254 {
255 phosphor::dump::Entry* dumpEntry = NULL;
256 auto dumpIt = entries.find(dumpId);
257 if (dumpIt != entries.end())
258 {
259 dumpEntry = dumpIt->second.get();
260 }
261 Child::Callback callback = [this, dumpEntry,
262 pid](Child&, const siginfo_t* si) {
263 // Set progress as failed if packaging return error
264 if (si->si_status != 0)
265 {
266 log<level::ERR>("Dump packaging failed");
267 if (dumpEntry != nullptr)
268 {
269 reinterpret_cast<phosphor::dump::Entry*>(dumpEntry)
270 ->status(phosphor::dump::OperationStatus::Failed);
271 }
272 }
273 else
274 {
275 log<level::INFO>("Dump packaging completed");
276 }
277 this->childPtrMap.erase(pid);
278 };
279 try
280 {
281 childPtrMap.emplace(
282 pid, std::make_unique<Child>(eventLoop.get(), pid,
283 WEXITED | WSTOPPED,
284 std::move(callback)));
285 }
286 catch (const sdeventplus::SdEventError& ex)
287 {
288 // Failed to add to event loop
289 log<level::ERR>(
290 fmt::format("Dump capture: Error occurred during "
291 "the sdeventplus::source::Child ex({})",
292 ex.what())
293 .c_str());
294 throw std::runtime_error(
295 "Dump capture: Error occurred during the "
296 "sdeventplus::source::Child creation");
297 }
298 }
299 else
300 {
301 auto error = errno;
302 log<level::ERR>(
303 fmt::format(
304 "Dump capture: Error occurred during fork, errno({})",
305 error)
306 .c_str());
307 throw std::runtime_error(
308 "Dump capture: Error occurred during fork");
309 }
310 }
311};
312
313} // namespace hostdump
314} // namespace dump
315} // namespace openpower