1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "bundler.h"
17
18 #include <assert.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21
22 #include <cassert>
23 #include <list>
24 #include <map>
25 #include <vector>
26
27 #include "bundler_schema_generated.h"
28 #include "flatbuffers/idl.h"
29 #include "flatbuffers/util.h"
30
31 using namespace bluetooth;
32 using namespace dumpsys;
33
34 struct Opts opts;
35
36 /**
37 * Load a binary schema from persistent store using flatbuffer API.
38 *
39 * @param filename; Name of file to open and read.
40 * @param binary_schema: Backing store for flatbuffer binary schema data.
41 *
42 * @return: True if operation successful, false otherwise.
43 */
LoadBinarySchema(const char * filename,std::string * binary_schema)44 bool LoadBinarySchema(const char* filename, std::string* binary_schema) {
45 assert(filename != nullptr);
46 assert(binary_schema != nullptr);
47 if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, binary_schema)) {
48 fprintf(stderr, "Unable to open binary flatbuffer schema file:%s\n", filename);
49 return false;
50 };
51 return true;
52 }
53
54 /**
55 * Verify a binary schema using flatbuffer API.
56 *
57 * @param schema: Raw binary schema to verify
58 *
59 * @return: True if operation successful, false otherwise.
60 */
VerifyBinarySchema(const std::vector<uint8_t> & raw_schema)61 bool VerifyBinarySchema(const std::vector<uint8_t>& raw_schema) {
62 flatbuffers::Verifier verifier(raw_schema.data(), raw_schema.size());
63 if (!reflection::VerifySchemaBuffer(verifier)) {
64 return false;
65 }
66 return true;
67 }
68
69 /**
70 * Bundle a set of binary flatbuffer schema into the bundler database.
71 *
72 * @param builder; Flatbuffer builder
73 * @param filenames: Set of filenames to include in bundle
74 * @param vector_map: Filename to filedata mapping
75 *
76 * @return: True if operation successful, false otherwise.
77 */
CreateBinarySchemaBundle(flatbuffers::FlatBufferBuilder * builder,const std::vector<std::string> & filenames,std::vector<flatbuffers::Offset<BundledSchemaMap>> * vector_map,std::list<std::string> * bundled_names)78 bool CreateBinarySchemaBundle(
79 flatbuffers::FlatBufferBuilder* builder,
80 const std::vector<std::string>& filenames,
81 std::vector<flatbuffers::Offset<BundledSchemaMap>>* vector_map,
82 std::list<std::string>* bundled_names) {
83 assert(builder != nullptr);
84 assert(vector_map != nullptr);
85 assert(bundled_names != nullptr);
86
87 for (auto filename : filenames) {
88 std::string string_schema;
89 if (!LoadBinarySchema(filename.c_str(), &string_schema)) {
90 fprintf(stderr, "Unable to load binary schema from filename:%s\n", filename.c_str());
91 return false;
92 }
93 std::vector<uint8_t> raw_schema(string_schema.begin(), string_schema.end());
94 if (!VerifyBinarySchema(raw_schema)) {
95 fprintf(stderr, "Failed verification on binary schema filename:%s\n", filename.c_str());
96 return false;
97 }
98
99 const reflection::Schema* schema = reflection::GetSchema(raw_schema.data());
100 if (schema->root_table() == nullptr) {
101 fprintf(stderr, "Unable to find root table for binary flatbuffer schema:%s\n", filename.c_str());
102 return false;
103 }
104
105 bundled_names->push_back(schema->root_table()->name()->str());
106 auto name = builder->CreateString(schema->root_table()->name()->str());
107 auto data = builder->CreateVector<uint8_t>(raw_schema.data(), raw_schema.size());
108 vector_map->push_back(CreateBundledSchemaMap(*builder, name, data));
109
110 if (opts.verbose) {
111 fprintf(stdout, "Bundled binary schema file:%s\n", schema->root_table()->name()->c_str());
112 }
113 }
114 return true;
115 }
116
117 /**
118 * Write generated header file containing the bundled binary schema
119 * data and meta data
120 *
121 * @param data: Source file data.
122 * @param data_len: length of data
123 */
WriteHeaderFile(FILE * fp,const uint8_t * data,size_t data_len)124 void WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len) {
125 assert(fp != nullptr);
126 std::string delim(kDefaultNamespaceDelim);
127 std::string ns_string(opts.ns_name);
128 std::vector<std::string> namespaces;
129
130 size_t start = 0;
131 size_t end = ns_string.find(delim);
132 while (end != std::string::npos) {
133 namespaces.push_back(ns_string.substr(start, end - start));
134 start = end + delim.size();
135 end = ns_string.find(delim, start);
136 }
137 if (start != 0 && start != std::string::npos) {
138 namespaces.push_back(ns_string.substr(start));
139 } else if (!ns_string.empty()) {
140 namespaces.push_back(ns_string);
141 }
142
143 std::string namespace_prefix;
144 for (const auto& name : namespaces) namespace_prefix += (name + '_');
145
146 fprintf(
147 fp,
148 "// Generated file by bluetooth_flatbuffer bundler\n"
149 "#include <string>\n");
150 for_each(
151 namespaces.begin(), namespaces.end(), [fp](const std::string& s) { fprintf(fp, "namespace %s {\n", s.c_str()); });
152 fprintf(fp, "extern const std::string& GetBundledSchemaData();\n");
153 fprintf(fp, "const unsigned char %sdata_[%zu] = {\n", namespace_prefix.c_str(), data_len);
154
155 for (size_t i = 0; i < data_len; i++) {
156 fprintf(fp, " 0x%02x", data[i]);
157 if (i != data_len - 1) {
158 fprintf(fp, ",");
159 }
160 if ((i + 1) % 16 == 0) {
161 fprintf(fp, "\n");
162 }
163 }
164 fprintf(fp, " };\n");
165 fprintf(
166 fp,
167 "const std::string %sstring_data_(%sdata_, %sdata_ + sizeof(%sdata_));\n",
168 namespace_prefix.c_str(),
169 namespace_prefix.c_str(),
170 namespace_prefix.c_str(),
171 namespace_prefix.c_str());
172 fprintf(fp, "const std::string& GetBundledSchemaData() { return %sstring_data_; }\n", namespace_prefix.c_str());
173
174 for_each(namespaces.crbegin(), namespaces.crend(), [fp](const std::string& s) {
175 fprintf(fp, "} // namespace %s\n", s.c_str());
176 });
177 }
178
ReadBundledSchema()179 int ReadBundledSchema() {
180 const char* filename = opts.filename;
181 assert(filename != nullptr);
182
183 std::string flatfile_data;
184 if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, &flatfile_data)) {
185 fprintf(stderr, "Unable to load schema data file:%s\n", filename);
186 return -5;
187 }
188
189 auto bundle_schema = flatbuffers::GetRoot<BundledSchema>(flatfile_data.c_str());
190 const flatbuffers::Vector<flatbuffers::Offset<BundledSchemaMap>>* map = bundle_schema->map();
191
192 fprintf(stdout, "Bundle schema title:%s\n", bundle_schema->title()->c_str());
193 fprintf(stdout, "Bundle schema root_name:%s\n", bundle_schema->root_name()->c_str());
194 int cnt = 0;
195 for (auto it = map->cbegin(); it != map->cend(); ++it, cnt++) {
196 fprintf(stdout, " %d name:%s schema:%s\n", cnt, it->name()->c_str(), "schema");
197 }
198 return EXIT_SUCCESS;
199 }
200
WriteBundledSchema()201 int WriteBundledSchema() {
202 const char* filename = opts.filename;
203 assert(filename != nullptr);
204
205 const char* main_root_name = opts.main_root_name;
206 if (main_root_name == nullptr) {
207 fprintf(stderr, "Must specify the name of the main root name for this bundle\n");
208 return EXIT_FAILURE;
209 }
210
211 std::vector<std::string> bfbs_filenames;
212 for (int i = 0; i < opts.arg.c; i++) {
213 bfbs_filenames.push_back(std::string(opts.arg.v[i]));
214 }
215 if (bfbs_filenames.empty()) {
216 fprintf(stderr, "No bfbs files are specified to bundle\n");
217 return EXIT_FAILURE;
218 }
219
220 flatbuffers::FlatBufferBuilder builder(1024);
221
222 std::list<std::string> bundled_names;
223 std::vector<flatbuffers::Offset<BundledSchemaMap>> vector_map;
224 if (!CreateBinarySchemaBundle(&builder, bfbs_filenames, &vector_map, &bundled_names)) {
225 fprintf(stderr, "Unable to bundle schema bfbs files\n");
226 return EXIT_FAILURE;
227 }
228
229 if (std::find(bundled_names.begin(), bundled_names.end(), main_root_name) == bundled_names.end()) {
230 fprintf(stderr, "The main root name must match one of the bundled schema names\n");
231 fprintf(stderr, " main root name:%s\n", main_root_name);
232 for (auto name : bundled_names) {
233 fprintf(stderr, " bundled schema name:%s\n", name.c_str());
234 }
235 return EXIT_FAILURE;
236 }
237
238 const char* title = opts.title;
239 auto schema_offset = CreateBundledSchemaDirect(builder, title, main_root_name, &vector_map);
240 builder.Finish(schema_offset);
241
242 std::string final_filename(opts.gen);
243 final_filename.append("/");
244 final_filename.append(filename);
245 if (!flatbuffers::SaveFile(
246 final_filename.c_str(), (const char*)builder.GetBufferPointer(), builder.GetSize(), helper::AsBinaryFile)) {
247 fprintf(stderr, "Unable to save file:%s\n", final_filename.c_str());
248 return EXIT_FAILURE;
249 }
250
251 std::string header(opts.gen);
252 header += ("/" + std::string(opts.filename) + ".cc");
253 FILE* fp = fopen(header.c_str(), "w+");
254 if (fp == nullptr) {
255 fprintf(stdout, "Unable to open for writing header file:%s\n", header.c_str());
256 return EXIT_FAILURE;
257 }
258 WriteHeaderFile(fp, builder.GetBufferPointer(), builder.GetSize());
259 fclose(fp);
260 return EXIT_SUCCESS;
261 }
262
Usage(int argc,char ** argv)263 int Usage(int argc, char** argv) {
264 fprintf(
265 stderr,
266 "Usage: %s [-r | -w] [-f <filename>] [-g <gen_out_path>] [-n <namespace> ] [-v] -m <main_root_name> <file.bfbs "
267 "...>\n",
268 argv[0]);
269 fprintf(stderr, " -r|-w : Read or write a dumpsys file\n");
270 fprintf(stderr, " -f : Filename bundled schema to read or write (default:%s)\n", kDefaultBundleDataFile);
271 fprintf(stderr, " -g : Generated file output path\n");
272 fprintf(stderr, " -n : Namespace to embed binary output bundle data source\n");
273 fprintf(stderr, " -m : Name of the main root of this bundle\n");
274 fprintf(stderr, " -v : Verbose printing mode\n");
275 return EXIT_FAILURE;
276 }
277
ParseArgs(int argc,char ** argv)278 void ParseArgs(int argc, char** argv) {
279 int opt;
280 int parsed_cnt = 1;
281 while ((opt = getopt(argc, argv, "f:g:m:n:rt:vw")) != -1) {
282 parsed_cnt++;
283 switch (opt) {
284 case 'f':
285 opts.filename = optarg;
286 parsed_cnt++;
287 break;
288 case 'g':
289 opts.gen = optarg;
290 parsed_cnt++;
291 break;
292 case 'm':
293 opts.main_root_name = optarg;
294 parsed_cnt++;
295 break;
296 case 'n':
297 opts.ns_name = optarg;
298 parsed_cnt++;
299 break;
300 case 'r':
301 opts.read = true;
302 break;
303 case 'w':
304 opts.write = true;
305 break;
306 case 't':
307 opts.title = optarg;
308 parsed_cnt++;
309 break;
310 case 'v':
311 opts.verbose = true;
312 break;
313 default:
314 exit(Usage(argc, argv));
315 break;
316 }
317 }
318 opts.arg.c = argc - parsed_cnt;
319 opts.arg.v = &argv[parsed_cnt];
320 }
321