blob: 463849df077792a2db00281db07ed93a22517b27 [file] [log] [blame]
Maksym Sloykoeba86882021-10-19 18:21:40 +00001#include "binarystore.hpp"
2#include "parse_config.hpp"
3#include "sys_file_impl.hpp"
4
5#include <getopt.h>
6
7#include <algorithm>
8#include <fstream>
William A. Kennington III7e145862024-02-01 15:56:33 -08009#include <iostream>
Maksym Sloykoeba86882021-10-19 18:21:40 +000010#include <nlohmann/json.hpp>
Willy Tuca170bb2023-11-06 00:26:43 -080011#include <stdplus/print.hpp>
Maksym Sloykoeba86882021-10-19 18:21:40 +000012
13constexpr auto defaultBlobConfigPath = "/usr/share/binaryblob/config.json";
14
15struct BlobToolConfig
16{
17 std::string configPath = defaultBlobConfigPath;
18 std::string programName;
19 std::string binStore;
20 std::string blobName;
21 size_t offsetBytes = 0;
22 enum class Action
23 {
24 HELP,
25 LIST,
26 READ,
Willy Tu7f107802023-11-06 23:05:25 -080027 MIGRATE,
Maksym Sloykoeba86882021-10-19 18:21:40 +000028 } action = Action::LIST;
29} toolConfig;
30
31void printUsage(const BlobToolConfig& cfg)
32{
Willy Tuca170bb2023-11-06 00:26:43 -080033 stdplus::print(stderr,
34 "Usage: \n"
35 "{} [OPTIONS]\n"
36 "\t--list\t\tList all supported blobs. This is a default.\n"
37 "\t--read\t\tRead blob specified in --blob argument (which "
38 "becomes mandatory).\n"
Willy Tu7f107802023-11-06 23:05:25 -080039 "\t--migrate\tUpdate all binary stores to use the alias "
40 "blob id if enabled.\n"
Willy Tuca170bb2023-11-06 00:26:43 -080041 "\t--config\tFILENAME\tPath to the configuration file. The "
42 "default is /usr/share/binaryblob/config.json.\n"
43 "\t--binary-store\tFILENAME\tPath to the binary storage. If "
44 "specified, configuration file is not used.\n"
45 "\t--blob\tSTRING\tThe name of the blob to read.\n"
46 "\t--offset\tNUMBER\tThe offset in the binary store file, "
47 "where the binary store actually starts.\n"
48 "\t--help\t\tPrint this help and exit\n",
49 cfg.programName);
Maksym Sloykoeba86882021-10-19 18:21:40 +000050}
51
52bool parseOptions(int argc, char* argv[], BlobToolConfig& cfg)
53{
54 cfg.programName = argv[0];
55
56 struct option longOptions[] = {
57 {"help", no_argument, 0, 'h'},
58 {"list", no_argument, 0, 'l'},
59 {"read", no_argument, 0, 'r'},
Willy Tu7f107802023-11-06 23:05:25 -080060 {"migrate", no_argument, 0, 'm'},
Maksym Sloykoeba86882021-10-19 18:21:40 +000061 {"config", required_argument, 0, 'c'},
62 {"binary-store", required_argument, 0, 's'},
63 {"blob", required_argument, 0, 'b'},
64 {"offset", required_argument, 0, 'g'},
65 {0, 0, 0, 0},
66 };
67
68 int optionIndex = 0;
Maksym Sloykoeba86882021-10-19 18:21:40 +000069 bool res = true;
70 while (1)
71 {
72 int ret = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
73
74 if (ret == -1)
75 break;
76
77 switch (ret)
78 {
79 case 'h':
80 cfg.action = BlobToolConfig::Action::HELP;
81 break;
82 case 'l':
83 cfg.action = BlobToolConfig::Action::LIST;
84 break;
85 case 'r':
86 cfg.action = BlobToolConfig::Action::READ;
87 break;
Willy Tu7f107802023-11-06 23:05:25 -080088 case 'm':
89 cfg.action = BlobToolConfig::Action::MIGRATE;
90 break;
Maksym Sloykoeba86882021-10-19 18:21:40 +000091 case 'c':
92 cfg.configPath = optarg;
93 break;
94 case 's':
95 cfg.binStore = optarg;
96 break;
97 case 'b':
98 cfg.blobName = optarg;
99 break;
100 case 'g':
101 cfg.offsetBytes = std::stoi(optarg);
102 break;
103 default:
104 res = false;
105 break;
106 }
107 }
108
109 return res;
110}
111
112int main(int argc, char* argv[])
113{
114 parseOptions(argc, argv, toolConfig);
115 if (toolConfig.action == BlobToolConfig::Action::HELP)
116 {
117 printUsage(toolConfig);
118 return 0;
119 }
120
121 std::vector<std::unique_ptr<binstore::BinaryStoreInterface>> stores;
122 if (!toolConfig.binStore.empty())
123 {
124 auto file = std::make_unique<binstore::SysFileImpl>(
125 toolConfig.binStore, toolConfig.offsetBytes);
126 if (!file)
127 {
Willy Tuca170bb2023-11-06 00:26:43 -0800128 stdplus::print(stderr, "Can't open binary store {}\n",
129 toolConfig.binStore);
Maksym Sloykoeba86882021-10-19 18:21:40 +0000130 printUsage(toolConfig);
131 return 1;
132 }
133
134 auto store =
135 binstore::BinaryStore::createFromFile(std::move(file), true);
136 stores.push_back(std::move(store));
137 }
138 else
139 {
140 std::ifstream input(toolConfig.configPath);
141 json j;
142
143 if (!input.good())
144 {
Willy Tuca170bb2023-11-06 00:26:43 -0800145 stdplus::print(stderr, "Config file not found:{}\n",
146 toolConfig.configPath);
Maksym Sloykoeba86882021-10-19 18:21:40 +0000147 return 1;
148 }
149
150 try
151 {
152 input >> j;
153 }
154 catch (const std::exception& e)
155 {
Willy Tuca170bb2023-11-06 00:26:43 -0800156 stdplus::print(stderr, "Failed to parse config into json:\n{}\n",
157 e.what());
Maksym Sloykoeba86882021-10-19 18:21:40 +0000158 return 1;
159 }
160
161 for (const auto& element : j)
162 {
163 conf::BinaryBlobConfig config;
164 try
165 {
166 conf::parseFromConfigFile(element, config);
167 }
168 catch (const std::exception& e)
169 {
Willy Tuca170bb2023-11-06 00:26:43 -0800170 stdplus::print(
171 stderr, "Encountered error when parsing config file:\n{}\n",
172 e.what());
Maksym Sloykoeba86882021-10-19 18:21:40 +0000173 return 1;
174 }
175
176 auto file = std::make_unique<binstore::SysFileImpl>(
177 config.sysFilePath, config.offsetBytes);
178
179 auto store = binstore::BinaryStore::createFromConfig(
Willy Tu7f107802023-11-06 23:05:25 -0800180 config.blobBaseId, std::move(file), config.maxSizeBytes,
181 config.aliasBlobBaseId);
182
183 if (toolConfig.action == BlobToolConfig::Action::MIGRATE)
184 {
185 if (config.migrateToAlias && config.aliasBlobBaseId.has_value())
186 {
187 store->setBaseBlobId(config.aliasBlobBaseId.value());
188 }
189 }
190 else
191 {
192 stores.push_back(std::move(store));
193 }
Maksym Sloykoeba86882021-10-19 18:21:40 +0000194 }
195 }
196
Willy Tu7f107802023-11-06 23:05:25 -0800197 if (toolConfig.action == BlobToolConfig::Action::MIGRATE)
198 {
199 stdplus::print(stderr,
200 "Migrated all BinaryStore back to configured Alias\n");
201 return 0;
202 }
203
Maksym Sloykoeba86882021-10-19 18:21:40 +0000204 if (toolConfig.action == BlobToolConfig::Action::LIST)
205 {
Willy Tuca170bb2023-11-06 00:26:43 -0800206 stdplus::print(stderr, "Supported Blobs: \n");
Maksym Sloykoeba86882021-10-19 18:21:40 +0000207 for (const auto& store : stores)
208 {
209 const auto blobIds = store->getBlobIds();
210 std::copy(
211 blobIds.begin(), blobIds.end(),
212 std::ostream_iterator<decltype(blobIds[0])>(std::cout, "\n"));
213 }
Willy Tu7f107802023-11-06 23:05:25 -0800214 return 0;
Maksym Sloykoeba86882021-10-19 18:21:40 +0000215 }
Willy Tu7f107802023-11-06 23:05:25 -0800216 if (toolConfig.action == BlobToolConfig::Action::READ)
Maksym Sloykoeba86882021-10-19 18:21:40 +0000217 {
218 if (toolConfig.blobName.empty())
219 {
Willy Tuca170bb2023-11-06 00:26:43 -0800220 stdplus::print(stderr,
221 "Must specify the name of the blob to read.\n");
Maksym Sloykoeba86882021-10-19 18:21:40 +0000222 printUsage(toolConfig);
223 return 1;
224 }
225
226 bool blobFound = false;
227
228 for (const auto& store : stores)
229 {
230 const auto blobIds = store->getBlobIds();
231 if (std::any_of(blobIds.begin(), blobIds.end(),
232 [](const std::string& bn) {
233 return bn == toolConfig.blobName;
234 }))
235 {
236 const auto blobData = store->readBlob(toolConfig.blobName);
237 if (blobData.empty())
238 {
Willy Tuca170bb2023-11-06 00:26:43 -0800239 stdplus::print(stderr, "No data read from {}\n",
240 store->getBaseBlobId());
Maksym Sloykoeba86882021-10-19 18:21:40 +0000241 continue;
242 }
243
244 blobFound = true;
245
246 std::copy(
247 blobData.begin(), blobData.end(),
248 std::ostream_iterator<decltype(blobData[0])>(std::cout));
249
250 // It's assumed that the names of the blobs are unique within
251 // the system.
252 break;
253 }
254 }
255
256 if (!blobFound)
257 {
Willy Tuca170bb2023-11-06 00:26:43 -0800258 stdplus::print(stderr, "Blob {} not found.\n", toolConfig.blobName);
Maksym Sloykoeba86882021-10-19 18:21:40 +0000259 return 1;
260 }
Willy Tu7f107802023-11-06 23:05:25 -0800261 return 0;
Maksym Sloykoeba86882021-10-19 18:21:40 +0000262 }
263
264 return 0;
265}