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