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