blob: f6556f17d0703528999ee2c592340e2a7c0ddf0a [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
37uint32_t Manager::createDump()
38{
39 std::vector<std::string> paths;
40 return captureDump(Type::UserRequested, paths);
41}
42
43uint32_t Manager::captureDump(Type type,
44 const std::vector<std::string>& fullPaths)
45{
46 // Get Dump size.
47 auto size = getAllowedSize();
48
49 pid_t pid = fork();
50
51 if (pid == 0)
52 {
53 fs::path dumpPath(dumpDir);
54 auto id = std::to_string(lastEntryId + 1);
55 dumpPath /= id;
56
57 // get dreport type map entry
58 auto tempType = TypeMap.find(type);
59
60 execl("/usr/bin/dreport", "dreport", "-d", dumpPath.c_str(), "-i",
61 id.c_str(), "-s", std::to_string(size).c_str(), "-q", "-v", "-p",
62 fullPaths.empty() ? "" : fullPaths.front().c_str(), "-t",
63 tempType->second.c_str(), nullptr);
64
65 // dreport script execution is failed.
66 auto error = errno;
67 log<level::ERR>("Error occurred during dreport function execution",
68 entry("ERRNO=%d", error));
69 elog<InternalFailure>();
70 }
71 else if (pid > 0)
72 {
73 auto rc = sd_event_add_child(eventLoop.get(), nullptr, pid,
74 WEXITED | WSTOPPED, callback, nullptr);
75 if (0 > rc)
76 {
77 // Failed to add to event loop
78 log<level::ERR>("Error occurred during the sd_event_add_child call",
79 entry("RC=%d", rc));
80 elog<InternalFailure>();
81 }
82 }
83 else
84 {
85 auto error = errno;
86 log<level::ERR>("Error occurred during fork", entry("ERRNO=%d", error));
87 elog<InternalFailure>();
88 }
89
90 return ++lastEntryId;
91}
92
93void Manager::createEntry(const fs::path& file)
94{
95 // Dump File Name format obmcdump_ID_EPOCHTIME.EXT
96 static constexpr auto ID_POS = 1;
97 static constexpr auto EPOCHTIME_POS = 2;
98 std::regex file_regex("obmcdump_([0-9]+)_([0-9]+).([a-zA-Z0-9]+)");
99
100 std::smatch match;
101 std::string name = file.filename();
102
103 if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
104 {
105 log<level::ERR>("Invalid Dump file name",
106 entry("FILENAME=%s", file.filename().c_str()));
107 return;
108 }
109
110 auto idString = match[ID_POS];
111 auto msString = match[EPOCHTIME_POS];
112
113 try
114 {
115 auto id = stoul(idString);
116 // Entry Object path.
117 auto objPath = fs::path(baseEntryPath) / std::to_string(id);
118
119 entries.insert(
120 std::make_pair(id, std::make_unique<bmc::Entry>(
121 bus, objPath.c_str(), id, stoull(msString),
122 fs::file_size(file), file, *this)));
123 }
124 catch (const std::invalid_argument& e)
125 {
126 log<level::ERR>(e.what());
127 return;
128 }
129}
130
131void Manager::watchCallback(const UserMap& fileInfo)
132{
133 for (const auto& i : fileInfo)
134 {
135 // For any new dump file create dump entry object
136 // and associated inotify watch.
137 if (IN_CLOSE_WRITE == i.second)
138 {
139 removeWatch(i.first);
140
141 createEntry(i.first);
142 }
143 // Start inotify watch on newly created directory.
144 else if ((IN_CREATE == i.second) && fs::is_directory(i.first))
145 {
146 auto watchObj = std::make_unique<Watch>(
147 eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
148 std::bind(
149 std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
150 this, std::placeholders::_1));
151
152 childWatchMap.emplace(i.first, std::move(watchObj));
153 }
154 }
155}
156
157void Manager::removeWatch(const fs::path& path)
158{
159 // Delete Watch entry from map.
160 childWatchMap.erase(path);
161}
162
163void Manager::restore()
164{
165 fs::path dir(dumpDir);
166 if (!fs::exists(dir) || fs::is_empty(dir))
167 {
168 return;
169 }
170
171 // Dump file path: <DUMP_PATH>/<id>/<filename>
172 for (const auto& p : fs::directory_iterator(dir))
173 {
174 auto idStr = p.path().filename().string();
175
176 // Consider only directory's with dump id as name.
177 // Note: As per design one file per directory.
178 if ((fs::is_directory(p.path())) &&
179 std::all_of(idStr.begin(), idStr.end(), ::isdigit))
180 {
181 lastEntryId =
182 std::max(lastEntryId, static_cast<uint32_t>(std::stoul(idStr)));
183 auto fileIt = fs::directory_iterator(p.path());
184 // Create dump entry d-bus object.
185 if (fileIt != fs::end(fileIt))
186 {
187 createEntry(fileIt->path());
188 }
189 }
190 }
191}
192
193size_t Manager::getAllowedSize()
194{
195 using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
196 using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
197
198 auto size = 0;
199
200 // Get current size of the dump directory.
201 for (const auto& p : fs::recursive_directory_iterator(dumpDir))
202 {
203 if (!fs::is_directory(p))
204 {
205 size += fs::file_size(p);
206 }
207 }
208
209 // Convert size into KB
210 size = size / 1024;
211
212 // Set the Dump size to Maximum if the free space is greater than
213 // Dump max size otherwise return the available size.
214
215 size = (size > BMC_DUMP_TOTAL_SIZE ? 0 : BMC_DUMP_TOTAL_SIZE - size);
216
217 if (size < BMC_DUMP_MIN_SPACE_REQD)
218 {
219 // Reached to maximum limit
220 elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
221 }
222 if (size > BMC_DUMP_MAX_SIZE)
223 {
224 size = BMC_DUMP_MAX_SIZE;
225 }
226
227 return size;
228}
229
230} // namespace bmc
231} // namespace dump
232} // namespace phosphor