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