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