blob: e06c1f6b153c53920b42b72bb1b2912f6bccefac [file] [log] [blame]
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -05001#include "config.h"
2
3#include "dump_manager_bmc.hpp"
4
5#include "bmc_dump_entry.hpp"
6#include "dump_internal.hpp"
7#include "xyz/openbmc_project/Common/error.hpp"
8#include "xyz/openbmc_project/Dump/Create/error.hpp"
9
George Liu858fbb22021-07-01 12:25:44 +080010#include <fmt/core.h>
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050011#include <sys/inotify.h>
12#include <unistd.h>
13
14#include <phosphor-logging/elog-errors.hpp>
15#include <phosphor-logging/elog.hpp>
Jayanth Othayoth0af74a52021-04-08 03:55:21 -050016
Tim Leebb9366d2021-06-24 14:00:07 +080017#include <cmath>
Jayanth Othayoth0af74a52021-04-08 03:55:21 -050018#include <ctime>
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050019#include <regex>
20
21namespace phosphor
22{
23namespace dump
24{
25namespace bmc
26{
27
28using namespace sdbusplus::xyz::openbmc_project::Common::Error;
29using namespace phosphor::logging;
30
Marri Devender Rao73953b82022-02-15 09:15:42 -060031bool Manager::fUserDumpInProgress = false;
32
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050033namespace internal
34{
35
36void Manager::create(Type type, std::vector<std::string> fullPaths)
37{
38 dumpMgr.phosphor::dump::bmc::Manager::captureDump(type, fullPaths);
39}
40
41} // namespace internal
42
Dhruvaraj Subhashchandran969f9a52020-10-30 01:42:39 -050043sdbusplus::message::object_path
Dhruvaraj Subhashchandranddc33662021-07-19 09:28:42 -050044 Manager::createDump(phosphor::dump::DumpCreateParams params)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050045{
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050046 if (params.size() > CREATE_DUMP_MAX_PARAMS)
Dhruvaraj Subhashchandran969f9a52020-10-30 01:42:39 -050047 {
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050048 log<level::WARNING>(
49 "BMC dump accepts not more than 2 additional parameters");
Dhruvaraj Subhashchandran969f9a52020-10-30 01:42:39 -050050 }
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050051
Marri Devender Rao73953b82022-02-15 09:15:42 -060052 if (Manager::fUserDumpInProgress == true)
53 {
54 elog<sdbusplus::xyz::openbmc_project::Common::Error::Unavailable>();
55 }
56
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050057 // Get the originator id and type from params
58 std::string originatorId;
59 originatorTypes originatorType;
60
61 phosphor::dump::extractOriginatorProperties(params, originatorId,
62 originatorType);
63
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050064 std::vector<std::string> paths;
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050065 auto id = captureDump(Type::UserRequested, paths);
66
67 // Entry Object path.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -050068 auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050069
70 try
71 {
Claire Weinanc0ab9d42022-08-17 23:01:07 -070072 uint64_t timeStamp =
73 std::chrono::duration_cast<std::chrono::microseconds>(
74 std::chrono::system_clock::now().time_since_epoch())
75 .count();
76
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050077 entries.insert(std::make_pair(
Dhruvaraj Subhashchandrana6ab8062020-10-29 15:29:10 -050078 id, std::make_unique<bmc::Entry>(
79 bus, objPath.c_str(), id, timeStamp, 0, std::string(),
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050080 phosphor::dump::OperationStatus::InProgress, originatorId,
81 originatorType, *this)));
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050082 }
83 catch (const std::invalid_argument& e)
84 {
George Liu858fbb22021-07-01 12:25:44 +080085 log<level::ERR>(fmt::format("Error in creating dump entry, "
86 "errormsg({}), OBJECTPATH({}), ID({})",
87 e.what(), objPath.c_str(), id)
88 .c_str());
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050089 elog<InternalFailure>();
90 }
91
Marri Devender Rao73953b82022-02-15 09:15:42 -060092 Manager::fUserDumpInProgress = true;
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050093 return objPath.string();
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050094}
95
96uint32_t Manager::captureDump(Type type,
97 const std::vector<std::string>& fullPaths)
98{
99 // Get Dump size.
100 auto size = getAllowedSize();
101
102 pid_t pid = fork();
103
104 if (pid == 0)
105 {
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500106 std::filesystem::path dumpPath(dumpDir);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500107 auto id = std::to_string(lastEntryId + 1);
108 dumpPath /= id;
109
110 // get dreport type map entry
111 auto tempType = TypeMap.find(type);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500112 execl("/usr/bin/dreport", "dreport", "-d", dumpPath.c_str(), "-i",
113 id.c_str(), "-s", std::to_string(size).c_str(), "-q", "-v", "-p",
114 fullPaths.empty() ? "" : fullPaths.front().c_str(), "-t",
115 tempType->second.c_str(), nullptr);
116
117 // dreport script execution is failed.
118 auto error = errno;
Marri Devender Rao73953b82022-02-15 09:15:42 -0600119 log<level::ERR>(fmt::format("Error occurred during dreport "
120 "function execution, errno({})",
121 error)
122 .c_str());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500123 elog<InternalFailure>();
124 }
125 else if (pid > 0)
126 {
Marri Devender Rao73953b82022-02-15 09:15:42 -0600127 // local variable goes out of scope using pointer, callback method
128 // need to dellocate the pointer
129 Type* typePtr = new Type();
130 *typePtr = type;
131 int rc = sd_event_add_child(eventLoop.get(), nullptr, pid,
132 WEXITED | WSTOPPED, callback,
133 reinterpret_cast<void*>(typePtr));
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500134 if (0 > rc)
135 {
136 // Failed to add to event loop
Marri Devender Rao73953b82022-02-15 09:15:42 -0600137 log<level::ERR>(fmt::format("Error occurred during the "
138 "sd_event_add_child call, rc({})",
139 rc)
140 .c_str());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500141 elog<InternalFailure>();
142 }
143 }
144 else
145 {
146 auto error = errno;
George Liu858fbb22021-07-01 12:25:44 +0800147 log<level::ERR>(
148 fmt::format("Error occurred during fork, errno({})", error)
149 .c_str());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500150 elog<InternalFailure>();
151 }
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500152 return ++lastEntryId;
153}
154
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500155void Manager::createEntry(const std::filesystem::path& file)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500156{
157 // Dump File Name format obmcdump_ID_EPOCHTIME.EXT
158 static constexpr auto ID_POS = 1;
159 static constexpr auto EPOCHTIME_POS = 2;
160 std::regex file_regex("obmcdump_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)");
161
162 std::smatch match;
163 std::string name = file.filename();
164
165 if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
166 {
George Liu858fbb22021-07-01 12:25:44 +0800167 log<level::ERR>(fmt::format("Invalid Dump file name, FILENAME({})",
168 file.filename().c_str())
169 .c_str());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500170 return;
171 }
172
173 auto idString = match[ID_POS];
Xie Ning56bd7972022-02-25 15:20:02 +0800174 uint64_t timestamp = stoull(match[EPOCHTIME_POS]) * 1000 * 1000;
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500175
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500176 auto id = stoul(idString);
177
178 // If there is an existing entry update it and return.
179 auto dumpEntry = entries.find(id);
180 if (dumpEntry != entries.end())
181 {
182 dynamic_cast<phosphor::dump::bmc::Entry*>(dumpEntry->second.get())
Xie Ning56bd7972022-02-25 15:20:02 +0800183 ->update(timestamp, std::filesystem::file_size(file), file);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500184 return;
185 }
186
187 // Entry Object path.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500188 auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500189
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -0500190 // TODO: Get the persisted originator id & type
191 // For now, replacing it with null
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500192 try
193 {
Dhruvaraj Subhashchandrana6ab8062020-10-29 15:29:10 -0500194 entries.insert(std::make_pair(
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500195 id, std::make_unique<bmc::Entry>(
Xie Ning56bd7972022-02-25 15:20:02 +0800196 bus, objPath.c_str(), id, timestamp,
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500197 std::filesystem::file_size(file), file,
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -0500198 phosphor::dump::OperationStatus::Completed, std::string(),
199 originatorTypes::Internal, *this)));
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500200 }
201 catch (const std::invalid_argument& e)
202 {
George Liu858fbb22021-07-01 12:25:44 +0800203 log<level::ERR>(
Marri Devender Rao73953b82022-02-15 09:15:42 -0600204 fmt::format("Error in creating dump entry, errormsg({}), "
205 "OBJECTPATH({}), "
206 "ID({}), TIMESTAMP({}), SIZE({}), FILENAME({})",
207 e.what(), objPath.c_str(), id, timestamp,
208 std::filesystem::file_size(file),
209 file.filename().c_str())
George Liu858fbb22021-07-01 12:25:44 +0800210 .c_str());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500211 return;
212 }
213}
214
215void Manager::watchCallback(const UserMap& fileInfo)
216{
217 for (const auto& i : fileInfo)
218 {
219 // For any new dump file create dump entry object
220 // and associated inotify watch.
221 if (IN_CLOSE_WRITE == i.second)
222 {
Chirag Sharma4cb07992022-05-09 04:37:22 -0500223 if (!std::filesystem::is_directory(i.first))
224 {
225 // Don't require filename to be passed, as the path
226 // of dump directory is stored in the childWatchMap
227 removeWatch(i.first.parent_path());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500228
Chirag Sharma4cb07992022-05-09 04:37:22 -0500229 // dump file is written now create D-Bus entry
230 createEntry(i.first);
231 }
232 else
233 {
234 removeWatch(i.first);
235 }
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500236 }
237 // Start inotify watch on newly created directory.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500238 else if ((IN_CREATE == i.second) &&
239 std::filesystem::is_directory(i.first))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500240 {
241 auto watchObj = std::make_unique<Watch>(
242 eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
243 std::bind(
244 std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
245 this, std::placeholders::_1));
246
247 childWatchMap.emplace(i.first, std::move(watchObj));
248 }
249 }
250}
251
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500252void Manager::removeWatch(const std::filesystem::path& path)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500253{
254 // Delete Watch entry from map.
255 childWatchMap.erase(path);
256}
257
258void Manager::restore()
259{
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500260 std::filesystem::path dir(dumpDir);
261 if (!std::filesystem::exists(dir) || std::filesystem::is_empty(dir))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500262 {
263 return;
264 }
265
266 // Dump file path: <DUMP_PATH>/<id>/<filename>
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500267 for (const auto& p : std::filesystem::directory_iterator(dir))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500268 {
269 auto idStr = p.path().filename().string();
270
271 // Consider only directory's with dump id as name.
272 // Note: As per design one file per directory.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500273 if ((std::filesystem::is_directory(p.path())) &&
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500274 std::all_of(idStr.begin(), idStr.end(), ::isdigit))
275 {
276 lastEntryId =
277 std::max(lastEntryId, static_cast<uint32_t>(std::stoul(idStr)));
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500278 auto fileIt = std::filesystem::directory_iterator(p.path());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500279 // Create dump entry d-bus object.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500280 if (fileIt != std::filesystem::end(fileIt))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500281 {
282 createEntry(fileIt->path());
283 }
284 }
285 }
286}
287
Xie Ningfc69f352022-05-17 16:06:52 +0800288size_t getDirectorySize(const std::string dir)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500289{
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500290 auto size = 0;
Xie Ningfc69f352022-05-17 16:06:52 +0800291 for (const auto& p : std::filesystem::recursive_directory_iterator(dir))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500292 {
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500293 if (!std::filesystem::is_directory(p))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500294 {
Tim Leebb9366d2021-06-24 14:00:07 +0800295 size += std::ceil(std::filesystem::file_size(p) / 1024.0);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500296 }
297 }
Xie Ningfc69f352022-05-17 16:06:52 +0800298 return size;
299}
300
301size_t Manager::getAllowedSize()
302{
303 // Get current size of the dump directory.
304 auto size = getDirectorySize(dumpDir);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500305
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500306 // Set the Dump size to Maximum if the free space is greater than
307 // Dump max size otherwise return the available size.
308
309 size = (size > BMC_DUMP_TOTAL_SIZE ? 0 : BMC_DUMP_TOTAL_SIZE - size);
310
Xie Ningfc69f352022-05-17 16:06:52 +0800311#ifdef BMC_DUMP_ROTATE_CONFIG
312 // Delete the first existing file until the space is enough
313 while (size < BMC_DUMP_MIN_SPACE_REQD)
314 {
315 auto delEntry = min_element(
316 entries.begin(), entries.end(),
317 [](const auto& l, const auto& r) { return l.first < r.first; });
318 auto delPath =
319 std::filesystem::path(dumpDir) / std::to_string(delEntry->first);
320
321 size += getDirectorySize(delPath);
322
323 delEntry->second->delete_();
324 }
325#else
326 using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
327 using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
328
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500329 if (size < BMC_DUMP_MIN_SPACE_REQD)
330 {
331 // Reached to maximum limit
332 elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
333 }
Xie Ningfc69f352022-05-17 16:06:52 +0800334#endif
335
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500336 if (size > BMC_DUMP_MAX_SIZE)
337 {
338 size = BMC_DUMP_MAX_SIZE;
339 }
340
341 return size;
342}
343
344} // namespace bmc
345} // namespace dump
346} // namespace phosphor