From a3ece6c01d979931b097763c409ec7330aaadda1 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Wed, 8 Jan 2025 03:17:32 +0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=B7=20gucc:=20add=20block=20devices=20?= =?UTF-8?q?parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gucc/CMakeLists.txt | 1 + gucc/include/gucc/block_devices.hpp | 51 +++++++++++++++ gucc/meson.build | 1 + gucc/src/block_devices.cpp | 99 +++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 gucc/include/gucc/block_devices.hpp create mode 100644 gucc/src/block_devices.cpp diff --git a/gucc/CMakeLists.txt b/gucc/CMakeLists.txt index 7f21ede..a8e23f9 100644 --- a/gucc/CMakeLists.txt +++ b/gucc/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(${PROJECT_NAME} #SHARED src/cpu.cpp include/gucc/cpu.hpp src/pacmanconf_repo.cpp include/gucc/pacmanconf_repo.hpp src/initcpio.cpp include/gucc/initcpio.hpp + src/block_devices.cpp include/gucc/block_devices.hpp src/luks.cpp include/gucc/luks.hpp src/zfs.cpp include/gucc/zfs.hpp src/btrfs.cpp include/gucc/btrfs.hpp diff --git a/gucc/include/gucc/block_devices.hpp b/gucc/include/gucc/block_devices.hpp new file mode 100644 index 0000000..613b972 --- /dev/null +++ b/gucc/include/gucc/block_devices.hpp @@ -0,0 +1,51 @@ +#ifndef BLOCK_DEVICES_HPP +#define BLOCK_DEVICES_HPP + +#include // for uint64_t +#include // for optional +#include // for string +#include // for string_view +#include // for vector + +namespace gucc::disk { + +/// @brief Represents a block device with its properties. +struct BlockDevice { + /// Device name (e.g., /dev/nvme0n1). + std::string name; + /// Device type (e.g., part, crypt, disk). + std::string type; + /// Filesystem type (e.g., ext4, btrfs). + std::string fstype; + /// Device UUID. + std::string uuid; + /// Device model. + std::optional model; + /// Mount point. + std::optional mountpoint; + /// Parent device name. + std::optional pkname; + /// Filesystem label. + std::optional label; + /// Partition UUID. + std::optional partuuid; + /// Size of the device in bytes. + std::optional size; + + /// @brief Default constructor for BlockDevice. + BlockDevice() = default; +}; + +/// @brief Executes lsblk command and returns a list of block devices. +/// @return An optional vector of BlockDevice objects, std::nullopt otherwise. +auto list_block_devices() -> std::optional>; + +/// @brief Finds a block device by its name. +/// @param devices A vector of BlockDevice objects. +/// @param device_name A string view containing the name of the device to find. +/// @return An optional BlockDevice object if found, std::nullopt otherwise. +auto find_device_by_name(const std::vector& devices, std::string_view device_name) -> std::optional; + +} // namespace gucc::disk + +#endif // BLOCK_DEVICES_HPP diff --git a/gucc/meson.build b/gucc/meson.build index 307e437..f209116 100644 --- a/gucc/meson.build +++ b/gucc/meson.build @@ -7,6 +7,7 @@ gucc_lib = library('gucc', 'src/cpu.cpp', 'src/pacmanconf_repo.cpp', 'src/initcpio.cpp', + 'src/block_devices.cpp', 'src/luks.cpp', 'src/zfs.cpp', 'src/btrfs.cpp', diff --git a/gucc/src/block_devices.cpp b/gucc/src/block_devices.cpp new file mode 100644 index 0000000..42ab25f --- /dev/null +++ b/gucc/src/block_devices.cpp @@ -0,0 +1,99 @@ +#include "gucc/block_devices.hpp" +#include "gucc/io_utils.hpp" + +#include // for find_if +#include // for ranges::* +#include // for make_optional, move + +#include +#include + +#include + +using namespace std::string_view_literals; + +namespace { + +using BlockDevice = gucc::disk::BlockDevice; + +/// Constructs a BlockDevice from a RapidJSON object. +auto get_blockdevice_from_json(const rapidjson::Value& doc) -> BlockDevice { + auto device = BlockDevice{}; + if (doc.HasMember("name") && doc["name"].IsString()) { + device.name = doc["name"].GetString(); + } + if (doc.HasMember("type") && doc["type"].IsString()) { + device.type = doc["type"].GetString(); + } + if (doc.HasMember("fstype") && doc["fstype"].IsString()) { + device.fstype = doc["fstype"].GetString(); + } + if (doc.HasMember("uuid") && doc["uuid"].IsString()) { + device.uuid = doc["uuid"].GetString(); + } + + if (doc.HasMember("model") && doc["model"].IsString()) { + device.model = doc["model"].GetString(); + } + if (doc.HasMember("mountpoint") && doc["mountpoint"].IsString()) { + device.mountpoint = doc["mountpoint"].GetString(); + } + if (doc.HasMember("pkname") && doc["pkname"].IsString()) { + device.pkname = doc["pkname"].GetString(); + } + if (doc.HasMember("label") && doc["label"].IsString()) { + device.label = doc["label"].GetString(); + } + if (doc.HasMember("partuuid") && doc["partuuid"].IsString()) { + device.partuuid = doc["partuuid"].GetString(); + } + if (doc.HasMember("size") && doc["size"].IsInt64()) { + device.size = doc["size"].GetInt64(); + } + + return device; +} + +auto parse_lsblk_json(std::string_view json_output) -> std::vector { + rapidjson::Document document; + + document.Parse(json_output.data(), json_output.size()); + if (document.HasParseError()) { + spdlog::error("Failed to parse lsblk output: {}", rapidjson::GetParseError_En(document.GetParseError())); + return {}; + } + + // Extract data from JSON + std::vector block_devices{}; + if (document.HasMember("blockdevices") && document["blockdevices"].IsArray()) { + for (const auto& device_json : document["blockdevices"].GetArray()) { + auto block_device = get_blockdevice_from_json(device_json); + block_devices.emplace_back(std::move(block_device)); + } + } + return block_devices; +} + +} // namespace + +namespace gucc::disk { + +auto find_device_by_name(const std::vector& devices, std::string_view device_name) -> std::optional { + auto it = std::ranges::find_if(devices, [device_name](auto&& dev) { + return dev.name == device_name; + }); + if (it != std::ranges::end(devices)) { + return std::make_optional(std::move(*it)); + } + return std::nullopt; +} + +auto list_block_devices() -> std::optional> { + const auto& lsblk_output = utils::exec(R"(lsblk -f -o NAME,TYPE,FSTYPE,UUID,PARTUUID,PKNAME,LABEL,SIZE,MOUNTPOINT,MODEL -b -p -a -J -Q "type=='part' || type=='crypt' && fstype")"); + if (lsblk_output.empty()) { + return std::nullopt; + } + return std::make_optional>(parse_lsblk_json(lsblk_output)); +} + +} // namespace gucc::disk