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