blob: acd14a212af5e714a004f6aa575ec58cb566f46e [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
10#include <sys/inotify.h>
11#include <unistd.h>
12
13#include <phosphor-logging/elog-errors.hpp>
14#include <phosphor-logging/elog.hpp>
15#include <regex>
16
17namespace phosphor
18{
19namespace dump
20{
21namespace bmc
22{
23
24using namespace sdbusplus::xyz::openbmc_project::Common::Error;
25using namespace phosphor::logging;
26
27namespace internal
28{
29
30void Manager::create(Type type, std::vector<std::string> fullPaths)
31{
32 dumpMgr.phosphor::dump::bmc::Manager::captureDump(type, fullPaths);
33}
34
35} // namespace internal
36
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050037sdbusplus::message::object_path Manager::createDump()
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050038{
39 std::vector<std::string> paths;
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -050040 auto id = captureDump(Type::UserRequested, paths);
41
42 // Entry Object path.
43 auto objPath = fs::path(baseEntryPath) / std::to_string(id);
44
45 try
46 {
47 entries.insert(std::make_pair(
48 id, std::make_unique<bmc::Entry>(bus, objPath.c_str(), id, 0, 0,
49 std::string(), *this)));
50 }
51 catch (const std::invalid_argument& e)
52 {
53 log<level::ERR>(e.what());
54 log<level::ERR>("Error in creating dump entry",
55 entry("OBJECTPATH=%s", objPath.c_str()),
56 entry("ID=%d", id));
57 elog<InternalFailure>();
58 }
59
60 return objPath.string();
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -050061}
62
63uint32_t Manager::captureDump(Type type,
64 const std::vector<std::string>& fullPaths)
65{
66 // Get Dump size.
67 auto size = getAllowedSize();
68
69 pid_t pid = fork();
70
71 if (pid == 0)
72 {
73 fs::path dumpPath(dumpDir);
74 auto id = std::to_string(lastEntryId + 1);
75 dumpPath /= id;
76
77 // get dreport type map entry
78 auto tempType = TypeMap.find(type);
79
80 execl("/usr/bin/dreport", "dreport", "-d", dumpPath.c_str(), "-i",
81 id.c_str(), "-s", std::to_string(size).c_str(), "-q", "-v", "-p",
82 fullPaths.empty() ? "" : fullPaths.front().c_str(), "-t",
83 tempType->second.c_str(), nullptr);
84
85 // dreport script execution is failed.
86 auto error = errno;
87 log<level::ERR>("Error occurred during dreport function execution",
88 entry("ERRNO=%d", error));
89 elog<InternalFailure>();
90 }
91 else if (pid > 0)
92 {
93 auto rc = sd_event_add_child(eventLoop.get(), nullptr, pid,
94 WEXITED | WSTOPPED, callback, nullptr);
95 if (0 > rc)
96 {
97 // Failed to add to event loop
98 log<level::ERR>("Error occurred during the sd_event_add_child call",
99 entry("RC=%d", rc));
100 elog<InternalFailure>();
101 }
102 }
103 else
104 {
105 auto error = errno;
106 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
107 elog<InternalFailure>();
108 }
109
110 return ++lastEntryId;
111}
112
113void Manager::createEntry(const fs::path& file)
114{
115 // Dump File Name format obmcdump_ID_EPOCHTIME.EXT
116 static constexpr auto ID_POS = 1;
117 static constexpr auto EPOCHTIME_POS = 2;
118 std::regex file_regex("obmcdump_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)");
119
120 std::smatch match;
121 std::string name = file.filename();
122
123 if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
124 {
125 log<level::ERR>("Invalid Dump file name",
126 entry("FILENAME=%s", file.filename().c_str()));
127 return;
128 }
129
130 auto idString = match[ID_POS];
131 auto msString = match[EPOCHTIME_POS];
132
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500133 auto id = stoul(idString);
134
135 // If there is an existing entry update it and return.
136 auto dumpEntry = entries.find(id);
137 if (dumpEntry != entries.end())
138 {
139 dynamic_cast<phosphor::dump::bmc::Entry*>(dumpEntry->second.get())
140 ->update(stoull(msString), fs::file_size(file), file);
141 return;
142 }
143
144 // Entry Object path.
145 auto objPath = fs::path(baseEntryPath) / std::to_string(id);
146
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500147 try
148 {
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500149 entries.insert(
150 std::make_pair(id, std::make_unique<bmc::Entry>(
151 bus, objPath.c_str(), id, stoull(msString),
152 fs::file_size(file), file, *this)));
153 }
154 catch (const std::invalid_argument& e)
155 {
156 log<level::ERR>(e.what());
Dhruvaraj Subhashchandran6ccb50e2020-10-29 09:33:18 -0500157 log<level::ERR>("Error in creating dump entry",
158 entry("OBJECTPATH=%s", objPath.c_str()),
159 entry("ID=%d", id),
160 entry("TIMESTAMP=%ull", stoull(msString)),
161 entry("SIZE=%d", fs::file_size(file)),
162 entry("FILENAME=%s", file.c_str()));
Dhruvaraj Subhashchandranfef66a92020-09-06 13:10:59 -0500163 return;
164 }
165}
166
167void Manager::watchCallback(const UserMap& fileInfo)
168{
169 for (const auto& i : fileInfo)
170 {
171 // For any new dump file create dump entry object
172 // and associated inotify watch.
173 if (IN_CLOSE_WRITE == i.second)
174 {
175 removeWatch(i.first);
176
177 createEntry(i.first);
178 }
179 // Start inotify watch on newly created directory.
180 else if ((IN_CREATE == i.second) && fs::is_directory(i.first))
181 {
182 auto watchObj = std::make_unique<Watch>(
183 eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
184 std::bind(
185 std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
186 this, std::placeholders::_1));
187
188 childWatchMap.emplace(i.first, std::move(watchObj));
189 }
190 }
191}
192
193void Manager::removeWatch(const fs::path& path)
194{
195 // Delete Watch entry from map.
196 childWatchMap.erase(path);
197}
198
199void Manager::restore()
200{
201 fs::path dir(dumpDir);
202 if (!fs::exists(dir) || fs::is_empty(dir))
203 {
204 return;
205 }
206
207 // Dump file path: <DUMP_PATH>/<id>/<filename>
208 for (const auto& p : fs::directory_iterator(dir))
209 {
210 auto idStr = p.path().filename().string();
211
212 // Consider only directory's with dump id as name.
213 // Note: As per design one file per directory.
214 if ((fs::is_directory(p.path())) &&
215 std::all_of(idStr.begin(), idStr.end(), ::isdigit))
216 {
217 lastEntryId =
218 std::max(lastEntryId, static_cast<uint32_t>(std::stoul(idStr)));
219 auto fileIt = fs::directory_iterator(p.path());
220 // Create dump entry d-bus object.
221 if (fileIt != fs::end(fileIt))
222 {
223 createEntry(fileIt->path());
224 }
225 }
226 }
227}
228
229size_t Manager::getAllowedSize()
230{
231 using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
232 using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
233
234 auto size = 0;
235
236 // Get current size of the dump directory.
237 for (const auto& p : fs::recursive_directory_iterator(dumpDir))
238 {
239 if (!fs::is_directory(p))
240 {
241 size += fs::file_size(p);
242 }
243 }
244
245 // Convert size into KB
246 size = size / 1024;
247
248 // Set the Dump size to Maximum if the free space is greater than
249 // Dump max size otherwise return the available size.
250
251 size = (size > BMC_DUMP_TOTAL_SIZE ? 0 : BMC_DUMP_TOTAL_SIZE - size);
252
253 if (size < BMC_DUMP_MIN_SPACE_REQD)
254 {
255 // Reached to maximum limit
256 elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
257 }
258 if (size > BMC_DUMP_MAX_SIZE)
259 {
260 size = BMC_DUMP_MAX_SIZE;
261 }
262
263 return size;
264}
265
266} // namespace bmc
267} // namespace dump
268} // namespace phosphor