]> git.proxmox.com Git - ceph.git/blame - ceph/src/mon/MonCap.cc
update sources to v12.1.3
[ceph.git] / ceph / src / mon / MonCap.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
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#include <boost/config/warning_disable.hpp>
16#include <boost/spirit/include/qi_uint.hpp>
17#include <boost/spirit/include/qi.hpp>
18#include <boost/fusion/include/std_pair.hpp>
19#include <boost/spirit/include/phoenix.hpp>
20#include <boost/fusion/adapted/struct/adapt_struct.hpp>
21#include <boost/fusion/include/adapt_struct.hpp>
22
23#include "MonCap.h"
24#include "include/stringify.h"
25#include "common/debug.h"
26#include "common/Formatter.h"
27
28#include <algorithm>
29
c07f9fc5
FG
30#include <boost/regex.hpp>
31#include "include/assert.h"
32
7c673cae
FG
33static inline bool is_not_alnum_space(char c)
34{
35 return !(isalpha(c) || isdigit(c) || (c == '-') || (c == '_'));
36}
37
38static string maybe_quote_string(const std::string& str)
39{
40 if (find_if(str.begin(), str.end(), is_not_alnum_space) == str.end())
41 return str;
42 return string("\"") + str + string("\"");
43}
44
45using std::ostream;
46using std::vector;
47
48#define dout_subsys ceph_subsys_mon
49
31f18b77 50ostream& operator<<(ostream& out, const mon_rwxa_t& p)
7c673cae
FG
51{
52 if (p == MON_CAP_ANY)
53 return out << "*";
54
55 if (p & MON_CAP_R)
56 out << "r";
57 if (p & MON_CAP_W)
58 out << "w";
59 if (p & MON_CAP_X)
60 out << "x";
61 return out;
62}
63
64ostream& operator<<(ostream& out, const StringConstraint& c)
65{
c07f9fc5
FG
66 switch (c.match_type) {
67 case StringConstraint::MATCH_TYPE_EQUAL:
7c673cae 68 return out << "value " << c.value;
c07f9fc5
FG
69 case StringConstraint::MATCH_TYPE_PREFIX:
70 return out << "prefix " << c.value;
71 case StringConstraint::MATCH_TYPE_REGEX:
72 return out << "regex " << c.value;
73 default:
74 break;
75 }
76 return out;
7c673cae
FG
77}
78
79ostream& operator<<(ostream& out, const MonCapGrant& m)
80{
81 out << "allow";
82 if (m.service.length()) {
83 out << " service " << maybe_quote_string(m.service);
84 }
85 if (m.command.length()) {
86 out << " command " << maybe_quote_string(m.command);
87 if (!m.command_args.empty()) {
88 out << " with";
89 for (map<string,StringConstraint>::const_iterator p = m.command_args.begin();
90 p != m.command_args.end();
91 ++p) {
c07f9fc5
FG
92 switch (p->second.match_type) {
93 case StringConstraint::MATCH_TYPE_EQUAL:
94 out << " " << maybe_quote_string(p->first) << "="
95 << maybe_quote_string(p->second.value);
96 break;
97 case StringConstraint::MATCH_TYPE_PREFIX:
98 out << " " << maybe_quote_string(p->first) << " prefix "
99 << maybe_quote_string(p->second.value);
100 break;
101 case StringConstraint::MATCH_TYPE_REGEX:
102 out << " " << maybe_quote_string(p->first) << " regex "
103 << maybe_quote_string(p->second.value);
104 break;
105 default:
106 break;
107 }
7c673cae
FG
108 }
109 }
110 }
111 if (m.profile.length()) {
112 out << " profile " << maybe_quote_string(m.profile);
113 }
114 if (m.allow != 0)
115 out << " " << m.allow;
116 return out;
117}
118
119
120// <magic>
121// fusion lets us easily populate structs via the qi parser.
122
123typedef map<string,StringConstraint> kvmap;
124
125BOOST_FUSION_ADAPT_STRUCT(MonCapGrant,
126 (std::string, service)
127 (std::string, profile)
128 (std::string, command)
129 (kvmap, command_args)
130 (mon_rwxa_t, allow))
131
132BOOST_FUSION_ADAPT_STRUCT(StringConstraint,
c07f9fc5
FG
133 (StringConstraint::MatchType, match_type)
134 (std::string, value))
7c673cae
FG
135
136// </magic>
137
138void MonCapGrant::expand_profile(int daemon_type, const EntityName& name) const
139{
140 // only generate this list once
141 if (!profile_grants.empty())
142 return;
143
144 if (profile == "read-only") {
145 // grants READ-ONLY caps monitor-wide
146 // 'auth' requires MON_CAP_X even for RO, which we do not grant here.
147 profile_grants.push_back(mon_rwxa_t(MON_CAP_R));
148 return;
149 }
150
151 if (profile == "read-write") {
152 // grants READ-WRITE caps monitor-wide
153 // 'auth' requires MON_CAP_X for all operations, which we do not grant.
154 profile_grants.push_back(mon_rwxa_t(MON_CAP_R | MON_CAP_W));
155 return;
156 }
157
158 switch (daemon_type) {
159 case CEPH_ENTITY_TYPE_MON:
160 expand_profile_mon(name);
161 return;
162 case CEPH_ENTITY_TYPE_MGR:
163 expand_profile_mgr(name);
164 return;
165 }
166}
167
168void MonCapGrant::expand_profile_mgr(const EntityName& name) const
169{
170}
171
172void MonCapGrant::expand_profile_mon(const EntityName& name) const
173{
174 if (profile == "mon") {
175 profile_grants.push_back(MonCapGrant("mon", MON_CAP_ALL));
176 profile_grants.push_back(MonCapGrant("log", MON_CAP_ALL));
177 }
178 if (profile == "osd") {
179 profile_grants.push_back(MonCapGrant("osd", MON_CAP_ALL));
180 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
181 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R | MON_CAP_W));
182 profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
183 }
184 if (profile == "mds") {
185 profile_grants.push_back(MonCapGrant("mds", MON_CAP_ALL));
186 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
187 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
188 // This command grant is checked explicitly in MRemoveSnaps handling
189 profile_grants.push_back(MonCapGrant("osd pool rmsnap"));
31f18b77 190 profile_grants.push_back(MonCapGrant("osd blacklist"));
7c673cae
FG
191 profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
192 }
193 if (profile == "mgr") {
194 profile_grants.push_back(MonCapGrant("mgr", MON_CAP_ALL));
31f18b77
FG
195 profile_grants.push_back(MonCapGrant("log", MON_CAP_R | MON_CAP_W));
196 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R | MON_CAP_W));
197 profile_grants.push_back(MonCapGrant("mds", MON_CAP_R | MON_CAP_W));
7c673cae 198 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R | MON_CAP_W));
31f18b77
FG
199 profile_grants.push_back(MonCapGrant("auth", MON_CAP_R | MON_CAP_X));
200 profile_grants.push_back(MonCapGrant("config-key", MON_CAP_R | MON_CAP_W));
c07f9fc5
FG
201 StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
202 "daemon-private/mgr/");
203 profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
204 profile_grants.push_back(MonCapGrant("config-key set", "key", constraint));
205 profile_grants.push_back(MonCapGrant("config-key put", "key", constraint));
206 profile_grants.push_back(MonCapGrant("config-key exists", "key", constraint));
207 profile_grants.push_back(MonCapGrant("config-key delete", "key", constraint));
7c673cae
FG
208 }
209 if (profile == "osd" || profile == "mds" || profile == "mon" ||
210 profile == "mgr") {
c07f9fc5
FG
211 StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
212 string("daemon-private/") + stringify(name) +
213 string("/"));
7c673cae 214 string prefix = string("daemon-private/") + stringify(name) + string("/");
c07f9fc5
FG
215 profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
216 profile_grants.push_back(MonCapGrant("config-key put", "key", constraint));
217 profile_grants.push_back(MonCapGrant("config-key set", "key", constraint));
218 profile_grants.push_back(MonCapGrant("config-key exists", "key", constraint));
219 profile_grants.push_back(MonCapGrant("config-key delete", "key", constraint));
7c673cae
FG
220 }
221 if (profile == "bootstrap-osd") {
7c673cae
FG
222 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
223 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
224 profile_grants.push_back(MonCapGrant("mon getmap"));
c07f9fc5 225 profile_grants.push_back(MonCapGrant("osd new"));
7c673cae
FG
226 }
227 if (profile == "bootstrap-mds") {
228 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
229 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
230 profile_grants.push_back(MonCapGrant("mon getmap"));
231 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
c07f9fc5
FG
232 profile_grants.back().command_args["entity"] = StringConstraint(
233 StringConstraint::MATCH_TYPE_PREFIX, "mds.");
234 profile_grants.back().command_args["caps_mon"] = StringConstraint(
235 StringConstraint::MATCH_TYPE_EQUAL, "allow profile mds");
236 profile_grants.back().command_args["caps_osd"] = StringConstraint(
237 StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
238 profile_grants.back().command_args["caps_mds"] = StringConstraint(
239 StringConstraint::MATCH_TYPE_EQUAL, "allow");
7c673cae
FG
240 }
241 if (profile == "bootstrap-mgr") {
242 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
243 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
244 profile_grants.push_back(MonCapGrant("mon getmap"));
245 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mgr keys
c07f9fc5
FG
246 profile_grants.back().command_args["entity"] = StringConstraint(
247 StringConstraint::MATCH_TYPE_PREFIX, "mgr.");
248 profile_grants.back().command_args["caps_mon"] = StringConstraint(
249 StringConstraint::MATCH_TYPE_EQUAL, "allow profile mgr");
7c673cae
FG
250 }
251 if (profile == "bootstrap-rgw") {
252 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
253 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
254 profile_grants.push_back(MonCapGrant("mon getmap"));
255 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
c07f9fc5
FG
256 profile_grants.back().command_args["entity"] = StringConstraint(
257 StringConstraint::MATCH_TYPE_PREFIX, "client.rgw.");
258 profile_grants.back().command_args["caps_mon"] = StringConstraint(
259 StringConstraint::MATCH_TYPE_EQUAL, "allow rw");
260 profile_grants.back().command_args["caps_osd"] = StringConstraint(
261 StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
7c673cae 262 }
d2e6a577
FG
263 if (profile == "bootstrap-rbd") {
264 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
265 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
266 profile_grants.back().command_args["entity"] = StringConstraint(
267 StringConstraint::MATCH_TYPE_PREFIX, "client.");
268 profile_grants.back().command_args["caps_mon"] = StringConstraint(
269 StringConstraint::MATCH_TYPE_EQUAL, "profile rbd");
270 profile_grants.back().command_args["caps_osd"] = StringConstraint(
271 StringConstraint::MATCH_TYPE_REGEX,
272 "^([ ,]*profile(=|[ ]+)['\"]?rbd[^ ,'\"]*['\"]?([ ]+pool(=|[ ]+)['\"]?[^,'\"]+['\"]?)?)+$");
273 }
7c673cae
FG
274 if (profile == "fs-client") {
275 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
276 profile_grants.push_back(MonCapGrant("mds", MON_CAP_R));
277 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
278 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
279 }
280 if (profile == "simple-rados-client") {
281 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
282 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
283 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
284 }
c07f9fc5
FG
285 if (profile == "rbd") {
286 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
287 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
288 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
289
290 // exclusive lock dead-client blacklisting (IP+nonce required)
291 profile_grants.push_back(MonCapGrant("osd blacklist"));
292 profile_grants.back().command_args["blacklistop"] = StringConstraint(
293 StringConstraint::MATCH_TYPE_EQUAL, "add");
294 profile_grants.back().command_args["addr"] = StringConstraint(
d2e6a577 295 StringConstraint::MATCH_TYPE_REGEX, "^[^/]+/[0-9]+$");
c07f9fc5 296 }
7c673cae
FG
297
298 if (profile == "role-definer") {
299 // grants ALL caps to the auth subsystem, read-only on the
300 // monitor subsystem and nothing else.
301 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
302 profile_grants.push_back(MonCapGrant("auth", MON_CAP_ALL));
303 }
304}
305
306mon_rwxa_t MonCapGrant::get_allowed(CephContext *cct,
307 int daemon_type,
308 EntityName name,
309 const std::string& s, const std::string& c,
310 const map<string,string>& c_args) const
311{
312 if (profile.length()) {
313 expand_profile(daemon_type, name);
314 mon_rwxa_t a;
315 for (list<MonCapGrant>::const_iterator p = profile_grants.begin();
316 p != profile_grants.end(); ++p)
317 a = a | p->get_allowed(cct, daemon_type, name, s, c, c_args);
318 return a;
319 }
320 if (service.length()) {
321 if (service != s)
322 return 0;
323 return allow;
324 }
325 if (command.length()) {
326 if (command != c)
327 return 0;
328 for (map<string,StringConstraint>::const_iterator p = command_args.begin(); p != command_args.end(); ++p) {
329 map<string,string>::const_iterator q = c_args.find(p->first);
330 // argument must be present if a constraint exists
331 if (q == c_args.end())
332 return 0;
c07f9fc5
FG
333 switch (p->second.match_type) {
334 case StringConstraint::MATCH_TYPE_EQUAL:
7c673cae
FG
335 if (p->second.value != q->second)
336 return 0;
c07f9fc5
FG
337 break;
338 case StringConstraint::MATCH_TYPE_PREFIX:
339 if (q->second.find(p->second.value) != 0)
7c673cae 340 return 0;
c07f9fc5
FG
341 break;
342 case StringConstraint::MATCH_TYPE_REGEX:
343 {
d2e6a577
FG
344 boost::regex pattern(
345 p->second.value, boost::regex::extended | boost::regex::no_except);
c07f9fc5
FG
346 if (pattern.empty() || !boost::regex_match(q->second, pattern))
347 return 0;
348 }
349 break;
350 default:
351 break;
7c673cae
FG
352 }
353 }
354 return MON_CAP_ALL;
355 }
356 return allow;
357}
358
359ostream& operator<<(ostream&out, const MonCap& m)
360{
361 for (vector<MonCapGrant>::const_iterator p = m.grants.begin(); p != m.grants.end(); ++p) {
362 if (p != m.grants.begin())
363 out << ", ";
364 out << *p;
365 }
366 return out;
367}
368
369bool MonCap::is_allow_all() const
370{
371 for (vector<MonCapGrant>::const_iterator p = grants.begin(); p != grants.end(); ++p)
372 if (p->is_allow_all())
373 return true;
374 return false;
375}
376
377void MonCap::set_allow_all()
378{
379 grants.clear();
380 grants.push_back(MonCapGrant(MON_CAP_ANY));
381 text = "allow *";
382}
383
384bool MonCap::is_capable(CephContext *cct,
385 int daemon_type,
386 EntityName name,
387 const string& service,
388 const string& command, const map<string,string>& command_args,
389 bool op_may_read, bool op_may_write, bool op_may_exec) const
390{
391 if (cct)
392 ldout(cct, 20) << "is_capable service=" << service << " command=" << command
393 << (op_may_read ? " read":"")
394 << (op_may_write ? " write":"")
395 << (op_may_exec ? " exec":"")
396 << " on cap " << *this
397 << dendl;
398 mon_rwxa_t allow = 0;
399 for (vector<MonCapGrant>::const_iterator p = grants.begin();
400 p != grants.end(); ++p) {
401 if (cct)
402 ldout(cct, 20) << " allow so far " << allow << ", doing grant " << *p << dendl;
403
404 if (p->is_allow_all()) {
405 if (cct)
406 ldout(cct, 20) << " allow all" << dendl;
407 return true;
408 }
409
410 // check enumerated caps
411 allow = allow | p->get_allowed(cct, daemon_type, name, service, command,
412 command_args);
413 if ((!op_may_read || (allow & MON_CAP_R)) &&
414 (!op_may_write || (allow & MON_CAP_W)) &&
415 (!op_may_exec || (allow & MON_CAP_X))) {
416 if (cct)
417 ldout(cct, 20) << " match" << dendl;
418 return true;
419 }
420 }
421 return false;
422}
423
424void MonCap::encode(bufferlist& bl) const
425{
426 ENCODE_START(4, 4, bl); // legacy MonCaps was 3, 3
427 ::encode(text, bl);
428 ENCODE_FINISH(bl);
429}
430
431void MonCap::decode(bufferlist::iterator& bl)
432{
433 string s;
434 DECODE_START(4, bl);
435 ::decode(s, bl);
436 DECODE_FINISH(bl);
437 parse(s, NULL);
438}
439
440void MonCap::dump(Formatter *f) const
441{
442 f->dump_string("text", text);
443}
444
445void MonCap::generate_test_instances(list<MonCap*>& ls)
446{
447 ls.push_back(new MonCap);
448 ls.push_back(new MonCap);
449 ls.back()->parse("allow *");
450 ls.push_back(new MonCap);
451 ls.back()->parse("allow rwx");
452 ls.push_back(new MonCap);
453 ls.back()->parse("allow service foo x");
454 ls.push_back(new MonCap);
455 ls.back()->parse("allow command bar x");
456 ls.push_back(new MonCap);
457 ls.back()->parse("allow service foo r, allow command bar x");
458 ls.push_back(new MonCap);
459 ls.back()->parse("allow command bar with k1=v1 x");
460 ls.push_back(new MonCap);
461 ls.back()->parse("allow command bar with k1=v1 k2=v2 x");
462}
463
464// grammar
465namespace qi = boost::spirit::qi;
466namespace ascii = boost::spirit::ascii;
467namespace phoenix = boost::phoenix;
468
469
470template <typename Iterator>
471struct MonCapParser : qi::grammar<Iterator, MonCap()>
472{
473 MonCapParser() : MonCapParser::base_type(moncap)
474 {
475 using qi::char_;
476 using qi::int_;
477 using qi::ulong_long;
478 using qi::lexeme;
479 using qi::alnum;
480 using qi::_val;
481 using qi::_1;
482 using qi::_2;
483 using qi::_3;
484 using qi::eps;
485 using qi::lit;
486
487 quoted_string %=
488 lexeme['"' >> +(char_ - '"') >> '"'] |
489 lexeme['\'' >> +(char_ - '\'') >> '\''];
490 unquoted_word %= +char_("a-zA-Z0-9_.-");
491 str %= quoted_string | unquoted_word;
492
493 spaces = +(lit(' ') | lit('\n') | lit('\t'));
494
495 // command := command[=]cmd [k1=v1 k2=v2 ...]
c07f9fc5
FG
496 str_match = '=' >> qi::attr(StringConstraint::MATCH_TYPE_EQUAL) >> str;
497 str_prefix = spaces >> lit("prefix") >> spaces >>
498 qi::attr(StringConstraint::MATCH_TYPE_PREFIX) >> str;
499 str_regex = spaces >> lit("regex") >> spaces >>
500 qi::attr(StringConstraint::MATCH_TYPE_REGEX) >> str;
501 kv_pair = str >> (str_match | str_prefix | str_regex);
7c673cae
FG
502 kv_map %= kv_pair >> *(spaces >> kv_pair);
503 command_match = -spaces >> lit("allow") >> spaces >> lit("command") >> (lit('=') | spaces)
504 >> qi::attr(string()) >> qi::attr(string())
505 >> str
506 >> -(spaces >> lit("with") >> spaces >> kv_map)
507 >> qi::attr(0);
508
509 // service foo rwxa
510 service_match %= -spaces >> lit("allow") >> spaces >> lit("service") >> (lit('=') | spaces)
511 >> str >> qi::attr(string()) >> qi::attr(string())
512 >> qi::attr(map<string,StringConstraint>())
513 >> spaces >> rwxa;
514
515 // profile foo
c07f9fc5
FG
516 profile_match %= -spaces >> -(lit("allow") >> spaces)
517 >> lit("profile") >> (lit('=') | spaces)
7c673cae
FG
518 >> qi::attr(string())
519 >> str
520 >> qi::attr(string())
521 >> qi::attr(map<string,StringConstraint>())
522 >> qi::attr(0);
523
524 // rwxa
525 rwxa_match %= -spaces >> lit("allow") >> spaces
526 >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(string())
527 >> qi::attr(map<string,StringConstraint>())
528 >> rwxa;
529
530 // rwxa := * | [r][w][x]
531 rwxa =
532 (lit("*")[_val = MON_CAP_ANY]) |
533 ( eps[_val = 0] >>
534 ( lit('r')[_val |= MON_CAP_R] ||
535 lit('w')[_val |= MON_CAP_W] ||
536 lit('x')[_val |= MON_CAP_X]
537 )
538 );
539
540 // grant := allow ...
541 grant = -spaces >> (rwxa_match | profile_match | service_match | command_match) >> -spaces;
542
543 // moncap := grant [grant ...]
544 grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
545 moncap = grants [_val = phoenix::construct<MonCap>(_1)];
546
547 }
548 qi::rule<Iterator> spaces;
549 qi::rule<Iterator, unsigned()> rwxa;
550 qi::rule<Iterator, string()> quoted_string;
551 qi::rule<Iterator, string()> unquoted_word;
552 qi::rule<Iterator, string()> str;
553
c07f9fc5 554 qi::rule<Iterator, StringConstraint()> str_match, str_prefix, str_regex;
7c673cae
FG
555 qi::rule<Iterator, pair<string, StringConstraint>()> kv_pair;
556 qi::rule<Iterator, map<string, StringConstraint>()> kv_map;
557
558 qi::rule<Iterator, MonCapGrant()> rwxa_match;
559 qi::rule<Iterator, MonCapGrant()> command_match;
560 qi::rule<Iterator, MonCapGrant()> service_match;
561 qi::rule<Iterator, MonCapGrant()> profile_match;
562 qi::rule<Iterator, MonCapGrant()> grant;
563 qi::rule<Iterator, std::vector<MonCapGrant>()> grants;
564 qi::rule<Iterator, MonCap()> moncap;
565};
566
567bool MonCap::parse(const string& str, ostream *err)
568{
569 string s = str;
570 string::iterator iter = s.begin();
571 string::iterator end = s.end();
572
573 MonCapParser<string::iterator> g;
574 bool r = qi::parse(iter, end, g, *this);
575 //MonCapGrant foo;
576 //bool r = qi::phrase_parse(iter, end, g, ascii::space, foo);
577 if (r && iter == end) {
578 text = str;
579 return true;
580 }
581
582 // Make sure no grants are kept after parsing failed!
583 grants.clear();
584
585 if (err) {
586 if (iter != end)
587 *err << "moncap parse failed, stopped at '" << std::string(iter, end)
588 << "' of '" << str << "'\n";
589 else
590 *err << "moncap parse failed, stopped at end of '" << str << "'\n";
591 }
592
593 return false;
594}
595