| #include "binarystore.hpp" |
| #include "parse_config.hpp" |
| #include "sys_file_impl.hpp" |
| |
| #include <getopt.h> |
| |
| #include <algorithm> |
| #include <fstream> |
| #include <nlohmann/json.hpp> |
| #include <stdplus/print.hpp> |
| |
| constexpr auto defaultBlobConfigPath = "/usr/share/binaryblob/config.json"; |
| |
| struct BlobToolConfig |
| { |
| std::string configPath = defaultBlobConfigPath; |
| std::string programName; |
| std::string binStore; |
| std::string blobName; |
| size_t offsetBytes = 0; |
| enum class Action |
| { |
| HELP, |
| LIST, |
| READ, |
| MIGRATE, |
| } action = Action::LIST; |
| } toolConfig; |
| |
| void printUsage(const BlobToolConfig& cfg) |
| { |
| stdplus::print(stderr, |
| "Usage: \n" |
| "{} [OPTIONS]\n" |
| "\t--list\t\tList all supported blobs. This is a default.\n" |
| "\t--read\t\tRead blob specified in --blob argument (which " |
| "becomes mandatory).\n" |
| "\t--migrate\tUpdate all binary stores to use the alias " |
| "blob id if enabled.\n" |
| "\t--config\tFILENAME\tPath to the configuration file. The " |
| "default is /usr/share/binaryblob/config.json.\n" |
| "\t--binary-store\tFILENAME\tPath to the binary storage. If " |
| "specified, configuration file is not used.\n" |
| "\t--blob\tSTRING\tThe name of the blob to read.\n" |
| "\t--offset\tNUMBER\tThe offset in the binary store file, " |
| "where the binary store actually starts.\n" |
| "\t--help\t\tPrint this help and exit\n", |
| cfg.programName); |
| } |
| |
| bool parseOptions(int argc, char* argv[], BlobToolConfig& cfg) |
| { |
| cfg.programName = argv[0]; |
| |
| struct option longOptions[] = { |
| {"help", no_argument, 0, 'h'}, |
| {"list", no_argument, 0, 'l'}, |
| {"read", no_argument, 0, 'r'}, |
| {"migrate", no_argument, 0, 'm'}, |
| {"config", required_argument, 0, 'c'}, |
| {"binary-store", required_argument, 0, 's'}, |
| {"blob", required_argument, 0, 'b'}, |
| {"offset", required_argument, 0, 'g'}, |
| {0, 0, 0, 0}, |
| }; |
| |
| int optionIndex = 0; |
| bool res = true; |
| while (1) |
| { |
| int ret = getopt_long_only(argc, argv, "", longOptions, &optionIndex); |
| |
| if (ret == -1) |
| break; |
| |
| switch (ret) |
| { |
| case 'h': |
| cfg.action = BlobToolConfig::Action::HELP; |
| break; |
| case 'l': |
| cfg.action = BlobToolConfig::Action::LIST; |
| break; |
| case 'r': |
| cfg.action = BlobToolConfig::Action::READ; |
| break; |
| case 'm': |
| cfg.action = BlobToolConfig::Action::MIGRATE; |
| break; |
| case 'c': |
| cfg.configPath = optarg; |
| break; |
| case 's': |
| cfg.binStore = optarg; |
| break; |
| case 'b': |
| cfg.blobName = optarg; |
| break; |
| case 'g': |
| cfg.offsetBytes = std::stoi(optarg); |
| break; |
| default: |
| res = false; |
| break; |
| } |
| } |
| |
| return res; |
| } |
| |
| int main(int argc, char* argv[]) |
| { |
| parseOptions(argc, argv, toolConfig); |
| if (toolConfig.action == BlobToolConfig::Action::HELP) |
| { |
| printUsage(toolConfig); |
| return 0; |
| } |
| |
| std::vector<std::unique_ptr<binstore::BinaryStoreInterface>> stores; |
| if (!toolConfig.binStore.empty()) |
| { |
| auto file = std::make_unique<binstore::SysFileImpl>( |
| toolConfig.binStore, toolConfig.offsetBytes); |
| if (!file) |
| { |
| stdplus::print(stderr, "Can't open binary store {}\n", |
| toolConfig.binStore); |
| printUsage(toolConfig); |
| return 1; |
| } |
| |
| auto store = |
| binstore::BinaryStore::createFromFile(std::move(file), true); |
| stores.push_back(std::move(store)); |
| } |
| else |
| { |
| std::ifstream input(toolConfig.configPath); |
| json j; |
| |
| if (!input.good()) |
| { |
| stdplus::print(stderr, "Config file not found:{}\n", |
| toolConfig.configPath); |
| return 1; |
| } |
| |
| try |
| { |
| input >> j; |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print(stderr, "Failed to parse config into json:\n{}\n", |
| e.what()); |
| return 1; |
| } |
| |
| for (const auto& element : j) |
| { |
| conf::BinaryBlobConfig config; |
| try |
| { |
| conf::parseFromConfigFile(element, config); |
| } |
| catch (const std::exception& e) |
| { |
| stdplus::print( |
| stderr, "Encountered error when parsing config file:\n{}\n", |
| e.what()); |
| return 1; |
| } |
| |
| auto file = std::make_unique<binstore::SysFileImpl>( |
| config.sysFilePath, config.offsetBytes); |
| |
| auto store = binstore::BinaryStore::createFromConfig( |
| config.blobBaseId, std::move(file), config.maxSizeBytes, |
| config.aliasBlobBaseId); |
| |
| if (toolConfig.action == BlobToolConfig::Action::MIGRATE) |
| { |
| if (config.migrateToAlias && config.aliasBlobBaseId.has_value()) |
| { |
| store->setBaseBlobId(config.aliasBlobBaseId.value()); |
| } |
| } |
| else |
| { |
| stores.push_back(std::move(store)); |
| } |
| } |
| } |
| |
| if (toolConfig.action == BlobToolConfig::Action::MIGRATE) |
| { |
| stdplus::print(stderr, |
| "Migrated all BinaryStore back to configured Alias\n"); |
| return 0; |
| } |
| |
| if (toolConfig.action == BlobToolConfig::Action::LIST) |
| { |
| stdplus::print(stderr, "Supported Blobs: \n"); |
| for (const auto& store : stores) |
| { |
| const auto blobIds = store->getBlobIds(); |
| std::copy( |
| blobIds.begin(), blobIds.end(), |
| std::ostream_iterator<decltype(blobIds[0])>(std::cout, "\n")); |
| } |
| return 0; |
| } |
| if (toolConfig.action == BlobToolConfig::Action::READ) |
| { |
| if (toolConfig.blobName.empty()) |
| { |
| stdplus::print(stderr, |
| "Must specify the name of the blob to read.\n"); |
| printUsage(toolConfig); |
| return 1; |
| } |
| |
| bool blobFound = false; |
| |
| for (const auto& store : stores) |
| { |
| const auto blobIds = store->getBlobIds(); |
| if (std::any_of(blobIds.begin(), blobIds.end(), |
| [](const std::string& bn) { |
| return bn == toolConfig.blobName; |
| })) |
| { |
| const auto blobData = store->readBlob(toolConfig.blobName); |
| if (blobData.empty()) |
| { |
| stdplus::print(stderr, "No data read from {}\n", |
| store->getBaseBlobId()); |
| continue; |
| } |
| |
| blobFound = true; |
| |
| std::copy( |
| blobData.begin(), blobData.end(), |
| std::ostream_iterator<decltype(blobData[0])>(std::cout)); |
| |
| // It's assumed that the names of the blobs are unique within |
| // the system. |
| break; |
| } |
| } |
| |
| if (!blobFound) |
| { |
| stdplus::print(stderr, "Blob {} not found.\n", toolConfig.blobName); |
| return 1; |
| } |
| return 0; |
| } |
| |
| return 0; |
| } |