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