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