blob: a47096087cb4dc0cca8d3c27a9d7e5074b26ec2a [file] [log] [blame]
Dhruvaraj Subhashchandran830a6d42021-05-05 15:47:57 -05001#include "config.h"
2
3#include "bmc_dump_entry.hpp"
4#include "dump_internal.hpp"
5#include "dump_manager_bmc.hpp"
6#include "xyz/openbmc_project/Common/error.hpp"
7#include "xyz/openbmc_project/Dump/Create/error.hpp"
8
9#include <fmt/core.h>
10#include <sys/inotify.h>
11#include <unistd.h>
12
13#include <phosphor-logging/elog-errors.hpp>
14#include <phosphor-logging/elog.hpp>
15
16#include <cmath>
17#include <ctime>
18#include <regex>
19
20namespace phosphor
21{
22namespace dump
23{
24namespace bmc_stored
25{
26
27using namespace sdbusplus::xyz::openbmc_project::Common::Error;
28using namespace phosphor::logging;
29
30void Manager::createEntry(const std::filesystem::path& file)
31{
32 static constexpr auto ID_POS = 1;
33 static constexpr auto EPOCHTIME_POS = 2;
34 std::regex file_regex(dumpFilenameFormat.c_str());
35
36 std::smatch match;
37 std::string name = file.filename();
38
39 if (!((std::regex_search(name, match, file_regex)) && (match.size() > 0)))
40 {
41 log<level::ERR>(fmt::format("Invalid Dump file name, FILENAME({})",
42 file.filename().c_str())
43 .c_str());
44 return;
45 }
46
47 auto idString = match[ID_POS];
48 uint64_t timestamp = stoull(match[EPOCHTIME_POS]) * 1000 * 1000;
49
50 auto id = stoul(idString);
51
52 // If there is an existing entry update it and return.
53 auto dumpEntry = entries.find(id);
54 if (dumpEntry != entries.end())
55 {
56 dynamic_cast<phosphor::dump::bmc_stored::Entry*>(
57 dumpEntry->second.get())
58 ->update(timestamp, std::filesystem::file_size(file), file);
59 return;
60 }
61
62 // Entry Object path.
63 auto objPath = std::filesystem::path(baseEntryPath) / std::to_string(id);
64
65 try
66 {
67 createEntry(id, objPath, timestamp, std::filesystem::file_size(file),
68 file, phosphor::dump::OperationStatus::Completed,
69 std::string(), originatorTypes::Internal);
70 }
71 catch (const InternalFailure& e)
72 {
73 log<level::ERR>(
74 fmt::format(
75 "Error in creating dump entry, errormsg({}), OBJECTPATH({}), "
76 "ID({}), TIMESTAMP({}), SIZE({}), FILENAME({})",
77 e.what(), objPath.c_str(), id, timestamp,
78 std::filesystem::file_size(file), file.filename().c_str())
79 .c_str());
80 return;
81 }
82}
83
84void Manager::watchCallback(const UserMap& fileInfo)
85{
86 for (const auto& i : fileInfo)
87 {
88 // For any new dump file create dump entry object
89 // and associated inotify watch.
90 if (IN_CLOSE_WRITE == i.second)
91 {
92 if (!std::filesystem::is_directory(i.first))
93 {
94 // Don't require filename to be passed, as the path
95 // of dump directory is stored in the childWatchMap
96 removeWatch(i.first.parent_path());
97
98 // dump file is written now create D-Bus entry
99 createEntry(i.first);
100 }
101 else
102 {
103 removeWatch(i.first);
104 }
105 }
106 // Start inotify watch on newly created directory.
107 else if ((IN_CREATE == i.second) &&
108 std::filesystem::is_directory(i.first))
109 {
110 auto watchObj = std::make_unique<Watch>(
111 eventLoop, IN_NONBLOCK, IN_CLOSE_WRITE, EPOLLIN, i.first,
112 std::bind(
113 std::mem_fn(&phosphor::dump::bmc::Manager::watchCallback),
114 this, std::placeholders::_1));
115
116 childWatchMap.emplace(i.first, std::move(watchObj));
117 }
118 }
119}
120
121void Manager::removeWatch(const std::filesystem::path& path)
122{
123 // Delete Watch entry from map.
124 childWatchMap.erase(path);
125}
126
127void Manager::restore()
128{
129 std::filesystem::path dir(dumpDir);
130 if (!std::filesystem::exists(dir) || std::filesystem::is_empty(dir))
131 {
132 return;
133 }
134
135 // Dump file path: <DUMP_PATH>/<id>/<filename>
136 for (const auto& p : std::filesystem::directory_iterator(dir))
137 {
138 auto idStr = p.path().filename().string();
139
140 // Consider only directory's with dump id as name.
141 // Note: As per design one file per directory.
142 if ((std::filesystem::is_directory(p.path())) &&
143 std::all_of(idStr.begin(), idStr.end(), ::isdigit))
144 {
145 lastEntryId =
146 std::max(lastEntryId, static_cast<uint32_t>(std::stoul(idStr)));
147 auto fileIt = std::filesystem::directory_iterator(p.path());
148 // Create dump entry d-bus object.
149 if (fileIt != std::filesystem::end(fileIt))
150 {
151 createEntry(fileIt->path());
152 }
153 }
154 }
155}
156
157size_t getDirectorySize(const std::string dir)
158{
159 auto size = 0;
160 for (const auto& p : std::filesystem::recursive_directory_iterator(dir))
161 {
162 if (!std::filesystem::is_directory(p))
163 {
164 size += std::ceil(std::filesystem::file_size(p) / 1024.0);
165 }
166 }
167 return size;
168}
169
170size_t Manager::getAllowedSize()
171{
172 // Get current size of the dump directory.
173 auto size = getDirectorySize(dumpDir);
174
175 // Set the Dump size to Maximum if the free space is greater than
176 // Dump max size otherwise return the available size.
177
178 size = (size > allocatedSize ? 0 : allocatedSize - size);
179
180#ifdef BMC_DUMP_ROTATE_CONFIG
181 // Delete the first existing file until the space is enough
182 while (size < minDumpSize)
183 {
184 auto delEntry = min_element(
185 entries.begin(), entries.end(),
186 [](const auto& l, const auto& r) { return l.first < r.first; });
187 auto delPath =
188 std::filesystem::path(dumpDir) / std::to_string(delEntry->first);
189
190 size += getDirectorySize(delPath);
191
192 delEntry->second->delete_();
193 }
194#else
195 using namespace sdbusplus::xyz::openbmc_project::Dump::Create::Error;
196 using Reason = xyz::openbmc_project::Dump::Create::QuotaExceeded::REASON;
197
198 if (size < minDumpSize)
199 {
200 log<level::ERR>(fmt::format("Not enough space available({}) miniumum "
201 "needed({}) filled({}) allocated({})",
202 size, minDumpSize,
203 getDirectorySize(dumpDir), allocatedSize)
204 .c_str());
205 // Reached to maximum limit
206 elog<QuotaExceeded>(Reason("Not enough space: Delete old dumps"));
207 }
208#endif
209
210 if (size > maxDumpSize)
211 {
212 size = maxDumpSize;
213 }
214
215 return size;
216}
217
218} // namespace bmc_stored
219} // namespace dump
220} // namespace phosphor