blob: ac29abd263f5c3a408d8522943bbe0787884c909 [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>
9#include <iostream>
10#include <nlohmann/json.hpp>
11
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,
26 } action = Action::LIST;
27} toolConfig;
28
29void printUsage(const BlobToolConfig& cfg)
30{
31 std::cout
32 << "Usage: \n"
33 << cfg.programName << " [OPTIONS]\n"
34 << "\t--list\t\tList all supported blobs. This is a default.\n"
35 << "\t--read\t\tRead blob specified in --blob argument"
36 " (which becomes mandatory).\n"
37 << "\t--config\tFILENAME\tPath to the configuration file. The default "
38 "is /usr/share/binaryblob/config.json.\n"
39 << "\t--binary-store\tFILENAME\tPath to the binary storage. If "
40 "specified,"
41 "configuration file is not used.\n"
42 << "\t--blob\tSTRING\tThe name of the blob to read.\n"
43 << "\t--offset\tNUMBER\tThe offset in the binary store file, where"
44 " the binary store actually starts.\n"
45 << "\t--help\t\tPrint this help and exit\n";
46}
47
48bool parseOptions(int argc, char* argv[], BlobToolConfig& cfg)
49{
50 cfg.programName = argv[0];
51
52 struct option longOptions[] = {
53 {"help", no_argument, 0, 'h'},
54 {"list", no_argument, 0, 'l'},
55 {"read", no_argument, 0, 'r'},
56 {"config", required_argument, 0, 'c'},
57 {"binary-store", required_argument, 0, 's'},
58 {"blob", required_argument, 0, 'b'},
59 {"offset", required_argument, 0, 'g'},
60 {0, 0, 0, 0},
61 };
62
63 int optionIndex = 0;
64 std::string configPath = defaultBlobConfigPath;
65 bool res = true;
66 while (1)
67 {
68 int ret = getopt_long_only(argc, argv, "", longOptions, &optionIndex);
69
70 if (ret == -1)
71 break;
72
73 switch (ret)
74 {
75 case 'h':
76 cfg.action = BlobToolConfig::Action::HELP;
77 break;
78 case 'l':
79 cfg.action = BlobToolConfig::Action::LIST;
80 break;
81 case 'r':
82 cfg.action = BlobToolConfig::Action::READ;
83 break;
84 case 'c':
85 cfg.configPath = optarg;
86 break;
87 case 's':
88 cfg.binStore = optarg;
89 break;
90 case 'b':
91 cfg.blobName = optarg;
92 break;
93 case 'g':
94 cfg.offsetBytes = std::stoi(optarg);
95 break;
96 default:
97 res = false;
98 break;
99 }
100 }
101
102 return res;
103}
104
105int main(int argc, char* argv[])
106{
107 parseOptions(argc, argv, toolConfig);
108 if (toolConfig.action == BlobToolConfig::Action::HELP)
109 {
110 printUsage(toolConfig);
111 return 0;
112 }
113
114 std::vector<std::unique_ptr<binstore::BinaryStoreInterface>> stores;
115 if (!toolConfig.binStore.empty())
116 {
117 auto file = std::make_unique<binstore::SysFileImpl>(
118 toolConfig.binStore, toolConfig.offsetBytes);
119 if (!file)
120 {
121 std::cerr << "Can't open binary store " << toolConfig.binStore
122 << std::endl;
123 printUsage(toolConfig);
124 return 1;
125 }
126
127 auto store =
128 binstore::BinaryStore::createFromFile(std::move(file), true);
129 stores.push_back(std::move(store));
130 }
131 else
132 {
133 std::ifstream input(toolConfig.configPath);
134 json j;
135
136 if (!input.good())
137 {
138 std::cerr << "Config file not found: " << toolConfig.configPath
139 << std::endl;
140 return 1;
141 }
142
143 try
144 {
145 input >> j;
146 }
147 catch (const std::exception& e)
148 {
149 std::cerr << "Failed to parse config into json: " << std::endl
150 << e.what() << std::endl;
151 return 1;
152 }
153
154 for (const auto& element : j)
155 {
156 conf::BinaryBlobConfig config;
157 try
158 {
159 conf::parseFromConfigFile(element, config);
160 }
161 catch (const std::exception& e)
162 {
163 std::cerr << "Encountered error when parsing config file:"
164 << std::endl
165 << e.what() << std::endl;
166 return 1;
167 }
168
169 auto file = std::make_unique<binstore::SysFileImpl>(
170 config.sysFilePath, config.offsetBytes);
171
172 auto store = binstore::BinaryStore::createFromConfig(
173 config.blobBaseId, std::move(file));
174 stores.push_back(std::move(store));
175 }
176 }
177
178 if (toolConfig.action == BlobToolConfig::Action::LIST)
179 {
180 std::cout << "Supported Blobs: " << std::endl;
181 for (const auto& store : stores)
182 {
183 const auto blobIds = store->getBlobIds();
184 std::copy(
185 blobIds.begin(), blobIds.end(),
186 std::ostream_iterator<decltype(blobIds[0])>(std::cout, "\n"));
187 }
188 }
189 else if (toolConfig.action == BlobToolConfig::Action::READ)
190 {
191 if (toolConfig.blobName.empty())
192 {
193 std::cerr << "Must specify the name of the blob to read."
194 << std::endl;
195 printUsage(toolConfig);
196 return 1;
197 }
198
199 bool blobFound = false;
200
201 for (const auto& store : stores)
202 {
203 const auto blobIds = store->getBlobIds();
204 if (std::any_of(blobIds.begin(), blobIds.end(),
205 [](const std::string& bn) {
206 return bn == toolConfig.blobName;
207 }))
208 {
209 const auto blobData = store->readBlob(toolConfig.blobName);
210 if (blobData.empty())
211 {
212 std::cerr << "No data read from " << store->getBaseBlobId()
213 << std::endl;
214 continue;
215 }
216
217 blobFound = true;
218
219 std::copy(
220 blobData.begin(), blobData.end(),
221 std::ostream_iterator<decltype(blobData[0])>(std::cout));
222
223 // It's assumed that the names of the blobs are unique within
224 // the system.
225 break;
226 }
227 }
228
229 if (!blobFound)
230 {
231 std::cerr << "Blob " << toolConfig.blobName << " not found."
232 << std::endl;
233 return 1;
234 }
235 }
236
237 return 0;
238}