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