// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include #include #include #include #include #include #include #include #include #include #include #include namespace sdesktop::endpoints { auto DeviceInfoEndpointCommon::handle(Context &context) -> void { http::Code status; switch (context.getMethod()) { case http::Method::get: status = handleGet(context); break; default: status = http::Code::BadRequest; break; } context.setResponseStatus(status); sender::putToSendQueue(context.createSimpleResponse()); } auto DeviceInfoEndpointCommon::handleGet(Context &context) -> http::Code { const auto &requestBody = context.getBody(); if (requestBody[json::fileList].is_number()) { const auto diagFileType = parseDiagnosticFileType(requestBody[json::fileList]); if (!magic_enum::enum_contains(diagFileType)) { LOG_ERROR("Bad diagnostic type %d requested", static_cast(diagFileType)); return http::Code::BadRequest; } return gatherListOfDiagnostics(context, diagFileType); } else { return getDeviceInfo(context); } } auto DeviceInfoEndpointCommon::parseDiagnosticFileType(const json11::Json &fileList) -> DiagnosticFileType { return magic_enum::enum_cast(fileList.int_value()).value(); } auto DeviceInfoEndpointCommon::gatherListOfDiagnostics(Context &context, DiagnosticFileType diagDataType) -> http::Code { std::vector fileList; auto status = http::Code::NoContent; try { requestLogsFlush(); } catch (const std::runtime_error &e) { LOG_ERROR("Logs flush exception: %s", e.what()); } switch (diagDataType) { case DiagnosticFileType::Logs: { fileList = listDirectory(purefs::dir::getLogsPath()); break; } case DiagnosticFileType::CrashDumps: { fileList = listDirectory(purefs::dir::getCrashDumpsPath()); break; } } if (!fileList.empty()) { status = http::Code::OK; context.setResponseBody(fileListToJsonObject(fileList)); } return status; } auto DeviceInfoEndpointCommon::requestLogsFlush() const -> void { auto owner = dynamic_cast(ownerServicePtr); if (owner) { owner->requestLogsFlush(); } } auto DeviceInfoEndpointCommon::fileListToJsonObject(const std::vector &fileList) const -> json11::Json::object const { json11::Json::array fileArray; for (const auto &file : fileList) { fileArray.push_back(file); } return json11::Json::object{{json::files, fileArray}}; } auto DeviceInfoEndpointCommon::listDirectory(const std::string &path) -> std::vector { std::vector entries; for (const auto &entry : std::filesystem::directory_iterator(path)) { entries.push_back(entry.path()); } return entries; } auto DeviceInfoEndpointCommon::getStorageStats(const std::string &path) -> std::tuple { std::unique_ptr vfstat = std::make_unique(); if (statvfs(path.c_str(), vfstat.get()) < 0) { return {-1, -1}; } unsigned long totalMbytes = (uint64_t(vfstat->f_blocks) * uint64_t(vfstat->f_bsize)) / (1024LLU * 1024LLU); unsigned long freeMbytes = (uint64_t(vfstat->f_bfree) * uint64_t(vfstat->f_bsize)) / (1024LLU * 1024LLU); return {totalMbytes, freeMbytes}; } auto DeviceInfoEndpointCommon::getStorageInfo() -> std::tuple { unsigned long totalDeviceSpaceMiB = 0; unsigned long reservedSystemSpaceMiB = 0; unsigned long usedUserSpaceMiB = 0; const std::array systemStoragePaths{purefs::dir::getRootDiskPath(), purefs::dir::getPreviousOSPath()}; for (const auto &p : systemStoragePaths) { auto [totalSpace, freeSpace] = getStorageStats(p); if (totalSpace < 0 || freeSpace < 0) { LOG_ERROR("Failed to get stats for %s", p.c_str()); continue; } totalDeviceSpaceMiB += totalSpace; reservedSystemSpaceMiB = totalDeviceSpaceMiB; } // User partition stats const auto userStoragePath = purefs::dir::getUserDiskPath(); auto [totalSpace, freeSpace] = getStorageStats(userStoragePath); if (totalSpace < 0 || freeSpace < 0) { LOG_ERROR("Failed to get stats for %s", userStoragePath.c_str()); } else { usedUserSpaceMiB = totalSpace - freeSpace; totalDeviceSpaceMiB += totalSpace; } return {totalDeviceSpaceMiB, reservedSystemSpaceMiB, usedUserSpaceMiB}; } } // namespace sdesktop::endpoints