]>
git.proxmox.com Git - ceph.git/blob - ceph/src/mon/MonCap.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2013 Inktank
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.
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 #include <boost/algorithm/string/predicate.hpp>
25 #include "include/stringify.h"
26 #include "include/ipaddr.h"
27 #include "common/debug.h"
28 #include "common/Formatter.h"
33 #include "include/ceph_assert.h"
42 using ceph::bufferlist
;
43 using ceph::Formatter
;
45 static inline bool is_not_alnum_space(char c
)
47 return !(isalpha(c
) || isdigit(c
) || (c
== '-') || (c
== '_'));
50 static std::string
maybe_quote_string(const std::string
& str
)
52 if (find_if(str
.begin(), str
.end(), is_not_alnum_space
) == str
.end())
54 return string("\"") + str
+ string("\"");
57 #define dout_subsys ceph_subsys_mon
59 ostream
& operator<<(ostream
& out
, const mon_rwxa_t
& p
)
73 ostream
& operator<<(ostream
& out
, const StringConstraint
& c
)
75 switch (c
.match_type
) {
76 case StringConstraint::MATCH_TYPE_EQUAL
:
77 return out
<< "value " << c
.value
;
78 case StringConstraint::MATCH_TYPE_PREFIX
:
79 return out
<< "prefix " << c
.value
;
80 case StringConstraint::MATCH_TYPE_REGEX
:
81 return out
<< "regex " << c
.value
;
88 ostream
& operator<<(ostream
& out
, const MonCapGrant
& m
)
91 if (m
.service
.length()) {
92 out
<< " service " << maybe_quote_string(m
.service
);
94 if (m
.command
.length()) {
95 out
<< " command " << maybe_quote_string(m
.command
);
96 if (!m
.command_args
.empty()) {
98 for (auto p
= m
.command_args
.begin();
99 p
!= m
.command_args
.end();
101 switch (p
->second
.match_type
) {
102 case StringConstraint::MATCH_TYPE_EQUAL
:
103 out
<< " " << maybe_quote_string(p
->first
) << "="
104 << maybe_quote_string(p
->second
.value
);
106 case StringConstraint::MATCH_TYPE_PREFIX
:
107 out
<< " " << maybe_quote_string(p
->first
) << " prefix "
108 << maybe_quote_string(p
->second
.value
);
110 case StringConstraint::MATCH_TYPE_REGEX
:
111 out
<< " " << maybe_quote_string(p
->first
) << " regex "
112 << maybe_quote_string(p
->second
.value
);
120 if (m
.profile
.length()) {
121 out
<< " profile " << maybe_quote_string(m
.profile
);
124 out
<< " " << m
.allow
;
125 if (m
.network
.size())
126 out
<< " network " << m
.network
;
132 // fusion lets us easily populate structs via the qi parser.
134 typedef map
<string
,StringConstraint
> kvmap
;
136 BOOST_FUSION_ADAPT_STRUCT(MonCapGrant
,
137 (std::string
, service
)
138 (std::string
, profile
)
139 (std::string
, command
)
140 (kvmap
, command_args
)
142 (std::string
, network
)
143 (std::string
, fs_name
))
145 BOOST_FUSION_ADAPT_STRUCT(StringConstraint
,
146 (StringConstraint::MatchType
, match_type
)
147 (std::string
, value
))
151 void MonCapGrant::parse_network()
153 network_valid
= ::parse_network(network
.c_str(), &network_parsed
,
157 void MonCapGrant::expand_profile(const EntityName
& name
) const
159 // only generate this list once
160 if (!profile_grants
.empty())
163 if (profile
== "read-only") {
164 // grants READ-ONLY caps monitor-wide
165 // 'auth' requires MON_CAP_X even for RO, which we do not grant here.
166 profile_grants
.push_back(mon_rwxa_t(MON_CAP_R
));
170 if (profile
== "read-write") {
171 // grants READ-WRITE caps monitor-wide
172 // 'auth' requires MON_CAP_X for all operations, which we do not grant.
173 profile_grants
.push_back(mon_rwxa_t(MON_CAP_R
| MON_CAP_W
));
177 if (profile
== "mon") {
178 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_ALL
));
179 profile_grants
.push_back(MonCapGrant("log", MON_CAP_ALL
));
181 if (profile
== "osd") {
182 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_ALL
));
183 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
184 profile_grants
.push_back(MonCapGrant("pg", MON_CAP_R
| MON_CAP_W
));
185 profile_grants
.push_back(MonCapGrant("log", MON_CAP_W
));
186 StringConstraint
constraint(StringConstraint::MATCH_TYPE_REGEX
,
187 string("osd_mclock_max_capacity_iops_(hdd|ssd)"));
188 profile_grants
.push_back(MonCapGrant("config set", "name", constraint
));
190 if (profile
== "mds") {
191 profile_grants
.push_back(MonCapGrant("mds", MON_CAP_ALL
));
192 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
193 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
));
194 // This command grant is checked explicitly in MRemoveSnaps handling
195 profile_grants
.push_back(MonCapGrant("osd pool rmsnap"));
196 profile_grants
.push_back(MonCapGrant("osd blocklist"));
197 profile_grants
.push_back(MonCapGrant("osd blacklist")); // for compat
198 profile_grants
.push_back(MonCapGrant("log", MON_CAP_W
));
200 if (profile
== "mgr") {
201 profile_grants
.push_back(MonCapGrant("mgr", MON_CAP_ALL
));
202 profile_grants
.push_back(MonCapGrant("log", MON_CAP_R
| MON_CAP_W
));
203 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
| MON_CAP_W
));
204 profile_grants
.push_back(MonCapGrant("mds", MON_CAP_R
| MON_CAP_W
));
205 profile_grants
.push_back(MonCapGrant("fs", MON_CAP_R
| MON_CAP_W
));
206 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
| MON_CAP_W
));
207 profile_grants
.push_back(MonCapGrant("auth", MON_CAP_R
| MON_CAP_W
| MON_CAP_X
));
208 profile_grants
.push_back(MonCapGrant("config-key", MON_CAP_R
| MON_CAP_W
));
209 profile_grants
.push_back(MonCapGrant("config", MON_CAP_R
| MON_CAP_W
));
210 // cephadm orchestrator provisions new daemon keys and updates caps
211 profile_grants
.push_back(MonCapGrant("auth get-or-create"));
212 profile_grants
.push_back(MonCapGrant("auth caps"));
213 profile_grants
.push_back(MonCapGrant("auth rm"));
214 // tell commands (this is a bit of a kludge)
215 profile_grants
.push_back(MonCapGrant("smart"));
216 // allow the Telemetry module to gather heap and mempool metrics
217 profile_grants
.push_back(MonCapGrant("heap"));
218 profile_grants
.push_back(MonCapGrant("dump_mempools"));
220 if (profile
== "osd" || profile
== "mds" || profile
== "mon" ||
222 StringConstraint
constraint(StringConstraint::MATCH_TYPE_PREFIX
,
223 string("daemon-private/") + stringify(name
) +
225 std::string prefix
= string("daemon-private/") + stringify(name
) + string("/");
226 profile_grants
.push_back(MonCapGrant("config-key get", "key", constraint
));
227 profile_grants
.push_back(MonCapGrant("config-key put", "key", constraint
));
228 profile_grants
.push_back(MonCapGrant("config-key set", "key", constraint
));
229 profile_grants
.push_back(MonCapGrant("config-key exists", "key", constraint
));
230 profile_grants
.push_back(MonCapGrant("config-key delete", "key", constraint
));
232 if (profile
== "bootstrap-osd") {
233 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
)); // read monmap
234 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
)); // read osdmap
235 profile_grants
.push_back(MonCapGrant("mon getmap"));
236 profile_grants
.push_back(MonCapGrant("osd new"));
237 profile_grants
.push_back(MonCapGrant("osd purge-new"));
239 if (profile
== "bootstrap-mds") {
240 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
)); // read monmap
241 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
)); // read osdmap
242 profile_grants
.push_back(MonCapGrant("mon getmap"));
243 profile_grants
.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
244 profile_grants
.back().command_args
["entity"] = StringConstraint(
245 StringConstraint::MATCH_TYPE_PREFIX
, "mds.");
246 profile_grants
.back().command_args
["caps_mon"] = StringConstraint(
247 StringConstraint::MATCH_TYPE_EQUAL
, "allow profile mds");
248 profile_grants
.back().command_args
["caps_osd"] = StringConstraint(
249 StringConstraint::MATCH_TYPE_EQUAL
, "allow rwx");
250 profile_grants
.back().command_args
["caps_mds"] = StringConstraint(
251 StringConstraint::MATCH_TYPE_EQUAL
, "allow");
253 if (profile
== "bootstrap-mgr") {
254 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
)); // read monmap
255 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
)); // read osdmap
256 profile_grants
.push_back(MonCapGrant("mon getmap"));
257 profile_grants
.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mgr keys
258 profile_grants
.back().command_args
["entity"] = StringConstraint(
259 StringConstraint::MATCH_TYPE_PREFIX
, "mgr.");
260 profile_grants
.back().command_args
["caps_mon"] = StringConstraint(
261 StringConstraint::MATCH_TYPE_EQUAL
, "allow profile mgr");
263 if (profile
== "bootstrap-rgw") {
264 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
)); // read monmap
265 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
)); // read osdmap
266 profile_grants
.push_back(MonCapGrant("mon getmap"));
267 profile_grants
.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
268 profile_grants
.back().command_args
["entity"] = StringConstraint(
269 StringConstraint::MATCH_TYPE_PREFIX
, "client.rgw.");
270 profile_grants
.back().command_args
["caps_mon"] = StringConstraint(
271 StringConstraint::MATCH_TYPE_EQUAL
, "allow rw");
272 profile_grants
.back().command_args
["caps_osd"] = StringConstraint(
273 StringConstraint::MATCH_TYPE_EQUAL
, "allow rwx");
275 if (profile
== "bootstrap-rbd" || profile
== "bootstrap-rbd-mirror") {
276 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
)); // read monmap
277 profile_grants
.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other rbd keys
278 profile_grants
.back().command_args
["entity"] = StringConstraint(
279 StringConstraint::MATCH_TYPE_PREFIX
, "client.");
280 profile_grants
.back().command_args
["caps_mon"] = StringConstraint(
281 StringConstraint::MATCH_TYPE_EQUAL
,
282 (profile
== "bootstrap-rbd-mirror" ? "profile rbd-mirror" :
284 profile_grants
.back().command_args
["caps_osd"] = StringConstraint(
285 StringConstraint::MATCH_TYPE_REGEX
,
286 "^([ ,]*profile(=|[ ]+)['\"]?rbd[^ ,'\"]*['\"]?([ ]+pool(=|[ ]+)['\"]?[^,'\"]+['\"]?)?)+$");
288 if (profile
== "fs-client") {
289 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
290 profile_grants
.push_back(MonCapGrant("mds", MON_CAP_R
));
291 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
));
292 profile_grants
.push_back(MonCapGrant("pg", MON_CAP_R
));
294 if (profile
== "simple-rados-client") {
295 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
296 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
));
297 profile_grants
.push_back(MonCapGrant("pg", MON_CAP_R
));
299 if (profile
== "simple-rados-client-with-blocklist") {
300 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
301 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
));
302 profile_grants
.push_back(MonCapGrant("pg", MON_CAP_R
));
303 profile_grants
.push_back(MonCapGrant("osd blocklist"));
304 profile_grants
.back().command_args
["blocklistop"] = StringConstraint(
305 StringConstraint::MATCH_TYPE_EQUAL
, "add");
306 profile_grants
.back().command_args
["addr"] = StringConstraint(
307 StringConstraint::MATCH_TYPE_REGEX
, "^[^/]+/[0-9]+$");
310 if (boost::starts_with(profile
, "rbd")) {
311 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
312 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
));
313 profile_grants
.push_back(MonCapGrant("pg", MON_CAP_R
));
315 // exclusive lock dead-client blocklisting (IP+nonce required)
316 profile_grants
.push_back(MonCapGrant("osd blocklist"));
317 profile_grants
.back().command_args
["blocklistop"] = StringConstraint(
318 StringConstraint::MATCH_TYPE_EQUAL
, "add");
319 profile_grants
.back().command_args
["addr"] = StringConstraint(
320 StringConstraint::MATCH_TYPE_REGEX
, "^[^/]+/[0-9]+$");
323 profile_grants
.push_back(MonCapGrant("osd blacklist"));
324 profile_grants
.back().command_args
["blacklistop"] = StringConstraint(
325 StringConstraint::MATCH_TYPE_EQUAL
, "add");
326 profile_grants
.back().command_args
["addr"] = StringConstraint(
327 StringConstraint::MATCH_TYPE_REGEX
, "^[^/]+/[0-9]+$");
330 if (profile
== "rbd-mirror") {
331 StringConstraint
constraint(StringConstraint::MATCH_TYPE_PREFIX
,
333 profile_grants
.push_back(MonCapGrant("config-key get", "key", constraint
));
334 } else if (profile
== "rbd-mirror-peer") {
335 StringConstraint
constraint(StringConstraint::MATCH_TYPE_REGEX
,
337 profile_grants
.push_back(MonCapGrant("config-key get", "key", constraint
));
339 constraint
= StringConstraint(StringConstraint::MATCH_TYPE_PREFIX
,
341 profile_grants
.push_back(MonCapGrant("config-key set", "key", constraint
));
343 else if (profile
== "crash") {
344 // TODO: we could limit this to getting the monmap and mgrmap...
345 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
347 if (profile
== "cephfs-mirror") {
348 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
349 profile_grants
.push_back(MonCapGrant("mds", MON_CAP_R
));
350 profile_grants
.push_back(MonCapGrant("osd", MON_CAP_R
));
351 profile_grants
.push_back(MonCapGrant("pg", MON_CAP_R
));
352 StringConstraint
constraint(StringConstraint::MATCH_TYPE_PREFIX
,
353 "cephfs/mirror/peer/");
354 profile_grants
.push_back(MonCapGrant("config-key get", "key", constraint
));
357 if (profile
== "role-definer") {
358 // grants ALL caps to the auth subsystem, read-only on the
359 // monitor subsystem and nothing else.
360 profile_grants
.push_back(MonCapGrant("mon", MON_CAP_R
));
361 profile_grants
.push_back(MonCapGrant("auth", MON_CAP_ALL
));
365 mon_rwxa_t
MonCapGrant::get_allowed(CephContext
*cct
,
367 const std::string
& s
, const std::string
& c
,
368 const map
<string
,string
>& c_args
) const
370 if (profile
.length()) {
371 expand_profile(name
);
373 for (auto p
= profile_grants
.begin();
374 p
!= profile_grants
.end(); ++p
)
375 a
= a
| p
->get_allowed(cct
, name
, s
, c
, c_args
);
378 if (service
.length()) {
383 if (command
.length()) {
386 for (map
<string
,StringConstraint
>::const_iterator p
= command_args
.begin(); p
!= command_args
.end(); ++p
) {
387 map
<string
,string
>::const_iterator q
= c_args
.find(p
->first
);
388 // argument must be present if a constraint exists
389 if (q
== c_args
.end())
391 switch (p
->second
.match_type
) {
392 case StringConstraint::MATCH_TYPE_EQUAL
:
393 if (p
->second
.value
!= q
->second
)
396 case StringConstraint::MATCH_TYPE_PREFIX
:
397 if (q
->second
.find(p
->second
.value
) != 0)
400 case StringConstraint::MATCH_TYPE_REGEX
:
403 p
->second
.value
, std::regex::extended
);
404 if (!std::regex_match(q
->second
, pattern
))
406 } catch(const std::regex_error
&) {
416 // we don't allow config-key service to be accessed with blanket caps other
417 // than '*' (i.e., 'any'), and that should have been checked by the caller
418 // via 'is_allow_all()'.
419 if (s
== "config-key") {
425 ostream
& operator<<(ostream
&out
, const MonCap
& m
)
427 for (vector
<MonCapGrant
>::const_iterator p
= m
.grants
.begin(); p
!= m
.grants
.end(); ++p
) {
428 if (p
!= m
.grants
.begin())
435 bool MonCap::is_allow_all() const
437 for (vector
<MonCapGrant
>::const_iterator p
= grants
.begin(); p
!= grants
.end(); ++p
)
438 if (p
->is_allow_all())
443 void MonCap::set_allow_all()
446 grants
.push_back(MonCapGrant(MON_CAP_ANY
));
450 bool MonCap::is_capable(
453 const string
& service
,
454 const string
& command
, const map
<string
,string
>& command_args
,
455 bool op_may_read
, bool op_may_write
, bool op_may_exec
,
456 const entity_addr_t
& addr
) const
459 ldout(cct
, 20) << "is_capable service=" << service
<< " command=" << command
460 << (op_may_read
? " read":"")
461 << (op_may_write
? " write":"")
462 << (op_may_exec
? " exec":"")
464 << " on cap " << *this
467 mon_rwxa_t allow
= 0;
468 for (vector
<MonCapGrant
>::const_iterator p
= grants
.begin();
469 p
!= grants
.end(); ++p
) {
471 ldout(cct
, 20) << " allow so far " << allow
<< ", doing grant " << *p
474 if (p
->network
.size() &&
475 (!p
->network_valid
||
476 !network_contains(p
->network_parsed
,
482 if (p
->is_allow_all()) {
484 ldout(cct
, 20) << " allow all" << dendl
;
488 // check enumerated caps
489 allow
= allow
| p
->get_allowed(cct
, name
, service
, command
, command_args
);
490 if ((!op_may_read
|| (allow
& MON_CAP_R
)) &&
491 (!op_may_write
|| (allow
& MON_CAP_W
)) &&
492 (!op_may_exec
|| (allow
& MON_CAP_X
))) {
494 ldout(cct
, 20) << " match" << dendl
;
501 void MonCap::encode(bufferlist
& bl
) const
503 ENCODE_START(4, 4, bl
); // legacy MonCaps was 3, 3
508 void MonCap::decode(bufferlist::const_iterator
& bl
)
517 void MonCap::dump(Formatter
*f
) const
519 f
->dump_string("text", text
);
522 void MonCap::generate_test_instances(list
<MonCap
*>& ls
)
524 ls
.push_back(new MonCap
);
525 ls
.push_back(new MonCap
);
526 ls
.back()->parse("allow *");
527 ls
.push_back(new MonCap
);
528 ls
.back()->parse("allow rwx");
529 ls
.push_back(new MonCap
);
530 ls
.back()->parse("allow service foo x");
531 ls
.push_back(new MonCap
);
532 ls
.back()->parse("allow command bar x");
533 ls
.push_back(new MonCap
);
534 ls
.back()->parse("allow service foo r, allow command bar x");
535 ls
.push_back(new MonCap
);
536 ls
.back()->parse("allow command bar with k1=v1 x");
537 ls
.push_back(new MonCap
);
538 ls
.back()->parse("allow command bar with k1=v1 k2=v2 x");
542 namespace qi
= boost::spirit::qi
;
543 namespace ascii
= boost::spirit::ascii
;
544 namespace phoenix
= boost::phoenix
;
547 template <typename Iterator
>
548 struct MonCapParser
: qi::grammar
<Iterator
, MonCap()>
550 MonCapParser() : MonCapParser::base_type(moncap
)
554 using qi::ulong_long
;
565 lexeme
['"' >> +(char_
- '"') >> '"'] |
566 lexeme
['\'' >> +(char_
- '\'') >> '\''];
567 unquoted_word
%= +char_("a-zA-Z0-9_./-");
568 str
%= quoted_string
| unquoted_word
;
569 network_str
%= +char_("/.:a-fA-F0-9][");
570 fs_name_str
%= +char_("a-zA-Z0-9_.-");
572 spaces
= +(lit(' ') | lit('\n') | lit('\t'));
574 // command := command[=]cmd [k1=v1 k2=v2 ...]
575 str_match
= '=' >> qi::attr(StringConstraint::MATCH_TYPE_EQUAL
) >> str
;
576 str_prefix
= spaces
>> lit("prefix") >> spaces
>>
577 qi::attr(StringConstraint::MATCH_TYPE_PREFIX
) >> str
;
578 str_regex
= spaces
>> lit("regex") >> spaces
>>
579 qi::attr(StringConstraint::MATCH_TYPE_REGEX
) >> str
;
580 kv_pair
= str
>> (str_match
| str_prefix
| str_regex
);
581 kv_map
%= kv_pair
>> *(spaces
>> kv_pair
);
582 command_match
= -spaces
>> lit("allow") >> spaces
>> lit("command") >> (lit('=') | spaces
)
583 >> qi::attr(string()) >> qi::attr(string())
585 >> -(spaces
>> lit("with") >> spaces
>> kv_map
)
587 >> -(spaces
>> lit("network") >> spaces
>> network_str
);
590 service_match
%= -spaces
>> lit("allow") >> spaces
>> lit("service") >> (lit('=') | spaces
)
591 >> str
>> qi::attr(string()) >> qi::attr(string())
592 >> qi::attr(map
<string
,StringConstraint
>())
594 >> -(spaces
>> lit("network") >> spaces
>> network_str
);
597 profile_match
%= -spaces
>> -(lit("allow") >> spaces
)
598 >> lit("profile") >> (lit('=') | spaces
)
599 >> qi::attr(string())
601 >> qi::attr(string())
602 >> qi::attr(map
<string
,StringConstraint
>())
604 >> -(spaces
>> lit("network") >> spaces
>> network_str
);
607 rwxa_match
%= -spaces
>> lit("allow") >> spaces
608 >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(string())
609 >> qi::attr(map
<string
,StringConstraint
>())
611 >> -(spaces
>> lit("network") >> spaces
>> network_str
)
612 >> -(spaces
>> lit("fsname") >> (lit('=') | spaces
) >> fs_name_str
);
614 // rwxa := * | [r][w][x]
616 (lit("*")[_val
= MON_CAP_ANY
]) |
617 (lit("all")[_val
= MON_CAP_ANY
]) |
619 ( lit('r')[_val
|= MON_CAP_R
] ||
620 lit('w')[_val
|= MON_CAP_W
] ||
621 lit('x')[_val
|= MON_CAP_X
]
625 // grant := allow ...
626 grant
= -spaces
>> (rwxa_match
| profile_match
| service_match
| command_match
) >> -spaces
;
628 // moncap := grant [grant ...]
629 grants
%= (grant
% (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
630 moncap
= grants
[_val
= phoenix::construct
<MonCap
>(_1
)];
633 qi::rule
<Iterator
> spaces
;
634 qi::rule
<Iterator
, unsigned()> rwxa
;
635 qi::rule
<Iterator
, string()> quoted_string
;
636 qi::rule
<Iterator
, string()> unquoted_word
;
637 qi::rule
<Iterator
, string()> str
, network_str
;
638 qi::rule
<Iterator
, string()> fs_name_str
;
640 qi::rule
<Iterator
, StringConstraint()> str_match
, str_prefix
, str_regex
;
641 qi::rule
<Iterator
, pair
<string
, StringConstraint
>()> kv_pair
;
642 qi::rule
<Iterator
, map
<string
, StringConstraint
>()> kv_map
;
644 qi::rule
<Iterator
, MonCapGrant()> rwxa_match
;
645 qi::rule
<Iterator
, MonCapGrant()> command_match
;
646 qi::rule
<Iterator
, MonCapGrant()> service_match
;
647 qi::rule
<Iterator
, MonCapGrant()> profile_match
;
648 qi::rule
<Iterator
, MonCapGrant()> grant
;
649 qi::rule
<Iterator
, std::vector
<MonCapGrant
>()> grants
;
650 qi::rule
<Iterator
, MonCap()> moncap
;
653 bool MonCap::parse(const string
& str
, ostream
*err
)
655 auto iter
= str
.begin();
656 auto end
= str
.end();
658 MonCapParser
<string::const_iterator
> exp
;
659 bool r
= qi::parse(iter
, end
, exp
, *this);
660 if (r
&& iter
== end
) {
662 for (auto& g
: grants
) {
668 // Make sure no grants are kept after parsing failed!
673 *err
<< "mon capability parse failed, stopped at '"
674 << std::string(iter
, end
)
675 << "' of '" << str
<< "'";
677 *err
<< "mon capability parse failed, stopped at end of '" << str
<< "'";