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