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