]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/cmdparse.cc
592b889b4597f83ab3decc9a162f75da1aba5c47
[ceph.git] / ceph / src / common / cmdparse.cc
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) 2013 Inktank Storage, Inc.
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License version 2, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include "json_spirit/json_spirit.h"
16 #include "common/debug.h"
17
18 using namespace std;
19
20 /**
21 * Given a cmddesc like "foo baz name=bar,type=CephString",
22 * return the prefix "foo baz".
23 */
24 std::string cmddesc_get_prefix(const std::string &cmddesc)
25 {
26 stringstream ss(cmddesc);
27 std::string word;
28 std::ostringstream result;
29 bool first = true;
30 while (std::getline(ss, word, ' ')) {
31 if (word.find_first_of(",=") != string::npos) {
32 break;
33 }
34
35 if (!first) {
36 result << " ";
37 }
38 result << word;
39 first = false;
40 }
41
42 return result.str();
43 }
44
45 /**
46 * Read a command description list out of cmd, and dump it to f.
47 * A signature description is a set of space-separated words;
48 * see MonCommands.h for more info.
49 */
50
51 void
52 dump_cmd_to_json(Formatter *f, const string& cmd)
53 {
54 // put whole command signature in an already-opened container
55 // elements are: "name", meaning "the typeless name that means a literal"
56 // an object {} with key:value pairs representing an argument
57
58 int argnum = 0;
59 stringstream ss(cmd);
60 std::string word;
61
62 while (std::getline(ss, word, ' ')) {
63 argnum++;
64 // if no , or =, must be a plain word to put out
65 if (word.find_first_of(",=") == string::npos) {
66 f->dump_string("arg", word);
67 continue;
68 }
69 // Snarf up all the key=val,key=val pairs, put 'em in a dict.
70 // no '=val' implies '=True'.
71 std::stringstream argdesc(word);
72 std::string keyval;
73 std::map<std::string, std::string>desckv;
74 // accumulate descriptor keywords in desckv
75
76 while (std::getline(argdesc, keyval, ',')) {
77 // key=value; key by itself implies value is bool true
78 // name="name" means arg dict will be titled 'name'
79 size_t pos = keyval.find('=');
80 std::string key, val;
81 if (pos != std::string::npos) {
82 key = keyval.substr(0, pos);
83 val = keyval.substr(pos+1);
84 } else {
85 key = keyval;
86 val = true;
87 }
88 desckv.insert(std::pair<std::string, std::string> (key, val));
89 }
90 // name the individual desc object based on the name key
91 f->open_object_section(desckv["name"].c_str());
92 // dump all the keys including name into the array
93 for (std::map<std::string, std::string>::iterator it = desckv.begin();
94 it != desckv.end(); ++it) {
95 f->dump_string(it->first.c_str(), it->second);
96 }
97 f->close_section(); // attribute object for individual desc
98 }
99 }
100
101 void
102 dump_cmd_and_help_to_json(Formatter *jf,
103 const string& secname,
104 const string& cmdsig,
105 const string& helptext)
106 {
107 jf->open_object_section(secname.c_str());
108 jf->open_array_section("sig");
109 dump_cmd_to_json(jf, cmdsig);
110 jf->close_section(); // sig array
111 jf->dump_string("help", helptext.c_str());
112 jf->close_section(); // cmd
113 }
114
115 void
116 dump_cmddesc_to_json(Formatter *jf,
117 const string& secname,
118 const string& cmdsig,
119 const string& helptext,
120 const string& module,
121 const string& perm,
122 const string& avail,
123 uint64_t flags)
124 {
125 jf->open_object_section(secname.c_str());
126 jf->open_array_section("sig");
127 dump_cmd_to_json(jf, cmdsig);
128 jf->close_section(); // sig array
129 jf->dump_string("help", helptext.c_str());
130 jf->dump_string("module", module.c_str());
131 jf->dump_string("perm", perm.c_str());
132 jf->dump_string("avail", avail.c_str());
133 jf->dump_int("flags", flags);
134 jf->close_section(); // cmd
135 }
136
137 void cmdmap_dump(const cmdmap_t &cmdmap, Formatter *f)
138 {
139 assert(f != nullptr);
140
141 class dump_visitor : public boost::static_visitor<void>
142 {
143 Formatter *f;
144 std::string const &key;
145 public:
146 dump_visitor(Formatter *f_, std::string const &key_)
147 : f(f_), key(key_)
148 {
149 }
150
151 void operator()(const std::string &operand) const
152 {
153 f->dump_string(key.c_str(), operand);
154 }
155
156 void operator()(const bool &operand) const
157 {
158 f->dump_bool(key.c_str(), operand);
159 }
160
161 void operator()(const int64_t &operand) const
162 {
163 f->dump_int(key.c_str(), operand);
164 }
165
166 void operator()(const double &operand) const
167 {
168 f->dump_float(key.c_str(), operand);
169 }
170
171 void operator()(const std::vector<std::string> &operand) const
172 {
173 f->open_array_section(key.c_str());
174 for (const auto i : operand) {
175 f->dump_string("item", i);
176 }
177 f->close_section();
178 }
179
180 void operator()(const std::vector<int64_t> &operand) const
181 {
182 f->open_array_section(key.c_str());
183 for (const auto i : operand) {
184 f->dump_int("item", i);
185 }
186 f->close_section();
187 }
188
189 void operator()(const std::vector<double> &operand) const
190 {
191 f->open_array_section(key.c_str());
192 for (const auto i : operand) {
193 f->dump_float("item", i);
194 }
195 f->close_section();
196 }
197 };
198
199 //f->open_object_section("cmdmap");
200 for (const auto &i : cmdmap) {
201 boost::apply_visitor(dump_visitor(f, i.first), i.second);
202 }
203 //f->close_section();
204 }
205
206
207 /** Parse JSON in vector cmd into a map from field to map of values
208 * (use mValue/mObject)
209 * 'cmd' should not disappear over lifetime of map
210 * 'mapp' points to the caller's map
211 * 'ss' captures any errors during JSON parsing; if function returns
212 * false, ss is valid */
213
214 bool
215 cmdmap_from_json(vector<string> cmd, map<string, cmd_vartype> *mapp, stringstream &ss)
216 {
217 json_spirit::mValue v;
218
219 string fullcmd;
220 // First, join all cmd strings
221 for (vector<string>::iterator it = cmd.begin();
222 it != cmd.end(); ++it)
223 fullcmd += *it;
224
225 try {
226 if (!json_spirit::read(fullcmd, v))
227 throw runtime_error("unparseable JSON " + fullcmd);
228 if (v.type() != json_spirit::obj_type)
229 throw(runtime_error("not JSON object " + fullcmd));
230
231 // allocate new mObject (map) to return
232 // make sure all contents are simple types (not arrays or objects)
233 json_spirit::mObject o = v.get_obj();
234 for (map<string, json_spirit::mValue>::iterator it = o.begin();
235 it != o.end(); ++it) {
236
237 // ok, marshal it into our string->cmd_vartype map, or throw an
238 // exception if it's not a simple datatype. This is kind of
239 // annoying, since json_spirit has a boost::variant inside it
240 // already, but it's not public. Oh well.
241
242 switch (it->second.type()) {
243
244 case json_spirit::obj_type:
245 default:
246 throw(runtime_error("JSON array/object not allowed " + fullcmd));
247 break;
248
249 case json_spirit::array_type:
250 {
251 // array is a vector of values. Unpack it to a vector
252 // of strings, doubles, or int64_t, the only types we handle.
253 const vector<json_spirit::mValue>& spvals = it->second.get_array();
254 if (spvals.empty()) {
255 // if an empty array is acceptable, the caller should always check for
256 // vector<string> if the expected value of "vector<int64_t>" in the
257 // cmdmap is missing.
258 (*mapp)[it->first] = vector<string>();
259 } else if (spvals.front().type() == json_spirit::str_type) {
260 vector<string> outv;
261 for (const auto& sv : spvals) {
262 if (sv.type() != json_spirit::str_type) {
263 throw(runtime_error("Can't handle arrays of multiple types"));
264 }
265 outv.push_back(sv.get_str());
266 }
267 (*mapp)[it->first] = std::move(outv);
268 } else if (spvals.front().type() == json_spirit::int_type) {
269 vector<int64_t> outv;
270 for (const auto& sv : spvals) {
271 if (spvals.front().type() != json_spirit::int_type) {
272 throw(runtime_error("Can't handle arrays of multiple types"));
273 }
274 outv.push_back(sv.get_int64());
275 }
276 (*mapp)[it->first] = std::move(outv);
277 } else if (spvals.front().type() == json_spirit::real_type) {
278 vector<double> outv;
279 for (const auto& sv : spvals) {
280 if (spvals.front().type() != json_spirit::real_type) {
281 throw(runtime_error("Can't handle arrays of multiple types"));
282 }
283 outv.push_back(sv.get_real());
284 }
285 (*mapp)[it->first] = std::move(outv);
286 } else {
287 throw(runtime_error("Can't handle arrays of types other than "
288 "int, string, or double"));
289 }
290 }
291 break;
292 case json_spirit::str_type:
293 (*mapp)[it->first] = it->second.get_str();
294 break;
295
296 case json_spirit::bool_type:
297 (*mapp)[it->first] = it->second.get_bool();
298 break;
299
300 case json_spirit::int_type:
301 (*mapp)[it->first] = it->second.get_int64();
302 break;
303
304 case json_spirit::real_type:
305 (*mapp)[it->first] = it->second.get_real();
306 break;
307 }
308 }
309 return true;
310 } catch (runtime_error &e) {
311 ss << e.what();
312 return false;
313 }
314 }
315
316 class stringify_visitor : public boost::static_visitor<string>
317 {
318 public:
319 template <typename T>
320 string operator()(T &operand) const
321 {
322 ostringstream oss;
323 oss << operand;
324 return oss.str();
325 }
326 };
327
328 string
329 cmd_vartype_stringify(const cmd_vartype &v)
330 {
331 return boost::apply_visitor(stringify_visitor(), v);
332 }
333
334
335 void
336 handle_bad_get(CephContext *cct, const string& k, const char *tname)
337 {
338 ostringstream errstr;
339 int status;
340 const char *typestr = abi::__cxa_demangle(tname, 0, 0, &status);
341 if (status != 0)
342 typestr = tname;
343 errstr << "bad boost::get: key " << k << " is not type " << typestr;
344 lderr(cct) << errstr.str() << dendl;
345
346 ostringstream oss;
347 oss << BackTrace(1);
348 lderr(cct) << oss.rdbuf() << dendl;
349 if (status == 0)
350 free((char *)typestr);
351 }
352
353 long parse_pos_long(const char *s, std::ostream *pss)
354 {
355 if (*s == '-' || *s == '+') {
356 if (pss)
357 *pss << "expected numerical value, got: " << s;
358 return -EINVAL;
359 }
360
361 string err;
362 long r = strict_strtol(s, 10, &err);
363 if ((r == 0) && !err.empty()) {
364 if (pss)
365 *pss << err;
366 return -1;
367 }
368 if (r < 0) {
369 if (pss)
370 *pss << "unable to parse positive integer '" << s << "'";
371 return -1;
372 }
373 return r;
374 }
375
376 int parse_osd_id(const char *s, std::ostream *pss)
377 {
378 // osd.NNN?
379 if (strncmp(s, "osd.", 4) == 0) {
380 s += 4;
381 }
382
383 // NNN?
384 ostringstream ss;
385 long id = parse_pos_long(s, &ss);
386 if (id < 0) {
387 *pss << ss.str();
388 return id;
389 }
390 if (id > 0xffff) {
391 *pss << "osd id " << id << " is too large";
392 return -ERANGE;
393 }
394 return id;
395 }