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