blob: d41979a811de64f5537b51c166b5b91a2a2d8044 [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"
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -05007#include "dump_types.hpp"
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -05008#include "xyz/openbmc_project/Common/error.hpp"
9#include "xyz/openbmc_project/Dump/Create/error.hpp"
10
11#include <sys/inotify.h>
12#include <unistd.h>
13
14#include <phosphor-logging/elog-errors.hpp>
15#include <phosphor-logging/elog.hpp>
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -050016#include <phosphor-logging/lg2.hpp>
Marri Devender Rao3ed02c32022-06-28 23:12:14 -050017#include <sdeventplus/exception.hpp>
18#include <sdeventplus/source/base.hpp>
Jayanth Othayoth0af74a52021-04-08 03:55:21 -050019
Tim Leebb9366d2021-06-24 14:00:07 +080020#include <cmath>
Jayanth Othayoth0af74a52021-04-08 03:55:21 -050021#include <ctime>
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050022#include <regex>
23
24namespace phosphor
25{
26namespace dump
27{
28namespace bmc
29{
30
31using namespace sdbusplus::xyz::openbmc_project::Common::Error;
32using namespace phosphor::logging;
33
Marri Devender Rao73953b82022-02-15 09:15:42 -060034bool Manager::fUserDumpInProgress = false;
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -050035constexpr auto BMC_DUMP = "BMC_DUMP";
Marri Devender Rao73953b82022-02-15 09:15:42 -060036
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050037namespace internal
38{
39
40void Manager::create(Type type, std::vector<std::string> fullPaths)
41{
42 dumpMgr.phosphor::dump::bmc::Manager::captureDump(type, fullPaths);
43}
44
45} // namespace internal
46
Dhruvaraj Subhashchandran969f9a52020-10-30 01:42:39 -050047sdbusplus::message::object_path
Dhruvaraj Subhashchandranddc33662021-07-19 09:28:42 -050048 Manager::createDump(phosphor::dump::DumpCreateParams params)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050049{
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050050 if (params.size() > CREATE_DUMP_MAX_PARAMS)
Dhruvaraj Subhashchandran969f9a52020-10-30 01:42:39 -050051 {
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -050052 lg2::warning("BMC dump accepts not more than 2 additional parameters");
Dhruvaraj Subhashchandran969f9a52020-10-30 01:42:39 -050053 }
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -050054
55 // Get the originator id and type from params
56 std::string originatorId;
57 originatorTypes originatorType;
58
59 phosphor::dump::extractOriginatorProperties(params, originatorId,
60 originatorType);
61
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -050062 using CreateParameters =
63 sdbusplus::common::xyz::openbmc_project::dump::Create::CreateParameters;
64
65 DumpTypes dumpType = DumpTypes::USER;
66 std::string type = extractParameter<std::string>(
67 convertCreateParametersToString(CreateParameters::DumpType), params);
68 if (!type.empty())
69 {
70 dumpType = validateDumpType(type, BMC_DUMP);
71 }
Dhruvaraj Subhashchandrane4350f92023-06-29 05:57:47 -050072
73 if (dumpType == DumpTypes::ELOG)
74 {
75 dumpType = getErrorDumpType(params);
76 }
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -050077 std::string path = extractParameter<std::string>(
78 convertCreateParametersToString(CreateParameters::FilePath), params);
79
80 if ((Manager::fUserDumpInProgress == true) && (dumpType == DumpTypes::USER))
81 {
82 lg2::info("Another user initiated dump in progress");
83 elog<sdbusplus::xyz::openbmc_project::Common::Error::Unavailable>();
84 }
85
86 lg2::info("Initiating new BMC dump with type: {TYPE} path: {PATH}", "TYPE",
87 dumpTypeToString(dumpType).value(), "PATH", path);
88
89 auto id = captureDump(dumpType, path);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050090
91 // Entry Object path.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -050092 auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050093
94 try
95 {
Claire Weinanc0ab9d42022-08-17 23:01:07 -070096 uint64_t timeStamp =
97 std::chrono::duration_cast<std::chrono::microseconds>(
98 std::chrono::system_clock::now().time_since_epoch())
99 .count();
100
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500101 entries.insert(std::make_pair(
Dhruvaraj Subhashchandrana6ab8062020-10-29 15:29:10 -0500102 id, std::make_unique<bmc::Entry>(
103 bus, objPath.c_str(), id, timeStamp, 0, std::string(),
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -0500104 phosphor::dump::OperationStatus::InProgress, originatorId,
105 originatorType, *this)));
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500106 }
107 catch (const std::invalid_argument& e)
108 {
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500109 lg2::error("Error in creating dump entry, errormsg: {ERROR}, "
110 "OBJECTPATH: {OBJECT_PATH}, ID: {ID}",
111 "ERROR", e, "OBJECT_PATH", objPath, "ID", id);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500112 elog<InternalFailure>();
113 }
114
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -0500115 if (dumpType == DumpTypes::USER)
116 {
117 Manager::fUserDumpInProgress = true;
118 }
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500119 return objPath.string();
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500120}
121
122uint32_t Manager::captureDump(Type type,
123 const std::vector<std::string>& fullPaths)
124{
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -0500125 // get dreport type map entry
126 auto tempType = TypeMap.find(type);
127 return captureDump(stringToDumpType(tempType->second).value(),
128 fullPaths.front());
129}
130uint32_t Manager::captureDump(DumpTypes type, const std::string& path)
131{
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500132 // Get Dump size.
133 auto size = getAllowedSize();
134
135 pid_t pid = fork();
136
137 if (pid == 0)
138 {
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500139 std::filesystem::path dumpPath(dumpDir);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500140 auto id = std::to_string(lastEntryId + 1);
141 dumpPath /= id;
142
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -0500143 auto strType = dumpTypeToString(type).value();
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500144 execl("/usr/bin/dreport", "dreport", "-d", dumpPath.c_str(), "-i",
145 id.c_str(), "-s", std::to_string(size).c_str(), "-q", "-v", "-p",
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -0500146 path.empty() ? "" : path.c_str(), "-t", strType.c_str(), nullptr);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500147
148 // dreport script execution is failed.
149 auto error = errno;
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500150 lg2::error("Error occurred during dreport function execution, "
151 "errno: {ERRNO}",
152 "ERRNO", error);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500153 elog<InternalFailure>();
154 }
155 else if (pid > 0)
156 {
Marri Devender Rao3ed02c32022-06-28 23:12:14 -0500157 Child::Callback callback = [this, type, pid](Child&, const siginfo_t*) {
Dhruvaraj Subhashchandran36047102023-06-29 03:46:25 -0500158 if (type == DumpTypes::USER)
Marri Devender Rao3ed02c32022-06-28 23:12:14 -0500159 {
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500160 lg2::info("User initiated dump completed, resetting flag");
Marri Devender Rao3ed02c32022-06-28 23:12:14 -0500161 Manager::fUserDumpInProgress = false;
162 }
163 this->childPtrMap.erase(pid);
164 };
165 try
166 {
167 childPtrMap.emplace(pid,
168 std::make_unique<Child>(eventLoop.get(), pid,
169 WEXITED | WSTOPPED,
170 std::move(callback)));
171 }
172 catch (const sdeventplus::SdEventError& ex)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500173 {
174 // Failed to add to event loop
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500175 lg2::error(
176 "Error occurred during the sdeventplus::source::Child creation "
177 "ex: {ERROR}",
178 "ERROR", ex);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500179 elog<InternalFailure>();
180 }
181 }
182 else
183 {
184 auto error = errno;
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500185 lg2::error("Error occurred during fork, errno: {ERRNO}", "ERRNO",
186 error);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500187 elog<InternalFailure>();
188 }
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500189 return ++lastEntryId;
190}
191
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500192void Manager::createEntry(const std::filesystem::path& file)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500193{
194 // Dump File Name format obmcdump_ID_EPOCHTIME.EXT
195 static constexpr auto ID_POS = 1;
196 static constexpr auto EPOCHTIME_POS = 2;
197 std::regex file_regex("obmcdump_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)");
198
199 std::smatch match;
200 std::string name = file.filename();
201
202 if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
203 {
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500204 lg2::error("Invalid Dump file name, FILENAME: {FILENAME}", "FILENAME",
205 file);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500206 return;
207 }
208
209 auto idString = match[ID_POS];
Xie Ning56bd7972022-02-25 15:20:02 +0800210 uint64_t timestamp = stoull(match[EPOCHTIME_POS]) * 1000 * 1000;
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500211
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500212 auto id = stoul(idString);
213
214 // If there is an existing entry update it and return.
215 auto dumpEntry = entries.find(id);
216 if (dumpEntry != entries.end())
217 {
218 dynamic_cast<phosphor::dump::bmc::Entry*>(dumpEntry->second.get())
Xie Ning56bd7972022-02-25 15:20:02 +0800219 ->update(timestamp, std::filesystem::file_size(file), file);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500220 return;
221 }
222
223 // Entry Object path.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500224 auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500225
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -0500226 // TODO: Get the persisted originator id & type
227 // For now, replacing it with null
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500228 try
229 {
Dhruvaraj Subhashchandrana6ab8062020-10-29 15:29:10 -0500230 entries.insert(std::make_pair(
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500231 id, std::make_unique<bmc::Entry>(
Xie Ning56bd7972022-02-25 15:20:02 +0800232 bus, objPath.c_str(), id, timestamp,
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500233 std::filesystem::file_size(file), file,
Asmitha Karunanithi74a1f392021-10-27 03:23:59 -0500234 phosphor::dump::OperationStatus::Completed, std::string(),
235 originatorTypes::Internal, *this)));
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500236 }
237 catch (const std::invalid_argument& e)
238 {
Dhruvaraj Subhashchandrand1f670f2023-06-05 22:19:25 -0500239 lg2::error(
240 "Error in creating dump entry, errormsg: {ERROR}, "
241 "OBJECTPATH: {OBJECT_PATH}, ID: {ID}, TIMESTAMP: {TIMESTAMP}, "
242 "SIZE: {SIZE}, FILENAME: {FILENAME}",
243 "ERROR", e, "OBJECT_PATH", objPath, "ID", id, "TIMESTAMP",
244 timestamp, "SIZE", std::filesystem::file_size(file), "FILENAME",
245 file);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500246 return;
247 }
248}
249
250void Manager::watchCallback(const UserMap& fileInfo)
251{
252 for (const auto& i : fileInfo)
253 {
254 // For any new dump file create dump entry object
255 // and associated inotify watch.
256 if (IN_CLOSE_WRITE == i.second)
257 {
Chirag Sharma4cb07992022-05-09 04:37:22 -0500258 if (!std::filesystem::is_directory(i.first))
259 {
260 // Don't require filename to be passed, as the path
261 // of dump directory is stored in the childWatchMap
262 removeWatch(i.first.parent_path());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500263
Chirag Sharma4cb07992022-05-09 04:37:22 -0500264 // dump file is written now create D-Bus entry
265 createEntry(i.first);
266 }
267 else
268 {
269 removeWatch(i.first);
270 }
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500271 }
272 // Start inotify watch on newly created directory.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500273 else if ((IN_CREATE == i.second) &&
274 std::filesystem::is_directory(i.first))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500275 {
276 auto watchObj = std::make_unique<Watch>(
277 eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
278 std::bind(
279 std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
280 this, std::placeholders::_1));
281
282 childWatchMap.emplace(i.first, std::move(watchObj));
283 }
284 }
285}
286
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500287void Manager::removeWatch(const std::filesystem::path& path)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500288{
289 // Delete Watch entry from map.
290 childWatchMap.erase(path);
291}
292
293void Manager::restore()
294{
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500295 std::filesystem::path dir(dumpDir);
296 if (!std::filesystem::exists(dir) || std::filesystem::is_empty(dir))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500297 {
298 return;
299 }
300
301 // Dump file path: <DUMP_PATH>/<id>/<filename>
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500302 for (const auto& p : std::filesystem::directory_iterator(dir))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500303 {
304 auto idStr = p.path().filename().string();
305
306 // Consider only directory's with dump id as name.
307 // Note: As per design one file per directory.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500308 if ((std::filesystem::is_directory(p.path())) &&
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500309 std::all_of(idStr.begin(), idStr.end(), ::isdigit))
310 {
Patrick Williams78e88402023-05-10 07:50:48 -0500311 lastEntryId = std::max(lastEntryId,
312 static_cast<uint32_t>(std::stoul(idStr)));
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500313 auto fileIt = std::filesystem::directory_iterator(p.path());
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500314 // Create dump entry d-bus object.
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500315 if (fileIt != std::filesystem::end(fileIt))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500316 {
317 createEntry(fileIt->path());
318 }
319 }
320 }
321}
322
Xie Ningfc69f352022-05-17 16:06:52 +0800323size_t getDirectorySize(const std::string dir)
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500324{
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500325 auto size = 0;
Xie Ningfc69f352022-05-17 16:06:52 +0800326 for (const auto& p : std::filesystem::recursive_directory_iterator(dir))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500327 {
Jayanth Othayoth3fc6df42021-04-08 03:45:24 -0500328 if (!std::filesystem::is_directory(p))
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500329 {
Tim Leebb9366d2021-06-24 14:00:07 +0800330 size += std::ceil(std::filesystem::file_size(p) / 1024.0);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500331 }
332 }
Xie Ningfc69f352022-05-17 16:06:52 +0800333 return size;
334}
335
336size_t Manager::getAllowedSize()
337{
338 // Get current size of the dump directory.
339 auto size = getDirectorySize(dumpDir);
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500340
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500341 // Set the Dump size to Maximum if the free space is greater than
342 // Dump max size otherwise return the available size.
343
344 size = (size > BMC_DUMP_TOTAL_SIZE ? 0 : BMC_DUMP_TOTAL_SIZE - size);
345
Xie Ningfc69f352022-05-17 16:06:52 +0800346#ifdef BMC_DUMP_ROTATE_CONFIG
347 // Delete the first existing file until the space is enough
348 while (size < BMC_DUMP_MIN_SPACE_REQD)
349 {
Patrick Williams78e88402023-05-10 07:50:48 -0500350 auto delEntry = min_element(entries.begin(), entries.end(),
351 [](const auto& l, const auto& r) {
352 return l.first < r.first;
353 });
354 auto delPath = std::filesystem::path(dumpDir) /
355 std::to_string(delEntry->first);
Xie Ningfc69f352022-05-17 16:06:52 +0800356
357 size += getDirectorySize(delPath);
358
359 delEntry->second->delete_();
360 }
361#else
362 using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
363 using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
364
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500365 if (size < BMC_DUMP_MIN_SPACE_REQD)
366 {
367 // Reached to maximum limit
368 elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
369 }
Xie Ningfc69f352022-05-17 16:06:52 +0800370#endif
371
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500372 if (size > BMC_DUMP_MAX_SIZE)
373 {
374 size = BMC_DUMP_MAX_SIZE;
375 }
376
377 return size;
378}
379
380} // namespace bmc
381} // namespace dump
382} // namespace phosphor