blob: 463849df077792a2db00281db07ed93a22517b27 [file] [log] [blame]
#include "binarystore.hpp"
#include "parse_config.hpp"
#include "sys_file_impl.hpp"
#include <getopt.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#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;
}