]>
Commit | Line | Data |
---|---|---|
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- | |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2015 Red Hat | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | ||
16 | #include <errno.h> | |
17 | ||
18 | #include <filesystem> | |
19 | #include <iomanip> | |
20 | ||
21 | #include "ceph_ver.h" | |
22 | #include "include/types.h" | |
23 | #include "common/Formatter.h" | |
24 | #include "common/ceph_argparse.h" | |
25 | #include "common/errno.h" | |
26 | #include "denc_plugin.h" | |
27 | #include "denc_registry.h" | |
28 | ||
29 | #define MB(m) ((m) * 1024 * 1024) | |
30 | ||
31 | namespace fs = std::filesystem; | |
32 | ||
33 | using namespace std; | |
34 | ||
35 | void usage(ostream &out) | |
36 | { | |
37 | out << "usage: ceph-dencoder [commands ...]" << std::endl; | |
38 | out << "\n"; | |
39 | out << " version print version string (to stdout)\n"; | |
40 | out << "\n"; | |
41 | out << " import <encfile> read encoded data from encfile\n"; | |
42 | out << " export <outfile> write encoded data to outfile\n"; | |
43 | out << "\n"; | |
44 | out << " set_features <num> set feature bits used for encoding\n"; | |
45 | out << " get_features print feature bits (int) to stdout\n"; | |
46 | out << "\n"; | |
47 | out << " list_types list supported types\n"; | |
48 | out << " type <classname> select in-memory type\n"; | |
49 | out << " skip <num> skip <num> leading bytes before decoding\n"; | |
50 | out << " decode decode into in-memory object\n"; | |
51 | out << " encode encode in-memory object\n"; | |
52 | out << " dump_json dump in-memory object as json (to stdout)\n"; | |
53 | out << " hexdump print encoded data in hex\n"; | |
54 | out << " get_struct_v print version of the encoded object\n"; | |
55 | out << " get_struct_compat print the oldest version of decoder that can decode the encoded object\n"; | |
56 | out << "\n"; | |
57 | out << " copy copy object (via operator=)\n"; | |
58 | out << " copy_ctor copy object (via copy ctor)\n"; | |
59 | out << "\n"; | |
60 | out << " count_tests print number of generated test objects (to stdout)\n"; | |
61 | out << " select_test <n> select generated test object as in-memory object\n"; | |
62 | out << " is_deterministic exit w/ success if type encodes deterministically\n"; | |
63 | } | |
64 | ||
65 | vector<DencoderPlugin> load_plugins() | |
66 | { | |
67 | fs::path mod_dir{CEPH_DENC_MOD_DIR}; | |
68 | if (auto ceph_lib = getenv("CEPH_LIB"); ceph_lib) { | |
69 | mod_dir = ceph_lib; | |
70 | } else if (fs::is_regular_file("CMakeCache.txt")) { | |
71 | mod_dir = std::filesystem::canonical("lib"); | |
72 | } | |
73 | if (!fs::is_directory(mod_dir)) { | |
74 | std::cerr << "unable to load dencoders from " | |
75 | << std::quoted(mod_dir.native()) << ". " | |
76 | << "it is not a directory." << std::endl; | |
77 | return {}; | |
78 | } | |
79 | vector<DencoderPlugin> dencoder_plugins; | |
80 | for (auto& entry : fs::directory_iterator(mod_dir)) { | |
81 | static const string_view DENC_MOD_PREFIX = "denc-mod-"; | |
82 | if (entry.path().stem().string().compare(0, DENC_MOD_PREFIX.size(), | |
83 | DENC_MOD_PREFIX) != 0) { | |
84 | continue; | |
85 | } | |
86 | DencoderPlugin plugin(entry); | |
87 | if (!plugin.good()) { | |
88 | continue; | |
89 | } | |
90 | dencoder_plugins.push_back(std::move(plugin)); | |
91 | } | |
92 | return dencoder_plugins; | |
93 | } | |
94 | ||
95 | int main(int argc, const char **argv) | |
96 | { | |
97 | vector<DencoderPlugin> plugins = load_plugins(); | |
98 | DencoderRegistry registry; | |
99 | for (auto& plugin : plugins) { | |
100 | for (auto& [name, denc] : plugin.register_dencoders()) { | |
101 | registry.register_dencoder(name, denc); | |
102 | } | |
103 | } | |
104 | ||
105 | auto args = argv_to_vec(argc, argv); | |
106 | env_to_vec(args); | |
107 | ||
108 | Dencoder *den = NULL; | |
109 | uint64_t features = CEPH_FEATURES_SUPPORTED_DEFAULT; | |
110 | bufferlist encbl; | |
111 | uint64_t skip = 0; | |
112 | ||
113 | if (args.empty()) { | |
114 | cerr << "-h for help" << std::endl; | |
115 | return 1; | |
116 | } | |
117 | for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ++i) { | |
118 | string err; | |
119 | ||
120 | auto& dencoders = registry.get(); | |
121 | if (*i == string("help") || *i == string("-h") || *i == string("--help")) { | |
122 | usage(cout); | |
123 | return 0; | |
124 | } else if (*i == string("version")) { | |
125 | cout << CEPH_GIT_NICE_VER << std::endl; | |
126 | } else if (*i == string("list_types")) { | |
127 | for (auto& dencoder : dencoders) | |
128 | cout << dencoder.first << std::endl; | |
129 | return 0; | |
130 | } else if (*i == string("type")) { | |
131 | ++i; | |
132 | if (i == args.end()) { | |
133 | cerr << "expecting type" << std::endl; | |
134 | return 1; | |
135 | } | |
136 | string cname = *i; | |
137 | if (!dencoders.count(cname)) { | |
138 | cerr << "class '" << cname << "' unknown" << std::endl; | |
139 | return 1; | |
140 | } | |
141 | den = dencoders[cname]; | |
142 | den->generate(); | |
143 | } else if (*i == string("skip")) { | |
144 | ++i; | |
145 | if (i == args.end()) { | |
146 | cerr << "expecting byte count" << std::endl; | |
147 | return 1; | |
148 | } | |
149 | skip = atoi(*i); | |
150 | } else if (*i == string("get_features")) { | |
151 | cout << CEPH_FEATURES_SUPPORTED_DEFAULT << std::endl; | |
152 | return 0; | |
153 | } else if (*i == string("set_features")) { | |
154 | ++i; | |
155 | if (i == args.end()) { | |
156 | cerr << "expecting features" << std::endl; | |
157 | return 1; | |
158 | } | |
159 | features = atoll(*i); | |
160 | } else if (*i == string("encode")) { | |
161 | if (!den) { | |
162 | cerr << "must first select type with 'type <name>'" << std::endl; | |
163 | return 1; | |
164 | } | |
165 | den->encode(encbl, features | CEPH_FEATURE_RESERVED); // hack for OSDMap | |
166 | } else if (*i == string("decode")) { | |
167 | if (!den) { | |
168 | cerr << "must first select type with 'type <name>'" << std::endl; | |
169 | return 1; | |
170 | } | |
171 | err = den->decode(encbl, skip); | |
172 | } else if (*i == string("copy_ctor")) { | |
173 | if (!den) { | |
174 | cerr << "must first select type with 'type <name>'" << std::endl; | |
175 | return 1; | |
176 | } | |
177 | den->copy_ctor(); | |
178 | } else if (*i == string("copy")) { | |
179 | if (!den) { | |
180 | cerr << "must first select type with 'type <name>'" << std::endl; | |
181 | return 1; | |
182 | } | |
183 | den->copy(); | |
184 | } else if (*i == string("dump_json")) { | |
185 | if (!den) { | |
186 | cerr << "must first select type with 'type <name>'" << std::endl; | |
187 | return 1; | |
188 | } | |
189 | JSONFormatter jf(true); | |
190 | jf.open_object_section("object"); | |
191 | den->dump(&jf); | |
192 | jf.close_section(); | |
193 | jf.flush(cout); | |
194 | cout << std::endl; | |
195 | ||
196 | } else if (*i == string("hexdump")) { | |
197 | encbl.hexdump(cout); | |
198 | } else if (*i == string("get_struct_v")) { | |
199 | std::cout << den->get_struct_v(encbl, 0) << std::endl; | |
200 | } else if (*i == string("get_struct_compat")) { | |
201 | std::cout << den->get_struct_v(encbl, sizeof(uint8_t)) << std::endl; | |
202 | } else if (*i == string("import")) { | |
203 | ++i; | |
204 | if (i == args.end()) { | |
205 | cerr << "expecting filename" << std::endl; | |
206 | return 1; | |
207 | } | |
208 | int r; | |
209 | if (*i == string("-")) { | |
210 | *i = "stdin"; | |
211 | // Read up to 1mb if stdin specified | |
212 | r = encbl.read_fd(STDIN_FILENO, MB(1)); | |
213 | } else { | |
214 | r = encbl.read_file(*i, &err); | |
215 | } | |
216 | if (r < 0) { | |
217 | cerr << "error reading " << *i << ": " << err << std::endl; | |
218 | return 1; | |
219 | } | |
220 | ||
221 | } else if (*i == string("export")) { | |
222 | ++i; | |
223 | if (i == args.end()) { | |
224 | cerr << "expecting filename" << std::endl; | |
225 | return 1; | |
226 | } | |
227 | int fd = ::open(*i, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644); | |
228 | if (fd < 0) { | |
229 | cerr << "error opening " << *i << " for write: " << cpp_strerror(errno) << std::endl; | |
230 | return 1; | |
231 | } | |
232 | int r = encbl.write_fd(fd); | |
233 | if (r < 0) { | |
234 | cerr << "error writing " << *i << ": " << cpp_strerror(errno) << std::endl; | |
235 | return 1; | |
236 | } | |
237 | ::close(fd); | |
238 | ||
239 | } else if (*i == string("count_tests")) { | |
240 | if (!den) { | |
241 | cerr << "must first select type with 'type <name>'" << std::endl; | |
242 | return 1; | |
243 | } | |
244 | cout << den->num_generated() << std::endl; | |
245 | } else if (*i == string("select_test")) { | |
246 | if (!den) { | |
247 | cerr << "must first select type with 'type <name>'" << std::endl; | |
248 | return 1; | |
249 | } | |
250 | ++i; | |
251 | if (i == args.end()) { | |
252 | cerr << "expecting instance number" << std::endl; | |
253 | return 1; | |
254 | } | |
255 | int n = atoi(*i); | |
256 | err = den->select_generated(n); | |
257 | } else if (*i == string("is_deterministic")) { | |
258 | if (!den) { | |
259 | cerr << "must first select type with 'type <name>'" << std::endl; | |
260 | return 1; | |
261 | } | |
262 | if (den->is_deterministic()) | |
263 | return 0; | |
264 | else | |
265 | return 1; | |
266 | } else { | |
267 | cerr << "unknown option '" << *i << "'" << std::endl; | |
268 | return 1; | |
269 | } | |
270 | if (err.length()) { | |
271 | cerr << "error: " << err << std::endl; | |
272 | return 1; | |
273 | } | |
274 | } | |
275 | return 0; | |
276 | } |