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