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