blob: c1e2e65a7e5289e5e5b6c8d7af2de39eb2137415 [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>
Maksym Sloykoeba86882021-10-19 18:21:40 +00009#include <nlohmann/json.hpp>
Willy Tuca170bb2023-11-06 00:26:43 -080010#include <stdplus/print.hpp>
Maksym Sloykoeba86882021-10-19 18:21:40 +000011
12constexpr auto defaultBlobConfigPath = "/usr/share/binaryblob/config.json";
13
14struct BlobToolConfig
15{
16 std::string configPath = defaultBlobConfigPath;
17 std::string programName;
18 std::string binStore;
19 std::string blobName;
20 size_t offsetBytes = 0;
21 enum class Action
22 {
23 HELP,
24 LIST,
25 READ,
Willy Tu7f107802023-11-06 23:05:25 -080026 MIGRATE,
Maksym Sloykoeba86882021-10-19 18:21:40 +000027 } action = Action::LIST;
28} toolConfig;
29
30void printUsage(const BlobToolConfig& cfg)
31{
Willy Tuca170bb2023-11-06 00:26:43 -080032 stdplus::print(stderr,
33 "Usage: \n"
34 "{} [OPTIONS]\n"
35 "\t--list\t\tList all supported blobs. This is a default.\n"
36 "\t--read\t\tRead blob specified in --blob argument (which "
37 "becomes mandatory).\n"
Willy Tu7f107802023-11-06 23:05:25 -080038 "\t--migrate\tUpdate all binary stores to use the alias "
39 "blob id if enabled.\n"
Willy Tuca170bb2023-11-06 00:26:43 -080040 "\t--config\tFILENAME\tPath to the configuration file. The "
41 "default is /usr/share/binaryblob/config.json.\n"
42 "\t--binary-store\tFILENAME\tPath to the binary storage. If "
43 "specified, configuration file is not used.\n"
44 "\t--blob\tSTRING\tThe name of the blob to read.\n"
45 "\t--offset\tNUMBER\tThe offset in the binary store file, "
46 "where the binary store actually starts.\n"
47 "\t--help\t\tPrint this help and exit\n",
48 cfg.programName);
Maksym Sloykoeba86882021-10-19 18:21:40 +000049}
50
51bool parseOptions(int argc, char* argv[], BlobToolConfig& cfg)
52{
53 cfg.programName = argv[0];
54
55 struct option longOptions[] = {
56 {"help", no_argument, 0, 'h'},
57 {"list", no_argument, 0, 'l'},
58 {"read", no_argument, 0, 'r'},
Willy Tu7f107802023-11-06 23:05:25 -080059 {"migrate", no_argument, 0, 'm'},
Maksym Sloykoeba86882021-10-19 18:21:40 +000060 {"config", required_argument, 0, 'c'},
61 {"binary-store", required_argument, 0, 's'},
62 {"blob", required_argument, 0, 'b'},
63 {"offset", required_argument, 0, 'g'},
64 {0, 0, 0, 0},
65 };
66
67 int optionIndex = 0;
Maksym Sloykoeba86882021-10-19 18:21:40 +000068 bool res = true;
69 while (1)
70 {
71 int ret = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
72
73 if (ret == -1)
74 break;
75
76 switch (ret)
77 {
78 case 'h':
79 cfg.action = BlobToolConfig::Action::HELP;
80 break;
81 case 'l':
82 cfg.action = BlobToolConfig::Action::LIST;
83 break;
84 case 'r':
85 cfg.action = BlobToolConfig::Action::READ;
86 break;
Willy Tu7f107802023-11-06 23:05:25 -080087 case 'm':
88 cfg.action = BlobToolConfig::Action::MIGRATE;
89 break;
Maksym Sloykoeba86882021-10-19 18:21:40 +000090 case 'c':
91 cfg.configPath = optarg;
92 break;
93 case 's':
94 cfg.binStore = optarg;
95 break;
96 case 'b':
97 cfg.blobName = optarg;
98 break;
99 case 'g':
100 cfg.offsetBytes = std::stoi(optarg);
101 break;
102 default:
103 res = false;
104 break;
105 }
106 }
107
108 return res;
109}
110
111int main(int argc, char* argv[])
112{
113 parseOptions(argc, argv, toolConfig);
114 if (toolConfig.action == BlobToolConfig::Action::HELP)
115 {
116 printUsage(toolConfig);
117 return 0;
118 }
119
120 std::vector<std::unique_ptr<binstore::BinaryStoreInterface>> stores;
121 if (!toolConfig.binStore.empty())
122 {
123 auto file = std::make_unique<binstore::SysFileImpl>(
124 toolConfig.binStore, toolConfig.offsetBytes);
125 if (!file)
126 {
Willy Tuca170bb2023-11-06 00:26:43 -0800127 stdplus::print(stderr, "Can't open binary store {}\n",
128 toolConfig.binStore);
Maksym Sloykoeba86882021-10-19 18:21:40 +0000129 printUsage(toolConfig);
130 return 1;
131 }
132
133 auto store =
134 binstore::BinaryStore::createFromFile(std::move(file), true);
135 stores.push_back(std::move(store));
136 }
137 else
138 {
139 std::ifstream input(toolConfig.configPath);
140 json j;
141
142 if (!input.good())
143 {
Willy Tuca170bb2023-11-06 00:26:43 -0800144 stdplus::print(stderr, "Config file not found:{}\n",
145 toolConfig.configPath);
Maksym Sloykoeba86882021-10-19 18:21:40 +0000146 return 1;
147 }
148
149 try
150 {
151 input >> j;
152 }
153 catch (const std::exception& e)
154 {
Willy Tuca170bb2023-11-06 00:26:43 -0800155 stdplus::print(stderr, "Failed to parse config into json:\n{}\n",
156 e.what());
Maksym Sloykoeba86882021-10-19 18:21:40 +0000157 return 1;
158 }
159
160 for (const auto& element : j)
161 {
162 conf::BinaryBlobConfig config;
163 try
164 {
165 conf::parseFromConfigFile(element, config);
166 }
167 catch (const std::exception& e)
168 {
Willy Tuca170bb2023-11-06 00:26:43 -0800169 stdplus::print(
170 stderr, "Encountered error when parsing config file:\n{}\n",
171 e.what());
Maksym Sloykoeba86882021-10-19 18:21:40 +0000172 return 1;
173 }
174
175 auto file = std::make_unique<binstore::SysFileImpl>(
176 config.sysFilePath, config.offsetBytes);
177
178 auto store = binstore::BinaryStore::createFromConfig(
Willy Tu7f107802023-11-06 23:05:25 -0800179 config.blobBaseId, std::move(file), config.maxSizeBytes,
180 config.aliasBlobBaseId);
181
182 if (toolConfig.action == BlobToolConfig::Action::MIGRATE)
183 {
184 if (config.migrateToAlias && config.aliasBlobBaseId.has_value())
185 {
186 store->setBaseBlobId(config.aliasBlobBaseId.value());
187 }
188 }
189 else
190 {
191 stores.push_back(std::move(store));
192 }
Maksym Sloykoeba86882021-10-19 18:21:40 +0000193 }
194 }
195
Willy Tu7f107802023-11-06 23:05:25 -0800196 if (toolConfig.action == BlobToolConfig::Action::MIGRATE)
197 {
198 stdplus::print(stderr,
199 "Migrated all BinaryStore back to configured Alias\n");
200 return 0;
201 }
202
Maksym Sloykoeba86882021-10-19 18:21:40 +0000203 if (toolConfig.action == BlobToolConfig::Action::LIST)
204 {
Willy Tuca170bb2023-11-06 00:26:43 -0800205 stdplus::print(stderr, "Supported Blobs: \n");
Maksym Sloykoeba86882021-10-19 18:21:40 +0000206 for (const auto& store : stores)
207 {
208 const auto blobIds = store->getBlobIds();
209 std::copy(
210 blobIds.begin(), blobIds.end(),
211 std::ostream_iterator<decltype(blobIds[0])>(std::cout, "\n"));
212 }
Willy Tu7f107802023-11-06 23:05:25 -0800213 return 0;
Maksym Sloykoeba86882021-10-19 18:21:40 +0000214 }
Willy Tu7f107802023-11-06 23:05:25 -0800215 if (toolConfig.action == BlobToolConfig::Action::READ)
Maksym Sloykoeba86882021-10-19 18:21:40 +0000216 {
217 if (toolConfig.blobName.empty())
218 {
Willy Tuca170bb2023-11-06 00:26:43 -0800219 stdplus::print(stderr,
220 "Must specify the name of the blob to read.\n");
Maksym Sloykoeba86882021-10-19 18:21:40 +0000221 printUsage(toolConfig);
222 return 1;
223 }
224
225 bool blobFound = false;
226
227 for (const auto& store : stores)
228 {
229 const auto blobIds = store->getBlobIds();
230 if (std::any_of(blobIds.begin(), blobIds.end(),
231 [](const std::string& bn) {
232 return bn == toolConfig.blobName;
233 }))
234 {
235 const auto blobData = store->readBlob(toolConfig.blobName);
236 if (blobData.empty())
237 {
Willy Tuca170bb2023-11-06 00:26:43 -0800238 stdplus::print(stderr, "No data read from {}\n",
239 store->getBaseBlobId());
Maksym Sloykoeba86882021-10-19 18:21:40 +0000240 continue;
241 }
242
243 blobFound = true;
244
245 std::copy(
246 blobData.begin(), blobData.end(),
247 std::ostream_iterator<decltype(blobData[0])>(std::cout));
248
249 // It's assumed that the names of the blobs are unique within
250 // the system.
251 break;
252 }
253 }
254
255 if (!blobFound)
256 {
Willy Tuca170bb2023-11-06 00:26:43 -0800257 stdplus::print(stderr, "Blob {} not found.\n", toolConfig.blobName);
Maksym Sloykoeba86882021-10-19 18:21:40 +0000258 return 1;
259 }
Willy Tu7f107802023-11-06 23:05:25 -0800260 return 0;
Maksym Sloykoeba86882021-10-19 18:21:40 +0000261 }
262
263 return 0;
264}