1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00

report: select: add support for processing string lists in selection

Selection list items are enclosed in '[' and ']' (if there's only
one item, the '[' and ']' can be omitted). Each element of the list
is a string (either quoted or unquoted, like the usual string operand
used in selection) and each element is delimited either by conjunction
(meaining "match all") or disjunction operator (meaning "match any").

For example, if "," is the conjuction operator and "/" is the
disjunction operator then:

  lv_tags=[a,b,c]

...will match all fields where tags contain *all* a, b and c.

  lv_tags=[a/b/c]

...will match all fields where tags contain *any* of a, b, or c.

Mixing operators within the list is not supported:

  lv_tags=[a,b/c]

...will give an error.

The order in which items are defined in the selection do not matter.

This patch enhances the selection parsing functionality to recognize
such lists.
This commit is contained in:
Peter Rajnoha 2014-05-29 09:41:50 +02:00
parent a6694cfc29
commit 8faa4ded9c

View File

@ -123,6 +123,10 @@ static struct op_def _op_cmp[] = {
#define SEL_PRECEDENCE_PS 0x00001000
#define SEL_PRECEDENCE_PE 0x00002000
#define SEL_LIST_MASK 0x000F0000
#define SEL_LIST_LS 0x00010000
#define SEL_LIST_LE 0x00020000
static struct op_def _op_log[] = {
{ "&&", SEL_AND, "All fields must match" },
{ ",", SEL_AND, "All fields must match" },
@ -131,9 +135,16 @@ static struct op_def _op_log[] = {
{ "!", SEL_MODIFIER_NOT, "Logical negation" },
{ "(", SEL_PRECEDENCE_PS, "Left parenthesis" },
{ ")", SEL_PRECEDENCE_PE, "Right parenthesis" },
{ "[", SEL_LIST_LS, "List start" },
{ "]", SEL_LIST_LE, "List end"},
{ NULL, 0, NULL},
};
struct selection_str_list {
unsigned type; /* either SEL_AND or SEL_OR */
struct dm_list *list;
};
struct field_selection {
struct field_properties *fp;
uint32_t flags;
@ -142,6 +153,7 @@ struct field_selection {
uint64_t i;
double d;
struct dm_regex *r;
struct selection_str_list *l;
} v;
};
@ -1349,6 +1361,171 @@ static const char *_tok_value_regex(const struct dm_report_field_type *ft,
return s;
}
static int _str_list_item_cmp(const void *a, const void *b)
{
const struct dm_str_list **item_a = (const struct dm_str_list **) a;
const struct dm_str_list **item_b = (const struct dm_str_list **) b;
return strcmp((*item_a)->str, (*item_b)->str);
}
static int _add_item_to_string_list(struct dm_pool *mem, const char *begin,
const char *end, struct dm_list *list)
{
struct dm_str_list *item;
if (begin == end)
return_0;
if (!(item = dm_pool_zalloc(mem, sizeof(*item))) ||
!(item->str = dm_pool_strndup(mem, begin, end - begin))) {
log_error("_add_item_to_string_list: memory allocation failed for string list item");
return 0;
}
dm_list_add(list, &item->list);
return 1;
}
static char _get_and_skip_quote_char(char const **s)
{
char c = 0;
if (**s == '"' || **s == '\'') {
c = **s;
(*s)++;
}
return c;
}
/*
* Input:
* ft - field type for which the value is parsed
* mem - memory pool to allocate from
* s - a pointer to the parsed string
* Output:
* begin - a pointer to the beginning of the token (whole list)
* end - a pointer to the end of the token + 1 (whole list)
* sel_str_list - the list of strings parsed
*/
static const char *_tok_value_string_list(const struct dm_report_field_type *ft,
struct dm_pool *mem, const char *s,
const char **begin, const char **end,
struct selection_str_list **sel_str_list)
{
static const char _str_list_item_parsing_failed[] = "Failed to parse string list value "
"for selection field %s.";
struct selection_str_list *ssl = NULL;
struct dm_str_list *item;
const char *begin_item, *end_item, *tmp;
uint32_t end_op_flags, end_op_flag_hit;
struct dm_str_list **arr;
size_t list_size;
unsigned int i;
int list_end;
char c;
if (!(ssl = dm_pool_alloc(mem, sizeof(*ssl))) ||
!(ssl->list = dm_pool_alloc(mem, sizeof(*ssl->list)))) {
log_error("_tok_value_string_list: memory allocation failed for selection list");
goto bad;
}
dm_list_init(ssl->list);
ssl->type = 0;
*begin = s;
if (!_tok_op_log(s, &tmp, SEL_LIST_LS)) {
/* Only one item - SEL_LIST_LS and SEL_LIST_LE not used */
c = _get_and_skip_quote_char(&s);
if (!(s = _tok_value_string(s, &begin_item, &end_item, c, SEL_AND | SEL_OR | SEL_PRECEDENCE_PE, NULL))) {
log_error(_str_list_item_parsing_failed, ft->id);
goto bad;
}
if (!_add_item_to_string_list(mem, begin_item, end_item, ssl->list))
goto_bad;
ssl->type = SEL_OR;
goto out;
}
/* More than one item - items enclosed in SEL_LIST_LS and SEL_LIST_LE.
* Each element is terminated by AND or OR operator or 'list end'.
* The first operator hit is then the one allowed for the whole list,
* no mixing allowed!
*/
end_op_flags = SEL_LIST_LE | SEL_AND | SEL_OR;
s++;
while (*s) {
s = _skip_space(s);
c = _get_and_skip_quote_char(&s);
if (!(s = _tok_value_string(s, &begin_item, &end_item, c, end_op_flags, NULL))) {
log_error(_str_list_item_parsing_failed, ft->id);
goto bad;
}
s = _skip_space(s);
if (!(end_op_flag_hit = _tok_op_log(s, &tmp, end_op_flags))) {
log_error("Invalid operator in selection list.");
goto bad;
}
list_end = end_op_flag_hit == SEL_LIST_LE;
if (ssl->type) {
if (!list_end && !(ssl->type & end_op_flag_hit)) {
log_error("Only one type of logical operator allowed "
"in selection list at a time.");
goto bad;
}
} else
ssl->type = list_end ? SEL_OR : end_op_flag_hit;
if (!_add_item_to_string_list(mem, begin_item, end_item, ssl->list))
goto_bad;
s = tmp;
if (list_end)
break;
}
if (end_op_flag_hit != SEL_LIST_LE) {
log_error("Missing list end for selection field %s", ft->id);
goto bad;
}
/* Sort the list. */
if (!(list_size = dm_list_size(ssl->list))) {
log_error(INTERNAL_ERROR "_tok_value_string_list: list has no items");
goto bad;
} else if (list_size == 1)
goto out;
if (!(arr = dm_malloc(sizeof(item) * list_size))) {
log_error("_tok_value_string_list: memory allocation failed for sort array");
goto bad;
}
i = 0;
dm_list_iterate_items(item, ssl->list)
arr[i++] = item;
qsort(arr, list_size, sizeof(item), _str_list_item_cmp);
dm_list_init(ssl->list);
for (i = 0; i < list_size; i++)
dm_list_add(ssl->list, &arr[i]->list);
dm_free(arr);
out:
*end = s;
*sel_str_list = ssl;
return s;
bad:
*end = s;
if (ssl)
dm_pool_free(mem, ssl);
*sel_str_list = NULL;
return s;
}
/*
* Input:
* ft - field type for which the value is parsed
@ -1367,19 +1544,17 @@ static const char *_tok_value(const struct dm_report_field_type *ft,
struct dm_pool *mem, void *custom)
{
int expected_type = ft->flags & DM_REPORT_FIELD_TYPE_MASK;
struct selection_str_list **str_list;
uint64_t *factor;
const char *tmp;
char c = 0;
char c;
s = _skip_space(s);
switch (expected_type) {
case DM_REPORT_FIELD_TYPE_STRING:
if (*s == '"' || *s == '\'') {
c = *s;
s++;
}
c = _get_and_skip_quote_char(&s);
if (!(s = _tok_value_string(s, begin, end, c, SEL_AND | SEL_OR | SEL_PRECEDENCE_PE, NULL))) {
log_error("Failed to parse string value "
"for selection field %s.", ft->id);
@ -1388,6 +1563,17 @@ static const char *_tok_value(const struct dm_report_field_type *ft,
*flags |= DM_REPORT_FIELD_TYPE_STRING;
break;
case DM_REPORT_FIELD_TYPE_STRING_LIST:
str_list = (struct selection_str_list **) custom;
s = _tok_value_string_list(ft, mem, s, begin, end, str_list);
if (!(*str_list)) {
log_error("Failed to parse string list value "
"for selection field %s.", ft->id);
return NULL;
}
*flags |= DM_REPORT_FIELD_TYPE_STRING_LIST;
break;
case DM_REPORT_FIELD_TYPE_NUMBER:
case DM_REPORT_FIELD_TYPE_SIZE:
if (!(s = _tok_value_number(s, begin, end))) {
@ -1511,7 +1697,7 @@ static struct field_selection *_create_field_selection(struct dm_report *rh,
goto error;
}
} else {
/* STRING, NUMBER or SIZE */
/* STRING, NUMBER, SIZE or STRING_LIST */
if (!(s = dm_pool_alloc(rh->mem, len + 1))) {
log_error("dm_report: dm_pool_alloc failed to store "
"value for selection field %s", field_id);
@ -1543,6 +1729,9 @@ static struct field_selection *_create_field_selection(struct dm_report *rh,
fs->v.d /= 512; /* store size in sectors! */
dm_pool_free(rh->mem, s);
break;
case DM_REPORT_FIELD_TYPE_STRING_LIST:
fs->v.l = *(struct selection_str_list **)custom;
break;
default:
log_error(INTERNAL_ERROR "_create_field_selection: "
"unknown type of selection field %s", field_id);
@ -1605,6 +1794,7 @@ static struct selection_node *_parse_selection(struct dm_report *rh,
const char *last;
uint32_t flags, field_num;
const struct dm_report_field_type *ft;
struct selection_str_list *str_list;
uint64_t factor;
void *custom;
char *tmp;
@ -1654,6 +1844,8 @@ static struct selection_node *_parse_selection(struct dm_report *rh,
if (ft->flags == DM_REPORT_FIELD_TYPE_SIZE ||
ft->flags == DM_REPORT_FIELD_TYPE_NUMBER)
custom = &factor;
else if (ft->flags == DM_REPORT_FIELD_TYPE_STRING_LIST)
custom = &str_list;
else
custom = NULL;
if (!(last = _tok_value(ft, last, &vs, &ve, &flags, rh->mem, custom)))