* [PATCH] libdevmapper: (0/6) Filtering feature in dm_report
@ 2007-04-18 16:41 Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (1/6) Tidy up _field_match() and _key_match() Jun'ichi Nomura
` (6 more replies)
0 siblings, 7 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:41 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
Hi,
This series of patches enables dmsetup to control which types
of devices are listed.
For example, "dmsetup info -c -o name --filter 'parents_count == 0'"
will display top-level dm devices and
"dmsetup info -c --filter 'uuid !~ /^LVM/'" will display
dm devices which are not LVM2 volumes.
As the feature is implemented in libdevmapper, LVM2 is easily
made to use it, too. The patch will be posted to lvm-devel.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (1/6) Tidy up _field_match() and _key_match()
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
@ 2007-04-18 16:47 ` Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (2/6) Fix trailing separator Jun'ichi Nomura
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:47 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 181 bytes --]
Hi,
This patch cleans up _field_match() and _key_match() by separating
common operations out.
No effect on functionality.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: libdm-report-tidy-add_field.patch --]
[-- Type: text/x-patch, Size: 4255 bytes --]
Tidy up _field_match() and _key_match()
* Add _add_field() function for flag setting, alloc, copy, and list_add of
struct field_properties
* Add _is_same_field() for lengthy test to check field name
abbreviation
---
lib/libdm-report.c | 101 +++++++++++++++++++++++++++++------------------------
1 file changed, 56 insertions(+), 45 deletions(-)
Index: device-mapper.work/lib/libdm-report.c
===================================================================
--- device-mapper.work.orig/lib/libdm-report.c
+++ device-mapper.work/lib/libdm-report.c
@@ -296,35 +296,62 @@ static int _copy_field(struct dm_report
return 1;
}
-static int _field_match(struct dm_report *rh, const char *field, size_t flen)
+static struct field_properties * _add_field(struct dm_report *rh,
+ uint32_t field_num, uint32_t flags)
{
- uint32_t f, l;
struct field_properties *fp;
+ rh->report_types |= rh->fields[field_num].type;
+ if (!(fp = dm_pool_zalloc(rh->mem, sizeof(struct field_properties)))) {
+ log_error("dm_report: "
+ "struct field_properties allocation failed");
+ return NULL;
+ }
+ if (!_copy_field(rh, fp, field_num)) {
+ dm_pool_free(rh->mem, fp);
+ return NULL;
+ }
+
+ /* Add additional flags */
+ fp->flags |= flags;
+
+ list_add(&rh->field_props, &fp->list);
+ return fp;
+}
+
+/*
+ * compare name1 and name2 (or prefix+name2)
+ * name2 is not \0 terminated. len is the length of name2.
+ */
+static int _is_same_field(const char *name1, const char *name2,
+ size_t len, const char *prefix)
+{
+ size_t l;
+
+ /* name1 == name2 */
+ if (!strncasecmp(name1, name2, len) && strlen(name1) == len)
+ return 1;
+
+ /* name1 == name2 */
+ l = strlen(prefix);
+ if (!strncasecmp(prefix, name1, l) &&
+ !strncasecmp(name1 + l, name2, len) && strlen(name1) == l + len)
+ return 1;
+
+ return 0;
+}
+
+static int _field_match(struct dm_report *rh, const char *field, size_t flen)
+{
+ uint32_t f;
+
if (!flen)
return 0;
- for (f = 0; rh->fields[f].report_fn; f++) {
- if ((!strncasecmp(rh->fields[f].id, field, flen) &&
- strlen(rh->fields[f].id) == flen) ||
- (l = strlen(rh->field_prefix),
- !strncasecmp(rh->field_prefix, rh->fields[f].id, l) &&
- !strncasecmp(rh->fields[f].id + l, field, flen) &&
- strlen(rh->fields[f].id) == l + flen)) {
- rh->report_types |= rh->fields[f].type;
- if (!(fp = dm_pool_zalloc(rh->mem, sizeof(*fp)))) {
- log_error("dm_report: "
- "struct field_properties allocation "
- "failed");
- return 0;
- }
- if (!_copy_field(rh, fp, f))
- return 0;
-
- list_add(&rh->field_props, &fp->list);
- return 1;
- }
- }
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if (_is_same_field(rh->fields[f].id,
+ field, flen, rh->field_prefix))
+ return (_add_field(rh, f, 0) != NULL);
return 0;
}
@@ -342,19 +369,9 @@ static int _add_sort_key(struct dm_repor
}
if (!found) {
- rh->report_types |= rh->fields[field_num].type;
- if (!(found = dm_pool_zalloc(rh->mem, sizeof(*found)))) {
- log_error("dm_report: "
- "struct field_properties allocation failed");
- return 0;
- }
- if (!_copy_field(rh, found, field_num))
- return 0;
-
/* Add as a non-display field */
- found->flags |= FLD_HIDDEN;
-
- list_add(&rh->field_props, &found->list);
+ if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
+ return 0;
}
if (found->flags & FLD_SORT_KEY) {
@@ -372,7 +389,7 @@ static int _add_sort_key(struct dm_repor
static int _key_match(struct dm_report *rh, const char *key, size_t len)
{
- uint32_t f, l;
+ uint32_t f;
uint32_t flags;
if (!len)
@@ -394,16 +411,10 @@ static int _key_match(struct dm_report *
return 0;
}
- for (f = 0; rh->fields[f].report_fn; f++) {
- if ((!strncasecmp(rh->fields[f].id, key, len) &&
- strlen(rh->fields[f].id) == len) ||
- (l = strlen(rh->field_prefix),
- !strncasecmp(rh->field_prefix, rh->fields[f].id, l) &&
- !strncasecmp(rh->fields[f].id + l, key, len) &&
- strlen(rh->fields[f].id) == l + len)) {
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if (_is_same_field(rh->fields[f].id,
+ key, len, rh->field_prefix))
return _add_sort_key(rh, f, flags);
- }
- }
return 0;
}
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (2/6) Fix trailing separator
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (1/6) Tidy up _field_match() and _key_match() Jun'ichi Nomura
@ 2007-04-18 16:47 ` Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (3/6) Move lib/regex from LVM2 Jun'ichi Nomura
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:47 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 492 bytes --]
Hi,
This patch fixes dm_report_output() so that it can correctly check
the end of the row.
It uses list_end() but trailing fields may have HIDDEN flag and
not be printed.
For example,
# dmsetup info -c -o name -O minor VG0-lv0
Name
VG0-lv0
# dmsetup info -c -o name -O minor --noheadings VG0-lv0
VG0-lv0:
# dmsetup info -c -o name --noheadings VG0-lv0
VG0-lv0
The patch fixes it by inserting hidden fields at the head of the list.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: libdm-report-fix-trailing-separator.patch --]
[-- Type: text/x-patch, Size: 989 bytes --]
Later, dm_report_output() loops through rh->field_props list
and checks the end of the list by list_end() whether to display the
separator or not.
The check doesn't work if hidden field is on the tail.
For example, try 'dmsetup info -c -o name -O minor' and
compare the results with and without '--noheadings'.
---
lib/libdm-report.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
Index: device-mapper.work/lib/libdm-report.c
===================================================================
--- device-mapper.work.orig/lib/libdm-report.c
+++ device-mapper.work/lib/libdm-report.c
@@ -315,7 +315,12 @@ static struct field_properties * _add_fi
/* Add additional flags */
fp->flags |= flags;
- list_add(&rh->field_props, &fp->list);
+ /* Hidden field must come first, otherwise list_end() doesn't work
+ as expected */
+ if (fp->flags & FLD_HIDDEN)
+ list_add_h(&rh->field_props, &fp->list);
+ else
+ list_add(&rh->field_props, &fp->list);
return fp;
}
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (3/6) Move lib/regex from LVM2
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (1/6) Tidy up _field_match() and _key_match() Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (2/6) Fix trailing separator Jun'ichi Nomura
@ 2007-04-18 16:47 ` Jun'ichi Nomura
2007-04-18 19:23 ` [PATCH] LVM2: (1/2) Use dm_regex Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (4/6) Add filtering feature to dm_report Jun'ichi Nomura
` (3 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:47 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 306 bytes --]
Hi,
This patch just moves lib/regex from LVM2 as a preparation for
the filtering feature of dm_report.
Now, the following functions are exported:
- dm_regex_create()
- dm_regex_match()
Corresponding patch to LVM2 will be posted to lvm-devel.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: libdm-regex.patch --]
[-- Type: text/x-patch, Size: 22489 bytes --]
Moved lib/regex from LVM2
Exported functions and a structure are renamed:
- matcher_create() -> dm_regex_create()
- matcher_run() -> dm_regex_match()
- struct matcher -> struct dm_regex
---
lib/.exported_symbols | 2
lib/Makefile.in | 3
lib/libdevmapper.h | 7
lib/regex/matcher.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/regex/parse_rx.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++++
lib/regex/parse_rx.h | 52 +++++++
lib/regex/ttree.c | 117 +++++++++++++++
lib/regex/ttree.h | 26 +++
8 files changed, 933 insertions(+)
Index: device-mapper.work/lib/.exported_symbols
===================================================================
--- device-mapper.work.orig/lib/.exported_symbols
+++ device-mapper.work/lib/.exported_symbols
@@ -127,3 +127,5 @@ dm_report_field_int32
dm_report_field_uint32
dm_report_field_uint64
dm_report_field_set_value
+dm_regex_create
+dm_regex_match
Index: device-mapper.work/lib/libdevmapper.h
===================================================================
--- device-mapper.work.orig/lib/libdevmapper.h
+++ device-mapper.work/lib/libdevmapper.h
@@ -711,4 +711,11 @@ int dm_report_field_uint64(struct dm_rep
void dm_report_field_set_value(struct dm_report_field *field, const void *value,
const void *sortvalue);
+/*
+ * dm_regex
+ */
+struct dm_regex;
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char **patterns,
+ unsigned num);
+int dm_regex_match(struct dm_regex *regex, const char *s);
#endif /* LIB_DEVICE_MAPPER_H */
Index: device-mapper.work/lib/regex/matcher.c
===================================================================
--- /dev/null
+++ device-mapper.work/lib/regex/matcher.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "parse_rx.h"
+#include "ttree.h"
+
+#define assert(x) do { if (!(x)) log_error("assertion failed"); } while(0)
+
+struct dfa_state {
+ int final;
+ struct dfa_state *lookup[256];
+};
+
+struct state_queue {
+ struct dfa_state *s;
+ dm_bitset_t bits;
+ struct state_queue *next;
+};
+
+struct dm_regex { /* Instance variables for the lexer */
+ struct dfa_state *start;
+ unsigned num_nodes;
+ int nodes_entered;
+ struct rx_node **nodes;
+ struct dm_pool *scratch, *mem;
+};
+
+#define TARGET_TRANS '\0'
+
+static int _count_nodes(struct rx_node *rx)
+{
+ int r = 1;
+
+ if (rx->left)
+ r += _count_nodes(rx->left);
+
+ if (rx->right)
+ r += _count_nodes(rx->right);
+
+ return r;
+}
+
+static void _fill_table(struct dm_regex *m, struct rx_node *rx)
+{
+ assert((rx->type != OR) || (rx->left && rx->right));
+
+ if (rx->left)
+ _fill_table(m, rx->left);
+
+ if (rx->right)
+ _fill_table(m, rx->right);
+
+ m->nodes[m->nodes_entered++] = rx;
+}
+
+static void _create_bitsets(struct dm_regex *m)
+{
+ int i;
+
+ for (i = 0; i < m->num_nodes; i++) {
+ struct rx_node *n = m->nodes[i];
+ n->firstpos = dm_bitset_create(m->scratch, m->num_nodes);
+ n->lastpos = dm_bitset_create(m->scratch, m->num_nodes);
+ n->followpos = dm_bitset_create(m->scratch, m->num_nodes);
+ }
+}
+
+static void _calc_functions(struct dm_regex *m)
+{
+ int i, j, final = 1;
+ struct rx_node *rx, *c1, *c2;
+
+ for (i = 0; i < m->num_nodes; i++) {
+ rx = m->nodes[i];
+ c1 = rx->left;
+ c2 = rx->right;
+
+ if (dm_bit(rx->charset, TARGET_TRANS))
+ rx->final = final++;
+
+ switch (rx->type) {
+ case CAT:
+ if (c1->nullable)
+ dm_bit_union(rx->firstpos,
+ c1->firstpos, c2->firstpos);
+ else
+ dm_bit_copy(rx->firstpos, c1->firstpos);
+
+ if (c2->nullable)
+ dm_bit_union(rx->lastpos,
+ c1->lastpos, c2->lastpos);
+ else
+ dm_bit_copy(rx->lastpos, c2->lastpos);
+
+ rx->nullable = c1->nullable && c2->nullable;
+ break;
+
+ case PLUS:
+ dm_bit_copy(rx->firstpos, c1->firstpos);
+ dm_bit_copy(rx->lastpos, c1->lastpos);
+ rx->nullable = c1->nullable;
+ break;
+
+ case OR:
+ dm_bit_union(rx->firstpos, c1->firstpos, c2->firstpos);
+ dm_bit_union(rx->lastpos, c1->lastpos, c2->lastpos);
+ rx->nullable = c1->nullable || c2->nullable;
+ break;
+
+ case QUEST:
+ case STAR:
+ dm_bit_copy(rx->firstpos, c1->firstpos);
+ dm_bit_copy(rx->lastpos, c1->lastpos);
+ rx->nullable = 1;
+ break;
+
+ case CHARSET:
+ dm_bit_set(rx->firstpos, i);
+ dm_bit_set(rx->lastpos, i);
+ rx->nullable = 0;
+ break;
+
+ default:
+ log_error("Internal error: Unknown calc node type");
+ }
+
+ /*
+ * followpos has it's own switch
+ * because PLUS and STAR do the
+ * same thing.
+ */
+ switch (rx->type) {
+ case CAT:
+ for (j = 0; j < m->num_nodes; j++) {
+ if (dm_bit(c1->lastpos, j)) {
+ struct rx_node *n = m->nodes[j];
+ dm_bit_union(n->followpos,
+ n->followpos, c2->firstpos);
+ }
+ }
+ break;
+
+ case PLUS:
+ case STAR:
+ for (j = 0; j < m->num_nodes; j++) {
+ if (dm_bit(rx->lastpos, j)) {
+ struct rx_node *n = m->nodes[j];
+ dm_bit_union(n->followpos,
+ n->followpos, rx->firstpos);
+ }
+ }
+ break;
+ }
+ }
+}
+
+static struct dfa_state *_create_dfa_state(struct dm_pool *mem)
+{
+ return dm_pool_zalloc(mem, sizeof(struct dfa_state));
+}
+
+static struct state_queue *_create_state_queue(struct dm_pool *mem,
+ struct dfa_state *dfa,
+ dm_bitset_t bits)
+{
+ struct state_queue *r = dm_pool_alloc(mem, sizeof(*r));
+
+ if (!r) {
+ stack;
+ return NULL;
+ }
+
+ r->s = dfa;
+ r->bits = dm_bitset_create(mem, bits[0]); /* first element is the size */
+ dm_bit_copy(r->bits, bits);
+ r->next = 0;
+ return r;
+}
+
+static int _calc_states(struct dm_regex *m, struct rx_node *rx)
+{
+ unsigned iwidth = (m->num_nodes / DM_BITS_PER_INT) + 1;
+ struct ttree *tt = ttree_create(m->scratch, iwidth);
+ struct state_queue *h, *t, *tmp;
+ struct dfa_state *dfa, *ldfa;
+ int i, a, set_bits = 0, count = 0;
+ dm_bitset_t bs, dfa_bits;
+
+ if (!tt)
+ return_0;
+
+ if (!(bs = dm_bitset_create(m->scratch, m->num_nodes)))
+ return_0;
+
+ /* create first state */
+ dfa = _create_dfa_state(m->mem);
+ m->start = dfa;
+ ttree_insert(tt, rx->firstpos + 1, dfa);
+
+ /* prime the queue */
+ h = t = _create_state_queue(m->scratch, dfa, rx->firstpos);
+ while (h) {
+ /* pop state off front of the queue */
+ dfa = h->s;
+ dfa_bits = h->bits;
+ h = h->next;
+
+ /* iterate through all the inputs for this state */
+ dm_bit_clear_all(bs);
+ for (a = 0; a < 256; a++) {
+ /* iterate through all the states in firstpos */
+ for (i = dm_bit_get_first(dfa_bits);
+ i >= 0; i = dm_bit_get_next(dfa_bits, i)) {
+ if (dm_bit(m->nodes[i]->charset, a)) {
+ if (a == TARGET_TRANS)
+ dfa->final = m->nodes[i]->final;
+
+ dm_bit_union(bs, bs,
+ m->nodes[i]->followpos);
+ set_bits = 1;
+ }
+ }
+
+ if (set_bits) {
+ ldfa = ttree_lookup(tt, bs + 1);
+ if (!ldfa) {
+ /* push */
+ ldfa = _create_dfa_state(m->mem);
+ ttree_insert(tt, bs + 1, ldfa);
+ tmp =
+ _create_state_queue(m->scratch,
+ ldfa, bs);
+ if (!h)
+ h = t = tmp;
+ else {
+ t->next = tmp;
+ t = tmp;
+ }
+
+ count++;
+ }
+
+ dfa->lookup[a] = ldfa;
+ set_bits = 0;
+ dm_bit_clear_all(bs);
+ }
+ }
+ }
+
+ log_debug("Matcher built with %d dfa states", count);
+ return 1;
+}
+
+struct dm_regex *dm_regex_create(struct dm_pool *mem, const char **patterns,
+ unsigned num)
+{
+ char *all, *ptr;
+ int i;
+ size_t len = 0;
+ struct rx_node *rx;
+ struct dm_pool *scratch = dm_pool_create("regex matcher", 10 * 1024);
+ struct dm_regex *m;
+
+ if (!scratch) {
+ stack;
+ return NULL;
+ }
+
+ if (!(m = dm_pool_alloc(mem, sizeof(*m)))) {
+ stack;
+ dm_pool_destroy(scratch);
+ return NULL;
+ }
+
+ memset(m, 0, sizeof(*m));
+
+ /* join the regexps together, delimiting with zero */
+ for (i = 0; i < num; i++)
+ len += strlen(patterns[i]) + 8;
+
+ ptr = all = dm_pool_alloc(scratch, len + 1);
+
+ if (!all) {
+ stack;
+ goto bad;
+ }
+
+ for (i = 0; i < num; i++) {
+ ptr += sprintf(ptr, "(.*(%s)%c)", patterns[i], TARGET_TRANS);
+ if (i < (num - 1))
+ *ptr++ = '|';
+ }
+
+ /* parse this expression */
+ if (!(rx = rx_parse_tok(scratch, all, ptr))) {
+ log_error("Couldn't parse regex");
+ goto bad;
+ }
+
+ m->mem = mem;
+ m->scratch = scratch;
+ m->num_nodes = _count_nodes(rx);
+ m->nodes = dm_pool_alloc(scratch, sizeof(*m->nodes) * m->num_nodes);
+
+ if (!m->nodes) {
+ stack;
+ goto bad;
+ }
+
+ _fill_table(m, rx);
+ _create_bitsets(m);
+ _calc_functions(m);
+ _calc_states(m, rx);
+ dm_pool_destroy(scratch);
+ m->scratch = NULL;
+
+ return m;
+
+ bad:
+ dm_pool_destroy(scratch);
+ dm_pool_destroy(mem);
+ return NULL;
+}
+
+static struct dfa_state *_step_matcher(int c, struct dfa_state *cs, int *r)
+{
+ if (!(cs = cs->lookup[(unsigned char) c]))
+ return NULL;
+
+ if (cs->final && (cs->final > *r))
+ *r = cs->final;
+
+ return cs;
+}
+
+int dm_regex_match(struct dm_regex *m, const char *b)
+{
+ struct dfa_state *cs = m->start;
+ int r = 0;
+
+ if (!(cs = _step_matcher(HAT_CHAR, cs, &r)))
+ goto out;
+
+ for (; *b; b++)
+ if (!(cs = _step_matcher(*b, cs, &r)))
+ goto out;
+
+ _step_matcher(DOLLAR_CHAR, cs, &r);
+
+ out:
+ /* subtract 1 to get back to zero index */
+ return r - 1;
+}
Index: device-mapper.work/lib/regex/parse_rx.c
===================================================================
--- /dev/null
+++ device-mapper.work/lib/regex/parse_rx.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "parse_rx.h"
+
+struct parse_sp { /* scratch pad for the parsing process */
+ struct dm_pool *mem;
+ int type; /* token type, 0 indicates a charset */
+ dm_bitset_t charset; /* The current charset */
+ const char *cursor; /* where we are in the regex */
+ const char *rx_end; /* 1pte for the expression being parsed */
+};
+
+static struct rx_node *_or_term(struct parse_sp *ps);
+
+static void _single_char(struct parse_sp *ps, unsigned int c, const char *ptr)
+{
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_clear_all(ps->charset);
+ dm_bit_set(ps->charset, c);
+}
+
+/*
+ * Get the next token from the regular expression.
+ * Returns: 1 success, 0 end of input, -1 error.
+ */
+static int _rx_get_token(struct parse_sp *ps)
+{
+ int neg = 0, range = 0;
+ char c, lc = 0;
+ const char *ptr = ps->cursor;
+ if (ptr == ps->rx_end) { /* end of input ? */
+ ps->type = -1;
+ return 0;
+ }
+
+ switch (*ptr) {
+ /* charsets and ncharsets */
+ case '[':
+ ptr++;
+ if (*ptr == '^') {
+ dm_bit_set_all(ps->charset);
+
+ /* never transition on zero */
+ dm_bit_clear(ps->charset, 0);
+ neg = 1;
+ ptr++;
+
+ } else
+ dm_bit_clear_all(ps->charset);
+
+ while ((ptr < ps->rx_end) && (*ptr != ']')) {
+ if (*ptr == '\\') {
+ /* an escaped character */
+ ptr++;
+ switch (*ptr) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ default:
+ c = *ptr;
+ }
+ } else if (*ptr == '-' && lc) {
+ /* we've got a range on our hands */
+ range = 1;
+ ptr++;
+ if (ptr == ps->rx_end) {
+ log_error("Incomplete range"
+ "specification");
+ return -1;
+ }
+ c = *ptr;
+ } else
+ c = *ptr;
+
+ if (range) {
+ /* add lc - c into the bitset */
+ if (lc > c) {
+ char tmp = c;
+ c = lc;
+ lc = tmp;
+ }
+
+ for (; lc <= c; lc++) {
+ if (neg)
+ dm_bit_clear(ps->charset, lc);
+ else
+ dm_bit_set(ps->charset, lc);
+ }
+ range = 0;
+ } else {
+ /* add c into the bitset */
+ if (neg)
+ dm_bit_clear(ps->charset, c);
+ else
+ dm_bit_set(ps->charset, c);
+ }
+ ptr++;
+ lc = c;
+ }
+
+ if (ptr >= ps->rx_end) {
+ ps->type = -1;
+ return -1;
+ }
+
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ break;
+
+ /* These characters are special, we just return their ASCII
+ codes as the type. Sorted into ascending order to help the
+ compiler */
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case '?':
+ case '|':
+ ps->type = (int) *ptr;
+ ps->cursor = ptr + 1;
+ break;
+
+ case '^':
+ _single_char(ps, HAT_CHAR, ptr);
+ break;
+
+ case '$':
+ _single_char(ps, DOLLAR_CHAR, ptr);
+ break;
+
+ case '.':
+ /* The 'all but newline' character set */
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_set_all(ps->charset);
+ dm_bit_clear(ps->charset, (int) '\n');
+ dm_bit_clear(ps->charset, (int) '\r');
+ dm_bit_clear(ps->charset, 0);
+ break;
+
+ case '\\':
+ /* escaped character */
+ ptr++;
+ if (ptr >= ps->rx_end) {
+ log_error("Badly quoted character at end "
+ "of expression");
+ ps->type = -1;
+ return -1;
+ }
+
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_clear_all(ps->charset);
+ switch (*ptr) {
+ case 'n':
+ dm_bit_set(ps->charset, (int) '\n');
+ break;
+ case 'r':
+ dm_bit_set(ps->charset, (int) '\r');
+ break;
+ case 't':
+ dm_bit_set(ps->charset, (int) '\t');
+ break;
+ default:
+ dm_bit_set(ps->charset, (int) *ptr);
+ }
+ break;
+
+ default:
+ /* add a single character to the bitset */
+ ps->type = 0;
+ ps->cursor = ptr + 1;
+ dm_bit_clear_all(ps->charset);
+ dm_bit_set(ps->charset, (int) *ptr);
+ break;
+ }
+
+ return 1;
+}
+
+static struct rx_node *_node(struct dm_pool *mem, int type,
+ struct rx_node *l, struct rx_node *r)
+{
+ struct rx_node *n = dm_pool_zalloc(mem, sizeof(*n));
+
+ if (n) {
+ if (!(n->charset = dm_bitset_create(mem, 256))) {
+ dm_pool_free(mem, n);
+ return NULL;
+ }
+
+ n->type = type;
+ n->left = l;
+ n->right = r;
+ }
+
+ return n;
+}
+
+static struct rx_node *_term(struct parse_sp *ps)
+{
+ struct rx_node *n;
+
+ switch (ps->type) {
+ case 0:
+ if (!(n = _node(ps->mem, CHARSET, NULL, NULL))) {
+ stack;
+ return NULL;
+ }
+
+ dm_bit_copy(n->charset, ps->charset);
+ _rx_get_token(ps); /* match charset */
+ break;
+
+ case '(':
+ _rx_get_token(ps); /* match '(' */
+ n = _or_term(ps);
+ if (ps->type != ')') {
+ log_error("missing ')' in regular expression");
+ return 0;
+ }
+ _rx_get_token(ps); /* match ')' */
+ break;
+
+ default:
+ n = 0;
+ }
+
+ return n;
+}
+
+static struct rx_node *_closure_term(struct parse_sp *ps)
+{
+ struct rx_node *l, *n;
+
+ if (!(l = _term(ps)))
+ return NULL;
+
+ for (;;) {
+ switch (ps->type) {
+ case '*':
+ n = _node(ps->mem, STAR, l, NULL);
+ break;
+
+ case '+':
+ n = _node(ps->mem, PLUS, l, NULL);
+ break;
+
+ case '?':
+ n = _node(ps->mem, QUEST, l, NULL);
+ break;
+
+ default:
+ return l;
+ }
+
+ if (!n) {
+ stack;
+ return NULL;
+ }
+
+ _rx_get_token(ps);
+ l = n;
+ }
+
+ return n;
+}
+
+static struct rx_node *_cat_term(struct parse_sp *ps)
+{
+ struct rx_node *l, *r, *n;
+
+ if (!(l = _closure_term(ps)))
+ return NULL;
+
+ if (ps->type == '|')
+ return l;
+
+ if (!(r = _cat_term(ps)))
+ return l;
+
+ if (!(n = _node(ps->mem, CAT, l, r)))
+ stack;
+
+ return n;
+}
+
+static struct rx_node *_or_term(struct parse_sp *ps)
+{
+ struct rx_node *l, *r, *n;
+
+ if (!(l = _cat_term(ps)))
+ return NULL;
+
+ if (ps->type != '|')
+ return l;
+
+ _rx_get_token(ps); /* match '|' */
+
+ if (!(r = _or_term(ps))) {
+ log_error("Badly formed 'or' expression");
+ return NULL;
+ }
+
+ if (!(n = _node(ps->mem, OR, l, r)))
+ stack;
+
+ return n;
+}
+
+struct rx_node *rx_parse_tok(struct dm_pool *mem,
+ const char *begin, const char *end)
+{
+ struct rx_node *r;
+ struct parse_sp *ps = dm_pool_zalloc(mem, sizeof(*ps));
+
+ if (!ps) {
+ stack;
+ return NULL;
+ }
+
+ ps->mem = mem;
+ ps->charset = dm_bitset_create(mem, 256);
+ ps->cursor = begin;
+ ps->rx_end = end;
+ _rx_get_token(ps); /* load the first token */
+
+ if (!(r = _or_term(ps))) {
+ log_error("Parse error in regex");
+ dm_pool_free(mem, ps);
+ }
+
+ return r;
+}
+
+struct rx_node *rx_parse_str(struct dm_pool *mem, const char *str)
+{
+ return rx_parse_tok(mem, str, str + strlen(str));
+}
Index: device-mapper.work/lib/regex/parse_rx.h
===================================================================
--- /dev/null
+++ device-mapper.work/lib/regex/parse_rx.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_PARSE_REGEX_H
+#define _LVM_PARSE_REGEX_H
+
+enum {
+ CAT,
+ STAR,
+ PLUS,
+ OR,
+ QUEST,
+ CHARSET
+};
+
+/*
+ * We're never going to be running the regex on non-printable
+ * chars, so we can use a couple of these chars to represent the
+ * start and end of a string.
+ */
+#define HAT_CHAR 0x2
+#define DOLLAR_CHAR 0x3
+
+struct rx_node {
+ int type;
+ dm_bitset_t charset;
+ struct rx_node *left, *right;
+
+ /* used to build the dfa for the toker */
+ int nullable, final;
+ dm_bitset_t firstpos;
+ dm_bitset_t lastpos;
+ dm_bitset_t followpos;
+};
+
+struct rx_node *rx_parse_str(struct dm_pool *mem, const char *str);
+struct rx_node *rx_parse_tok(struct dm_pool *mem,
+ const char *begin, const char *end);
+
+#endif
Index: device-mapper.work/lib/regex/ttree.c
===================================================================
--- /dev/null
+++ device-mapper.work/lib/regex/ttree.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "lib.h"
+#include "ttree.h"
+
+struct node {
+ unsigned k;
+ struct node *l, *m, *r;
+ void *data;
+};
+
+struct ttree {
+ int klen;
+ struct dm_pool *mem;
+ struct node *root;
+};
+
+static struct node **_lookup_single(struct node **c, unsigned int k)
+{
+ while (*c) {
+ if (k < (*c)->k)
+ c = &((*c)->l);
+
+ else if (k > (*c)->k)
+ c = &((*c)->r);
+
+ else {
+ c = &((*c)->m);
+ break;
+ }
+ }
+
+ return c;
+}
+
+void *ttree_lookup(struct ttree *tt, unsigned *key)
+{
+ struct node **c = &tt->root;
+ int count = tt->klen;
+
+ while (*c && count) {
+ c = _lookup_single(c, *key++);
+ count--;
+ }
+
+ return *c ? (*c)->data : NULL;
+}
+
+static struct node *_tree_node(struct dm_pool *mem, unsigned int k)
+{
+ struct node *n = dm_pool_zalloc(mem, sizeof(*n));
+
+ if (n)
+ n->k = k;
+
+ return n;
+}
+
+int ttree_insert(struct ttree *tt, unsigned int *key, void *data)
+{
+ struct node **c = &tt->root;
+ int count = tt->klen;
+ unsigned int k;
+
+ do {
+ k = *key++;
+ c = _lookup_single(c, k);
+ count--;
+
+ } while (*c && count);
+
+ if (!*c) {
+ count++;
+
+ while (count--) {
+ if (!(*c = _tree_node(tt->mem, k))) {
+ stack;
+ return 0;
+ }
+
+ k = *key++;
+
+ if (count)
+ c = &((*c)->m);
+ }
+ }
+ (*c)->data = data;
+
+ return 1;
+}
+
+struct ttree *ttree_create(struct dm_pool *mem, unsigned int klen)
+{
+ struct ttree *tt;
+
+ if (!(tt = dm_pool_zalloc(mem, sizeof(*tt)))) {
+ stack;
+ return NULL;
+ }
+
+ tt->klen = klen;
+ tt->mem = mem;
+ return tt;
+}
Index: device-mapper.work/lib/regex/ttree.h
===================================================================
--- /dev/null
+++ device-mapper.work/lib/regex/ttree.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is part of the device-mapper userspace tools.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License v.2.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LVM_TTREE_H
+#define _LVM_TTREE_H
+
+struct ttree;
+
+struct ttree *ttree_create(struct dm_pool *mem, unsigned int klen);
+
+void *ttree_lookup(struct ttree *tt, unsigned *key);
+int ttree_insert(struct ttree *tt, unsigned *key, void *data);
+
+#endif
Index: device-mapper.work/lib/Makefile.in
===================================================================
--- device-mapper.work.orig/lib/Makefile.in
+++ device-mapper.work/lib/Makefile.in
@@ -25,6 +25,9 @@ SOURCES =\
libdm-deptree.c \
libdm-string.c \
libdm-report.c \
+ regex/matcher.c \
+ regex/parse_rx.c \
+ regex/ttree.c \
mm/dbg_malloc.c \
mm/pool.c \
$(interface)/libdm-iface.c
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (4/6) Add filtering feature to dm_report
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
` (2 preceding siblings ...)
2007-04-18 16:47 ` [PATCH] libdevmapper: (3/6) Move lib/regex from LVM2 Jun'ichi Nomura
@ 2007-04-18 16:47 ` Jun'ichi Nomura
2007-04-18 19:32 ` [PATCH] LVM2: (2/2) Use dm_report filter Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (5/6) Add '--filter' option to dmsetup Jun'ichi Nomura
` (2 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:47 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 758 bytes --]
Hi,
This patch adds a filtering feature to dm_report.
- Add dm_report_set_filter() API.
By calling this, dm_report_object() omits rows which doesn't
match the condition specified by the filter.
- Fitlers can be composed with the following components:
* Simple comparison of field value
'==', '!=', '>', '>=', '<', '<='
Strings should be quoted by either ' or ".
Only decimal, non-negative integers are currently allowed.
* Regular expression
'=~', '!~'
Regular expression can be quoted by '/', '|', '#', or any
other characters.
* Logical combinations of the expressions
'and', 'or', '!', '(', ')'
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: libdm-report-add-filter.patch --]
[-- Type: text/x-patch, Size: 22659 bytes --]
Add filtering feature to dm_report.
Filter is set by dm_report_set_filters()
---
lib/.exported_symbols | 1
lib/libdevmapper.h | 4
lib/libdm-report.c | 783 +++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 781 insertions(+), 7 deletions(-)
Index: device-mapper.work/lib/.exported_symbols
===================================================================
--- device-mapper.work.orig/lib/.exported_symbols
+++ device-mapper.work/lib/.exported_symbols
@@ -127,5 +127,6 @@ dm_report_field_int32
dm_report_field_uint32
dm_report_field_uint64
dm_report_field_set_value
+dm_report_set_filter
dm_regex_create
dm_regex_match
Index: device-mapper.work/lib/libdevmapper.h
===================================================================
--- device-mapper.work.orig/lib/libdevmapper.h
+++ device-mapper.work/lib/libdevmapper.h
@@ -688,6 +688,10 @@ int dm_report_object(struct dm_report *r
int dm_report_output(struct dm_report *rh);
void dm_report_free(struct dm_report *rh);
+/* Set filter */
+int dm_report_set_filter(struct dm_report *rh,
+ const char *filter, uint32_t flags);
+
/*
* Report functions are provided for simple data types.
* They take care of allocating copies of the data.
Index: device-mapper.work/lib/libdm-report.c
===================================================================
--- device-mapper.work.orig/lib/libdm-report.c
+++ device-mapper.work/lib/libdm-report.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2007 NEC Corporation
*
* This file is part of device-mapper userspace tools.
* The code is based on LVM2 report function.
@@ -14,6 +15,7 @@
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <ctype.h>
#include "libdevmapper.h"
#include "list.h"
#include "log.h"
@@ -24,6 +26,7 @@
#define RH_SORT_REQUIRED 0x00000100
#define RH_HEADINGS_PRINTED 0x00000200
+struct filter_node;
struct dm_report {
struct dm_pool *mem;
@@ -46,6 +49,8 @@ struct dm_report {
/* To store caller private data */
void *private;
+
+ struct filter_node *filter_root;
};
/*
@@ -474,6 +479,653 @@ static int _parse_keys(struct dm_report
return 1;
}
+/*
+ * Filter tokens
+ */
+
+/*
+ * Comparison and logical operation tokens
+ * OP_CMP := '==' | '!=' | '>' | '>=' | '<' | '<=' | '=~' | '!~'
+ * OP_LOG := '!' | '(' | ')' | ',' | 'and' | 'or' | '&&' | '||'
+ */
+
+struct op_def {
+ const char *string;
+ uint32_t flags;
+ const char *desc;
+};
+
+static const char * _skip_space(const char *s)
+{
+ while (*s && isspace(*s))
+ s++;
+ return s;
+}
+
+static const char * _token_match(const char *s, const char *token)
+{
+ s = _skip_space(s);
+
+ if (!strncmp(s, token, strlen(token)))
+ return s + strlen(token);
+
+ return NULL;
+}
+
+static int _tok_op(struct op_def *t, const char *s, const char **end,
+ uint32_t expect)
+{
+ const char *next;
+
+ for (; t->string; t++) {
+ if (expect && !(t->flags & expect))
+ continue;
+
+ if ((next = _token_match(s, t->string))) {
+ *end = next;
+ return t->flags;
+ }
+ }
+
+ *end = s;
+ return 0;
+}
+
+/* OP_CMP definition and matcher function */
+
+#define FLD_CMP_MASK 0x000FF000
+#define FLD_CMP_EQUAL 0x00001000
+#define FLD_CMP_NOT 0x00002000
+#define FLD_CMP_GT 0x00004000
+#define FLD_CMP_LT 0x00008000
+#define FLD_CMP_REGEX 0x00010000
+
+/* _tok_op() tries to match from the first of this list.
+ * So longer one should come first.
+ * e.g. ">=" should appear earlier in the list than ">". */
+static struct op_def _op_cmp[] = {
+ { "==", FLD_CMP_EQUAL, "Equal to" },
+ { "!=", FLD_CMP_NOT|FLD_CMP_EQUAL, "Not equal" },
+ { ">=", FLD_CMP_GT|FLD_CMP_EQUAL|DM_REPORT_FIELD_TYPE_NUMBER,
+ "Greater than or equal to" },
+ { ">", FLD_CMP_GT|DM_REPORT_FIELD_TYPE_NUMBER, "Greater than" },
+ { "<=", FLD_CMP_LT|FLD_CMP_EQUAL|DM_REPORT_FIELD_TYPE_NUMBER,
+ "Lesser than or equal to" },
+ { "<", FLD_CMP_LT|DM_REPORT_FIELD_TYPE_NUMBER, "Lesser than" },
+ { "=~", FLD_CMP_REGEX|DM_REPORT_FIELD_TYPE_STRING,
+ "Matching regular expression" },
+ { "!~", FLD_CMP_REGEX|FLD_CMP_NOT|DM_REPORT_FIELD_TYPE_STRING,
+ "Not matching regular expression" },
+ { NULL, 0, NULL }
+};
+
+static uint32_t _tok_op_cmp(const char *s, const char **end)
+{
+ return _tok_op(_op_cmp, s, end, 0);
+}
+
+/* OP_LOG definitions and matcher functions */
+
+#define FILTER_TYPE_MASK 0x00FF
+#define FILTER_COND 0x0001
+#define FILTER_AND 0x0002
+#define FILTER_OR 0x0004
+#define FILTER_MODIFIER_MASK 0x0F00
+#define FILTER_NOT 0x0100
+#define FILTER_DELIMITER_MASK 0xF000
+#define FILTER_PS 0x1000
+#define FILTER_PE 0x2000
+
+static struct op_def _op_log[] = {
+ { "&&", FILTER_AND, NULL },
+ { "and", FILTER_AND, NULL },
+ { ",", FILTER_AND, NULL },
+ { "||", FILTER_OR, NULL },
+ { "or", FILTER_OR, NULL },
+ { "!", FILTER_NOT, NULL },
+ { "(", FILTER_PS, NULL },
+ { ")", FILTER_PE, NULL },
+ { NULL, 0, NULL},
+};
+
+static int _tok_op_log(const char *s, const char **end, uint32_t expect)
+{
+ return _tok_op(_op_log, s, end, expect);
+}
+
+/*
+ * Other tokens (FIELD, VALUE, STRING, NUMBER, REGEX)
+ * FIELD := <strings of alphabet, number and '_'>
+ * VALUE := NUMBER | STRING
+ * REGEX := <strings quoted by any character>
+ * NUMBER := <strings of [0-9]> (because sort_value is unsigned)
+ * STRING := <strings quoted by '"' or '\''>
+ *
+ * _tok_* functions
+ *
+ * Input:
+ * s - a pointer to the parsed string
+ * Output:
+ * begin - a pointer to the beginning of the token
+ * end - a pointer to the end of the token + 1
+ * or undefined if return value is NULL
+ * return value - a starting point of the next parsing
+ * NULL if s doesn't match with token type
+ * (the parsing should be terminated)
+ */
+
+static const char * _tok_number(const char *s,
+ const char **begin, const char **end)
+{
+ *begin = s;
+ while (*s && isdigit(*s))
+ s++;
+ *end = s;
+
+ return s;
+}
+
+static const char * _tok_string(const char *s,
+ const char **begin, const char **end,
+ const char endchar)
+{
+ *begin = s;
+ while (*s && *s != endchar)
+ s++;
+ *end = s;
+
+ return s;
+}
+
+static const char * _tok_regex(const char *s,
+ const char **begin, const char **end,
+ char *quote)
+{
+ s = _skip_space(s);
+
+ if (!*s) {
+ log_error("Regular expression expected");
+ return NULL;
+ }
+
+ switch (*s) {
+ case '(': *quote = ')'; break;
+ case '{': *quote = '}'; break;
+ case '[': *quote = ']'; break;
+ default: *quote = *s;
+ }
+
+ s = _tok_string(s + 1, begin, end, *quote);
+ if (!*s) {
+ log_error("Missing end quote of regex");
+ return NULL;
+ }
+ s++;
+
+ return s;
+}
+
+static const char * _tok_value(const char *s,
+ const char **begin, const char **end,
+ char *quote)
+{
+ s = _skip_space(s);
+
+ if (*s == '"' || *s == '\'') { /* quoted string */
+ *quote = *s;
+ s = _tok_string(s + 1, begin, end, *quote);
+ if (!*s) {
+ log_error("Missing end quote of string");
+ return NULL;
+ }
+ s++;
+ } else { /* number */
+ *quote = 0;
+ s = _tok_number(s, begin, end);
+ if (*begin == *end) {
+ log_error("Empty value or unquoted string");
+ return NULL;
+ }
+ }
+
+ return s;
+}
+
+static int _field_name_char(const char c)
+{
+ return (isalnum(c) || c == '_' || c == '-');
+}
+
+static const char * _tok_field_name(const char *s,
+ const char **begin, const char **end)
+{
+ s = _skip_space(s);
+
+ *begin = s;
+ while (*s && _field_name_char(*s))
+ s++;
+ *end = s;
+
+ if (*begin == *end)
+ return NULL;
+
+ return s;
+}
+
+static void _display_operators(void)
+{
+ int i;
+
+ log_print(" ");
+ log_print("Comparison operators");
+ log_print("--------------------");
+
+ for (i = 0; _op_cmp[i].string; i++)
+ log_print(" %-4s - %s%s%s", _op_cmp[i].string, _op_cmp[i].desc,
+ _op_cmp[i].flags & DM_REPORT_FIELD_TYPE_NUMBER ?
+ " (numeric field only)" : "",
+ _op_cmp[i].flags & DM_REPORT_FIELD_TYPE_STRING ?
+ " (string field only)" : "");
+
+ log_print(" ");
+ log_print("Comparison operands");
+ log_print("-------------------");
+ log_print(" numbers - decimal, non-negative, integer only");
+ log_print(" strings - characters quoted by ' or \"");
+ log_print(" regular expression - characters quoted by any character");
+
+ log_print(" ");
+ log_print("Combining comparison");
+ log_print("--------------------");
+ log_print(" and, && - logical AND");
+ log_print(" or, || - logical OR");
+ log_print(" () - grouping expressions");
+ log_print(" ! - logical NOT");
+}
+
+/*
+ * Filter components
+ */
+
+/* a comparison condition */
+struct field_filter {
+ struct field_properties *fp;
+ uint32_t flags; /* see _op_cmp[] */
+ union {
+ const char *string;
+ uint64_t number;
+ struct dm_regex *regex;
+ } v;
+};
+
+/* an expression: either comparison condition, and-clause or or-clause */
+struct filter_node {
+ uint32_t type; /* FILTER_* */
+ struct list list;
+ union {
+ struct field_filter * f; /* type is COND */
+ struct list l; /* type is AND or OR */
+ } e;
+};
+
+static struct field_filter *_create_field_filter(struct dm_report *rh,
+ uint32_t field_num,
+ const char *v, size_t len,
+ uint32_t flags)
+{
+ struct field_properties *fp, *found = NULL;
+ struct field_filter *filter;
+ char *s;
+
+ list_iterate_items(fp, &rh->field_props) {
+ if (fp->field_num == field_num) {
+ found = fp;
+ break;
+ }
+ }
+
+ /* The field is neither used in display options nor sort keys. */
+ if (!found) {
+ if (!(found = _add_field(rh, field_num, FLD_HIDDEN)))
+ return NULL;
+ }
+
+ if (!(found->flags & flags & DM_REPORT_FIELD_TYPE_MASK)) {
+ log_error("dm_report: Incompatible comparison type");
+ return NULL;
+ }
+
+ /* set up filter */
+ if (!(filter = dm_pool_zalloc(rh->mem, sizeof(struct field_filter)))) {
+ log_error("dm_report: struct field_filter allocation failed");
+ return NULL;
+ }
+ filter->fp = found;
+ filter->flags = flags;
+
+ /* store comparison operand */
+ if (flags & FLD_CMP_REGEX) {
+ if (!(s = dm_malloc(len + 1))) {
+ log_error("dm_report: dm_malloc failed");
+ goto out_free_filter;
+ }
+ memcpy(s, v, len);
+ s[len] = '\0';
+
+ filter->v.regex = dm_regex_create(rh->mem,
+ (const char **) &s, 1);
+ dm_free(s);
+ if (!filter->v.regex) {
+ log_error("dm_report: failed to create matcher");
+ goto out_free_filter;
+ }
+ } else {
+ if (!(s = dm_pool_alloc(rh->mem, len + 1))) {
+ log_error("dm_report: dm_pool_alloc failed");
+ goto out_free_filter;
+ }
+ memcpy(s, v, len);
+ s[len] = '\0';
+
+ if (flags & DM_REPORT_FIELD_TYPE_STRING) {
+ filter->v.string = s;
+ } else {
+ filter->v.number = strtoul(s, NULL, 0);
+ dm_pool_free(rh->mem, s);
+ }
+ }
+
+ return filter;
+
+ out_free_filter:
+ dm_pool_free(rh->mem, filter);
+ return NULL;
+}
+
+static struct field_filter * _filter_match(struct dm_report *rh,
+ const char *field, size_t flen,
+ const char *value, size_t vlen,
+ uint32_t flags)
+{
+ uint32_t f;
+
+ if (!flen)
+ return NULL;
+
+ for (f = 0; rh->fields[f].report_fn; f++)
+ if (_is_same_field(rh->fields[f].id,
+ field, flen, rh->field_prefix))
+ return _create_field_filter(rh, f, value, vlen, flags);
+
+ log_print("Undefined field name");
+ return NULL;
+}
+
+static struct filter_node * _alloc_filter_node(struct dm_pool *mem,
+ uint32_t type)
+{
+ struct filter_node *n;
+
+ if (!(n = dm_pool_zalloc(mem, sizeof(struct filter_node)))) {
+ log_error("dm_report: struct filter_node allocation failed");
+ return NULL;
+ }
+
+ list_init(&n->list);
+
+ n->type = type;
+ if (!(type & FILTER_COND))
+ list_init(&n->e.l);
+ return n;
+}
+
+/*
+ * Filter parser
+ *
+ * _parse_* functions
+ *
+ * Input:
+ * s - a pointer to the parsed string
+ * Output:
+ * next - a pointer used for next _parse_*'s input,
+ * next == s if return value is NULL
+ * return value - a filter node pointer,
+ * NULL if s doesn't match
+ */
+
+/*
+ * CONDITION := FIELD_NAME OP_CMP STRING |
+ * FIELD_NAME OP_CMP NUMBER |
+ * FIELD_NAME OP_REGEX REGEX
+ */
+static struct filter_node * _parse_condition(struct dm_report *rh,
+ const char *s, const char **next)
+{
+ struct field_filter *f;
+ struct filter_node *n;
+ const char *ws, *we; /* field name */
+ const char *vs, *ve; /* value */
+ const char *last;
+ uint32_t flags;
+ char quote;
+
+ /* field name */
+ if (!(last = _tok_field_name(s, &ws, &we))) {
+ log_error("Expecting field name");
+ goto syntax_error;
+ }
+ if (!last) {
+ log_error("Missing operator after the field name");
+ goto syntax_error;
+ }
+
+ /* comparison operator */
+ if (!(flags = _tok_op_cmp(we, &last))) {
+ log_error("Unrecognized comparison operator: %s", s);
+ goto syntax_error;
+ }
+ if (!last) {
+ log_error("Missing value after operator");
+ goto syntax_error;
+ }
+
+ /* comparison value */
+ if (flags & FLD_CMP_REGEX) {
+ if (!(last = _tok_regex(last, &vs, &ve, "e)))
+ goto syntax_error;
+ } else {
+ if (!(last = _tok_value(last, &vs, &ve, "e)))
+ goto syntax_error;
+
+ if (quote) {
+ /* the token is strings */
+ if (flags & DM_REPORT_FIELD_TYPE_NUMBER) {
+ log_print("The operator requires number");
+ goto syntax_error;
+ }
+ flags |= DM_REPORT_FIELD_TYPE_STRING;
+ } else {
+ /* the token is number */
+ if (flags & DM_REPORT_FIELD_TYPE_STRING) {
+ log_print("The operator requires string");
+ goto syntax_error;
+ }
+ flags |= DM_REPORT_FIELD_TYPE_NUMBER;
+ }
+ }
+ *next = _skip_space(last);
+
+ /* store condition */
+ f = _filter_match(rh, ws, (size_t) (we - ws),
+ vs, (size_t) (ve - vs), flags);
+ if (!f)
+ goto syntax_error;
+
+ if (!(n = _alloc_filter_node(rh->mem, FILTER_COND)))
+ return NULL;
+ n->e.f = f;
+ return n;
+
+ syntax_error:
+ log_error("Filter syntax error at %s", s);
+ *next = s;
+ return NULL;
+}
+
+/* EX := CONDITION | '!'? '(' EXPRESSION ')' */
+static struct filter_node * _parse_or_ex(struct dm_report *,
+ const char *, const char **,
+ struct filter_node *);
+
+static struct filter_node * _parse_ex(struct dm_report *rh,
+ const char *s, const char **next)
+{
+ struct filter_node *n = NULL;
+ uint32_t t;
+ const char *tmp;
+
+ t = _tok_op_log(s, next, FILTER_NOT|FILTER_PS);
+ if (t == FILTER_NOT) {
+ /* '!' '(' EXPRESSION ')' */
+ if (!_tok_op_log(*next, &tmp, FILTER_PS)) {
+ log_error("Syntax error: '(' expected");
+ goto out_error;
+ }
+ if (!(n = _parse_or_ex(rh, tmp, next, NULL)))
+ goto out_error;
+ n->type |= FILTER_NOT;
+ if (!_tok_op_log(*next, &tmp, FILTER_PE)) {
+ log_error("Syntax error: ')' expected");
+ goto out_error;
+ }
+ *next = tmp;
+ } else if (t == FILTER_PS) {
+ /* '(' EXPRESSION ')' */
+ if (!(n = _parse_or_ex(rh, *next, &tmp, NULL)))
+ goto out_error;
+ if (!_tok_op_log(tmp, next, FILTER_PE)) {
+ log_error("Syntax error: ')' expected");
+ goto out_error;
+ }
+ } else if ((s = _skip_space(s))) {
+ /* CONDITION */
+ n = _parse_condition(rh, s, next);
+ } else {
+ n = NULL;
+ *next = s;
+ }
+
+ return n;
+
+ out_error:
+ *next = s;
+ return NULL;
+}
+
+/* AND_EXPRESSION := EX (AND_OP AND_EXPRSSION) */
+static struct filter_node * _parse_and_ex(struct dm_report *rh,
+ const char *s, const char **next,
+ struct filter_node *and_n)
+{
+ struct filter_node *n;
+ const char *tmp;
+
+ n = _parse_ex(rh, s, next);
+ if (!n)
+ goto out_error;
+
+ if (!_tok_op_log(*next, &tmp, FILTER_AND)) {
+ if (!and_n)
+ return n;
+ list_add(&and_n->e.l, &n->list);
+ return and_n;
+ }
+
+ if (!and_n) {
+ if (!(and_n = _alloc_filter_node(rh->mem, FILTER_AND)))
+ goto out_error;
+ }
+ list_add(&and_n->e.l, &n->list);
+
+ return _parse_and_ex(rh, tmp, next, and_n);
+
+ out_error:
+ *next = s;
+ return NULL;
+}
+
+/* OR_EXPRESSION := AND_EXPRESSION (OR_OP OR_EXPRESSION) */
+static struct filter_node * _parse_or_ex(struct dm_report *rh,
+ const char *s, const char **next,
+ struct filter_node *or_n)
+{
+ struct filter_node *n;
+ const char *tmp;
+
+ n = _parse_and_ex(rh, s, next, NULL);
+ if (!n)
+ goto out_error;
+
+ if (!_tok_op_log(*next, &tmp, FILTER_OR)) {
+ if (!or_n)
+ return n;
+ list_add(&or_n->e.l, &n->list);
+ return or_n;
+ }
+
+ if (!or_n) {
+ if (!(or_n = _alloc_filter_node(rh->mem, FILTER_OR)))
+ goto out_error;
+ }
+ list_add(&or_n->e.l, &n->list);
+
+ return _parse_or_ex(rh, tmp, next, or_n);
+
+ out_error:
+ *next = s;
+ return NULL;
+}
+
+
+int dm_report_set_filter(struct dm_report *rh, const char *string,
+ uint32_t flags)
+{
+ const char *fin;
+ struct filter_node *root;
+
+ if (flags) {
+ log_error("dm_report_set_filter: flags not supported");
+ return 0;
+ }
+
+ if (rh->filter_root) {
+ log_error("dm_report_set_filter: filter already set");
+ return 0;
+ }
+
+ /* null string means no filter */
+ if (!string || !string[0])
+ return 1;
+
+ if (!(root = _alloc_filter_node(rh->mem, FILTER_OR)))
+ goto out_error;
+
+ if (!_parse_or_ex(rh, string, &fin, root) || *_skip_space(fin)) {
+ dm_pool_free(rh->mem, root);
+ goto out_error;
+ }
+
+ rh->filter_root = root;
+ return 1;
+
+ out_error:
+ _display_operators();
+ log_print(" ");
+ _display_fields(rh);
+ log_print(" ");
+ return 0;
+}
+
struct dm_report *dm_report_init(uint32_t *report_types,
const struct dm_report_object_type *types,
const struct dm_report_field_type *fields,
@@ -551,6 +1203,65 @@ void dm_report_free(struct dm_report *rh
/*
* Create a row of data for an object
*/
+static int _cmp_field_number(uint64_t a, uint64_t b, uint32_t flags)
+{
+ switch (flags & FLD_CMP_MASK) {
+ case FLD_CMP_EQUAL:
+ return a == b;
+ case FLD_CMP_NOT|FLD_CMP_EQUAL:
+ return a != b;
+ case FLD_CMP_GT:
+ return a > b;
+ case FLD_CMP_GT|FLD_CMP_EQUAL:
+ return a >= b;
+ case FLD_CMP_LT:
+ return a < b;
+ case FLD_CMP_LT|FLD_CMP_EQUAL:
+ return a <= b;
+ default:
+ log_error("Unsupported comparison type for number");
+ }
+ return 0;
+}
+
+static int _cmp_field_string(const char *a, const char *b, uint32_t flags)
+{
+ switch (flags & FLD_CMP_MASK) {
+ case FLD_CMP_EQUAL:
+ return !strcmp(a, b);
+ case FLD_CMP_NOT|FLD_CMP_EQUAL:
+ return strcmp(a, b);
+ default:
+ log_error("Unsupported comparison type for string");
+ }
+ return 0;
+}
+
+static int _cmp_field_regex(const char *s, struct dm_regex *r, uint32_t flags)
+{
+ return (dm_regex_match(r, s) >= 0) ^ ((flags & FLD_CMP_NOT) != 0);
+}
+
+static int _compare_field(struct dm_report_field *field,
+ struct field_filter *filter)
+{
+ if (!field->sort_value) {
+ log_error("dm_report: field without value: %d",
+ field->props->field_num);
+ return 0;
+ }
+
+ if (filter->flags & FLD_CMP_REGEX)
+ return _cmp_field_regex((const char *) field->sort_value,
+ filter->v.regex, filter->flags);
+ else if (field->props->flags & DM_REPORT_FIELD_TYPE_NUMBER)
+ return _cmp_field_number(*(const uint64_t *)field->sort_value,
+ filter->v.number, filter->flags);
+ else /* DM_REPORT_FIELD_TYPE_STRING */
+ return _cmp_field_string((const char *) field->sort_value,
+ filter->v.string, filter->flags);
+}
+
static void * _report_get_field_data(struct dm_report *rh,
struct field_properties *fp, void *object)
{
@@ -562,6 +1273,51 @@ static void * _report_get_field_data(str
return ret + rh->fields[fp->field_num].offset;
}
+static int _filter(struct filter_node *n, struct list *fields)
+{
+ int r;
+ struct filter_node *f;
+ struct dm_report_field *field;
+
+ switch (n->type & FILTER_TYPE_MASK) {
+ case FILTER_COND:
+ r = 1;
+ list_iterate_items(field, fields) {
+ if (n->e.f->fp != field->props)
+ continue;
+ if (!_compare_field(field, n->e.f))
+ r = 0;
+ }
+ break;
+ case FILTER_OR:
+ r = 0;
+ list_iterate_items(f, &n->e.l)
+ if ((r |= _filter(f, fields)))
+ break;
+ break;
+ case FILTER_AND:
+ r = 1;
+ list_iterate_items(f, &n->e.l)
+ if (!(r &= _filter(f, fields)))
+ break;
+ break;
+ default:
+ log_error("Unsupported filter type");
+ return 0;
+ }
+
+ return (n->type & FILTER_NOT) ? !r : r;
+}
+
+/* the object is given as a list of "struct field"s */
+static int _filter_object(struct dm_report *rh, struct list *fields)
+{
+ if (!rh->filter_root)
+ return 1;
+
+ return _filter(rh->filter_root, fields);
+}
+
int dm_report_object(struct dm_report *rh, void *object)
{
struct field_properties *fp;
@@ -582,24 +1338,23 @@ int dm_report_object(struct dm_report *r
rh->keys_count))) {
log_error("dm_report_object: "
"row sort value structure allocation failed");
- return 0;
+ goto out_free_row;
}
list_init(&row->fields);
- list_add(&rh->rows, &row->list);
/* For each field to be displayed, call its report_fn */
list_iterate_items(fp, &rh->field_props) {
if (!(field = dm_pool_zalloc(rh->mem, sizeof(*field)))) {
log_error("dm_report_object: "
"struct dm_report_field allocation failed");
- return 0;
+ goto out_free_row;
}
field->props = fp;
data = _report_get_field_data(rh, fp, object);
if (!data)
- return 0;
+ goto out_free_row;
if (!rh->fields[fp->field_num].report_fn(rh, rh->mem,
field, data,
@@ -607,9 +1362,20 @@ int dm_report_object(struct dm_report *r
log_error("dm_report_object: "
"report function failed for field %s",
rh->fields[fp->field_num].id);
- return 0;
+ goto out_free_row;
}
+ list_add(&row->fields, &field->list);
+ }
+
+ /* Check filter and decide whether to display or not */
+ if (!_filter_object(rh, &row->fields))
+ goto out_free_row;
+
+ list_add(&rh->rows, &row->list);
+
+ /* For the object to be displayed, udpate width and record sort value */
+ list_iterate_items(field, &row->fields) {
if ((strlen(field->report_string) > field->props->width))
field->props->width = strlen(field->report_string);
@@ -617,13 +1383,16 @@ int dm_report_object(struct dm_report *r
(field->props->flags & FLD_SORT_KEY)) {
(*row->sort_fields)[field->props->sort_posn] = field;
}
- list_add(&row->fields, &field->list);
}
if (!(rh->flags & DM_REPORT_OUTPUT_BUFFERED))
return dm_report_output(rh);
return 1;
+
+ out_free_row:
+ dm_pool_free(rh->mem, row);
+ return 0;
}
/*
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (5/6) Add '--filter' option to dmsetup
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
` (3 preceding siblings ...)
2007-04-18 16:47 ` [PATCH] libdevmapper: (4/6) Add filtering feature to dm_report Jun'ichi Nomura
@ 2007-04-18 16:47 ` Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (6/6) Add deps and treenode fields for dmsetup info -c Jun'ichi Nomura
2007-04-18 19:23 ` [PATCH] libdevmapper: (7/6) Add dm_report_get_report_types() Jun'ichi Nomura
6 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:47 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 163 bytes --]
Hi,
This patch adds '--filter' option to dmsetup, so that it can
use the filtering feature of dm_report.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: dmsetup-info-add-filter-option.patch --]
[-- Type: text/x-patch, Size: 2289 bytes --]
Add --filter (-F) option to dmsetup for filtering report output.
---
dmsetup/dmsetup.c | 15 ++++++++++++++-
man/dmsetup.8 | 2 +-
2 files changed, 15 insertions(+), 2 deletions(-)
Index: device-mapper.work/dmsetup/dmsetup.c
===================================================================
--- device-mapper.work.orig/dmsetup/dmsetup.c
+++ device-mapper.work/dmsetup/dmsetup.c
@@ -104,6 +104,7 @@ enum {
COLS_ARG,
EXEC_ARG,
FORCE_ARG,
+ FILTER_ARG,
GID_ARG,
MAJOR_ARG,
MINOR_ARG,
@@ -1650,6 +1651,13 @@ static int _report_init(struct command *
options, separator, flags, keys, NULL)))
goto out;
+ if (_string_args[FILTER_ARG] &&
+ !dm_report_set_filter(_report, _string_args[FILTER_ARG], 0)) {
+ dm_report_free(_report);
+ _report = NULL;
+ goto out;
+ }
+
r = 1;
out:
@@ -2059,6 +2067,7 @@ static int _process_switches(int *argc,
{"columns", 0, &ind, COLS_ARG},
{"exec", 1, &ind, EXEC_ARG},
{"force", 0, &ind, FORCE_ARG},
+ {"filter", 1, &ind, FILTER_ARG},
{"gid", 1, &ind, GID_ARG},
{"major", 1, &ind, MAJOR_ARG},
{"minor", 1, &ind, MINOR_ARG},
@@ -2133,7 +2142,7 @@ static int _process_switches(int *argc,
optarg = 0;
optind = OPTIND_INIT;
- while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfGj:m:Mno:O:ru:Uv",
+ while ((ind = -1, c = GETOPTLONG_FN(*argc, *argv, "cCfF:Gj:m:Mno:O:ru:Uv",
long_options, NULL)) != -1) {
if (c == ':' || c == '?')
return 0;
@@ -2141,6 +2150,10 @@ static int _process_switches(int *argc,
_switches[COLS_ARG]++;
if (c == 'f' || ind == FORCE_ARG)
_switches[FORCE_ARG]++;
+ if (c == 'F' || ind == FILTER_ARG) {
+ _switches[FILTER_ARG]++;
+ _string_args[FILTER_ARG] = optarg;
+ }
if (c == 'r' || ind == READ_ONLY)
_switches[READ_ONLY]++;
if (c == 'j' || ind == MAJOR_ARG) {
Index: device-mapper.work/man/dmsetup.8
===================================================================
--- device-mapper.work.orig/man/dmsetup.8
+++ device-mapper.work/man/dmsetup.8
@@ -44,7 +44,7 @@ dmsetup \- low level logical volume mana
.br
.B dmsetup info -c|-C|--columns
.I [--noheadings] [--separator separator] [-o fields] [-O|--sort sort_fields]
-.I [device_name]
+.I [--filter expression] [device_name]
.br
.B dmsetup deps
.I [device_name]
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (6/6) Add deps and treenode fields for dmsetup info -c
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
` (4 preceding siblings ...)
2007-04-18 16:47 ` [PATCH] libdevmapper: (5/6) Add '--filter' option to dmsetup Jun'ichi Nomura
@ 2007-04-18 16:47 ` Jun'ichi Nomura
2007-04-18 19:23 ` [PATCH] libdevmapper: (7/6) Add dm_report_get_report_types() Jun'ichi Nomura
6 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 16:47 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 359 bytes --]
Hi,
This patch adds optional fields to 'dmsetup info -c'.
You can specify the following fields with '-o', '-O' and filtering option.
- deps
- deps_count
- parents
- parents_count
For example, you can find the top-level dm devices by:
# dmsetup info -c -o name --filter 'parents_count == 0'
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: dmsetup-info-add-deps-columns.patch --]
[-- Type: text/x-patch, Size: 10148 bytes --]
Add fields of deps and treenode to dmsetup
---
dmsetup/dmsetup.c | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 276 insertions(+), 7 deletions(-)
Index: device-mapper.work/dmsetup/dmsetup.c
===================================================================
--- device-mapper.work.orig/dmsetup/dmsetup.c
+++ device-mapper.work/dmsetup/dmsetup.c
@@ -241,11 +241,14 @@ static int _parse_file(struct dm_task *d
struct dmsetup_report_obj {
struct dm_task *task;
struct dm_info *info;
+ struct dm_task *deps;
+ struct dm_tree_node *tree;
};
static int _display_info_cols(struct dm_task *dmt, struct dm_info *info)
{
struct dmsetup_report_obj obj;
+ int r = 0;
if (!info->exists) {
fprintf(stderr, "Device does not exist.\n");
@@ -254,11 +257,18 @@ static int _display_info_cols(struct dm_
obj.task = dmt;
obj.info = info;
+ obj.deps = NULL; /* task is created later on demand */
+ obj.tree = NULL; /* deptree is created later on demand */
if (!dm_report_object(_report, &obj))
- return 0;
+ goto out;
- return 1;
+ r = 1;
+
+ out:
+ if (obj.deps)
+ dm_task_destroy(obj.deps);
+ return r;
}
static void _display_info_long(struct dm_task *dmt, struct dm_info *info)
@@ -1457,17 +1467,26 @@ static int _add_dep(int argc __attribute
/*
* Create and walk dependency tree
*/
-static int _tree(int argc, char **argv, void *data __attribute((unused)))
+static int _build_whole_deptree(void)
{
+ if (_dtree)
+ return 1;
+
if (!(_dtree = dm_tree_create()))
return 0;
- if (!_process_all(argc, argv, 0, _add_dep))
+ if (!_process_all(0, NULL, 0, _add_dep))
return 0;
- _tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
+ return 1;
+}
+
+static int _tree(int argc, char **argv, void *data __attribute((unused)))
+{
+ if (!_build_whole_deptree())
+ return 0;
- dm_tree_free(_dtree);
+ _tree_walk_children(dm_tree_find_node(_dtree, 0, 0), 0);
return 1;
}
@@ -1539,8 +1558,193 @@ static int _dm_info_status_disp(struct d
return dm_report_field_string(rh, field, &s);
}
+#define MAJ_MIN_LEN 32
+
+static int _dm_info_device_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private)
+{
+ char buf[MAJ_MIN_LEN], *repstr;
+ struct dm_info *info = (struct dm_info *) data;
+
+ if (!dm_pool_begin_object(mem, 8)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ info->major, info->minor) < 0) {
+ log_error("dm_pool_alloc failed");
+ goto out_abandon;
+ }
+
+ if (!dm_pool_grow_object(mem, buf, strlen(buf))) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_tree_parent_maps_disp(struct dm_report *rh,
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ struct dm_tree_node *node = (struct dm_tree_node *) data, *parent;
+ void *t = NULL;
+ const char *name;
+ int first_node = 1;
+ char *repstr;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ while ((parent = dm_tree_next_child(&t, node, 1))) {
+ name = dm_tree_node_get_name(parent);
+ if (!name || !*name)
+ continue;
+ if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (!dm_pool_grow_object(mem, name, strlen(name))) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (first_node)
+ first_node = 0;
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_tree_parents_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ struct dm_tree_node *node = (struct dm_tree_node *) data, *parent;
+ void *t = NULL;
+ const struct dm_info *info;
+ int first_node = 1;
+ char buf[MAJ_MIN_LEN], *repstr;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ while ((parent = dm_tree_next_child(&t, node, 1))) {
+ info = dm_tree_node_get_info(parent);
+ if (!info->major && !info->minor)
+ continue;
+ if (!first_node && !dm_pool_grow_object(mem, ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ info->major, info->minor) < 0) {
+ log_error("dm_snprintf failed");
+ goto out_abandon;
+ }
+ if (!dm_pool_grow_object(mem, buf, strlen(buf))) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (first_node)
+ first_node = 0;
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
+static int _dm_tree_parents_count_disp(struct dm_report *rh,
+ struct dm_pool *mem,
+ struct dm_report_field *field,
+ const void *data, void *private)
+{
+ struct dm_tree_node *node = (struct dm_tree_node *) data;
+ int num_parent = dm_tree_node_num_children(node, 1);
+
+ return dm_report_field_int(rh, field, &num_parent);
+}
+
+static int _dm_deps_disp(struct dm_report *rh, struct dm_pool *mem,
+ struct dm_report_field *field, const void *data,
+ void *private)
+{
+ struct dm_deps *deps = (struct dm_deps *) data;
+ int i;
+ char buf[MAJ_MIN_LEN], *repstr;
+
+ if (!dm_pool_begin_object(mem, 16)) {
+ log_error("dm_pool_begin_object failed");
+ return 0;
+ }
+
+ for (i = 0; i < deps->count; i++) {
+ if (dm_snprintf(buf, sizeof(buf), "%d:%d",
+ (int) MAJOR(deps->device[i]),
+ (int) MINOR(deps->device[i])) < 0) {
+ log_error("dm_snprintf failed");
+ goto out_abandon;
+ }
+ if (!dm_pool_grow_object(mem, buf, strlen(buf))) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ if (i + 1 < deps->count && !dm_pool_grow_object(mem, ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ goto out_abandon;
+ }
+
+ repstr = dm_pool_end_object(mem);
+ dm_report_field_set_value(field, repstr, repstr);
+ return 1;
+
+ out_abandon:
+ dm_pool_abandon_object(mem);
+ return 0;
+}
+
/* Report types */
-enum { DR_TASK = 1, DR_INFO = 2 };
+enum { DR_TASK = 1, DR_INFO = 2, DR_DEPS = 4, DR_TREE = 8 };
static void *_task_get_obj(void *obj)
{
@@ -1552,9 +1756,65 @@ static void *_info_get_obj(void *obj)
return ((struct dmsetup_report_obj *)obj)->info;
}
+static void *_tree_get_obj(void *obj)
+{
+ struct dmsetup_report_obj *o = (struct dmsetup_report_obj *) obj;
+ struct dm_info *info;
+
+ if (o->tree)
+ return o->tree;
+
+ if (!_build_whole_deptree())
+ return NULL;
+
+ if (!(info = _info_get_obj(o)))
+ return NULL;
+
+ o->tree = dm_tree_find_node(_dtree, info->major, info->minor);
+ return o->tree;
+}
+
+static void *_deps_get_obj(void *obj)
+{
+ struct dmsetup_report_obj *o = (struct dmsetup_report_obj *) obj;
+ struct dm_task *dmt;
+ const char *name;
+
+ if (o->deps)
+ return dm_task_get_deps(o->deps);
+
+ /* name */
+ if (!(dmt = _task_get_obj(o)))
+ return NULL;
+ if (!(name = dm_task_get_name(dmt)))
+ return NULL;
+
+ /* deps */
+ if (!(dmt = dm_task_create(DM_DEVICE_DEPS)))
+ return NULL;
+
+ if (!dm_task_set_name(dmt, name))
+ goto out;
+
+ if (_switches[NOOPENCOUNT_ARG] && !dm_task_no_open_count(dmt))
+ goto out;
+
+ if (!dm_task_run(dmt))
+ goto out;
+
+ o->deps = dmt;
+ return dm_task_get_deps(dmt);
+
+ out:
+ dm_task_destroy(dmt);
+ return NULL;
+}
+
static const struct dm_report_object_type _report_types[] = {
{ DR_TASK, "Mapped Device Name", "", _task_get_obj },
{ DR_INFO, "Mapped Device Information", "", _info_get_obj },
+ { DR_DEPS, "Mapped Device Dependency", "", _deps_get_obj },
+ { DR_TREE, "Mapped Device Dependency", "", _tree_get_obj },
{ 0, "", "", NULL },
};
@@ -1570,11 +1830,17 @@ static const struct dm_report_field_type
FIELD_F(TASK, STR, "Name", 16, dm_name, "name", "Name of mapped device.")
FIELD_F(TASK, STR, "UUID", 32, dm_uuid, "uuid", "Unique (optional) identifier for mapped device.")
FIELD_F(INFO, STR, "Stat", 4, dm_info_status, "attr", "(L)ive, (I)nactive, (s)uspended, (r)ead-only, read-(w)rite.")
+FIELD_F(INFO, STR, "Device", 6, dm_info_device, "device", "Device major and minor numbers")
FIELD_O(INFO, dm_info, NUM, "Maj", major, 3, int32, "major", "Block device major number.")
FIELD_O(INFO, dm_info, NUM, "Min", minor, 3, int32, "minor", "Block device minor number.")
FIELD_O(INFO, dm_info, NUM, "Open", open_count, 4, int32, "open", "Number of references to open device, if requested.")
FIELD_O(INFO, dm_info, NUM, "Targ", target_count, 4, int32, "segments", "Number of segments in live table, if present.")
FIELD_O(INFO, dm_info, NUM, "Event", event_nr, 6, uint32, "events", "Number of most recent event.")
+FIELD_O(DEPS, dm_deps, NUM, "Deps", count, 4, int32, "deps_count", "Number of underlying devices")
+FIELD_F(DEPS, STR, "Depend on", 10, dm_deps, "deps", "List of underlying devic es")
+FIELD_F(TREE, STR, "Parent Devices", 16, dm_tree_parents, "parents", "List of overlaying devices")
+FIELD_F(TREE, STR, "Parent Maps", 12, dm_tree_parent_maps, "parent_maps", "List of overlaying maps")
+FIELD_F(TREE, NUM, "Parents", 7, dm_tree_parents_count, "parents_count", "Number of overlaying devices")
{0, 0, 0, 0, "", "", NULL, NULL},
/* *INDENT-ON* */
};
@@ -2300,5 +2566,8 @@ out:
dm_report_free(_report);
}
+ if (_dtree)
+ dm_tree_free(_dtree);
+
return r;
}
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] libdevmapper: (7/6) Add dm_report_get_report_types()
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
` (5 preceding siblings ...)
2007-04-18 16:47 ` [PATCH] libdevmapper: (6/6) Add deps and treenode fields for dmsetup info -c Jun'ichi Nomura
@ 2007-04-18 19:23 ` Jun'ichi Nomura
6 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 19:23 UTC (permalink / raw)
To: device-mapper development, Alasdair Kergon
[-- Attachment #1: Type: text/plain, Size: 211 bytes --]
Hi,
This patch was missing from the last post...
Since rh->report_types can be updated after dm_report_init(),
we need an interface to obtain it later.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
[-- Attachment #2: libdm-report-get-types.patch --]
[-- Type: text/x-patch, Size: 2075 bytes --]
Add dm_report_get_report_types() to obtain updated report_types field
after dm_report_set_filter() call.
---
dmsetup/dmsetup.c | 1 +
lib/.exported_symbols | 1 +
lib/libdevmapper.h | 1 +
lib/libdm-report.c | 5 +++++
4 files changed, 8 insertions(+)
Index: device-mapper.work/lib/.exported_symbols
===================================================================
--- device-mapper.work.orig/lib/.exported_symbols
+++ device-mapper.work/lib/.exported_symbols
@@ -128,5 +128,6 @@ dm_report_field_uint32
dm_report_field_uint64
dm_report_field_set_value
dm_report_set_filter
+dm_report_get_report_types
dm_regex_create
dm_regex_match
Index: device-mapper.work/lib/libdm-report.c
===================================================================
--- device-mapper.work.orig/lib/libdm-report.c
+++ device-mapper.work/lib/libdm-report.c
@@ -88,6 +88,11 @@ struct row {
struct dm_report_field *(*sort_fields)[]; /* Fields in sort order */
};
+uint32_t dm_report_get_report_types(struct dm_report *rh)
+{
+ return rh->report_types;
+}
+
static const struct dm_report_object_type *_find_type(struct dm_report *rh,
uint32_t report_type)
{
Index: device-mapper.work/lib/libdevmapper.h
===================================================================
--- device-mapper.work.orig/lib/libdevmapper.h
+++ device-mapper.work/lib/libdevmapper.h
@@ -687,6 +687,7 @@ struct dm_report *dm_report_init(uint32_
int dm_report_object(struct dm_report *rh, void *object);
int dm_report_output(struct dm_report *rh);
void dm_report_free(struct dm_report *rh);
+uint32_t dm_report_get_report_types(struct dm_report *rh);
/* Set filter */
int dm_report_set_filter(struct dm_report *rh,
Index: device-mapper.work/dmsetup/dmsetup.c
===================================================================
--- device-mapper.work.orig/dmsetup/dmsetup.c
+++ device-mapper.work/dmsetup/dmsetup.c
@@ -1924,6 +1924,7 @@ static int _report_init(struct command *
goto out;
}
+ report_type = dm_report_get_report_types(_report);
r = 1;
out:
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] LVM2: (1/2) Use dm_regex
2007-04-18 16:47 ` [PATCH] libdevmapper: (3/6) Move lib/regex from LVM2 Jun'ichi Nomura
@ 2007-04-18 19:23 ` Jun'ichi Nomura
0 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 19:23 UTC (permalink / raw)
To: lvm-devel
Hi,
This patch removes lib/regex from LVM2 and uses dm_regex, instead.
Corresponding patch for libdevmapper is this:
[PATCH] libdevmapper: (3/6) Move lib/regex from LVM2
https://www.redhat.com/archives/dm-devel/2007-April/msg00028.html
No change in functionality.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
-------------- next part --------------
A non-text attachment was scrubbed...
Name: lvm2-remove-use-dm_regex.patch
Type: text/x-patch
Size: 23254 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/lvm-devel/attachments/20070418/dd0e3a38/attachment.bin>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH] LVM2: (2/2) Use dm_report filter
2007-04-18 16:47 ` [PATCH] libdevmapper: (4/6) Add filtering feature to dm_report Jun'ichi Nomura
@ 2007-04-18 19:32 ` Jun'ichi Nomura
0 siblings, 0 replies; 10+ messages in thread
From: Jun'ichi Nomura @ 2007-04-18 19:32 UTC (permalink / raw)
To: lvm-devel
Hi,
This patch adds '--filter' option to reporting commands: lvs, vgs and pvs.
The patch depends on the following patches:
[PATCH] libdevmapper: (4/6) Add filtering feature to dm_report
https://www.redhat.com/archives/dm-devel/2007-April/msg00029.html
[PATCH] libdevmapper: (7/6) Add dm_report_get_report_types()
https://www.redhat.com/archives/dm-devel/2007-April/msg00035.html
Examples:
Find PVs used for VG vg0
# pvs --filter 'vg_name == "vg0"'
Find PVs not used by any LVs
# pvs --filter 'used == 0'
Find PVs satisfying both of the aboves
# pvs --filter 'vg_name == "vg0" && used == 0'
Find PVs not satisfying the above
# pvs --filter '! (vg_name == "vg0" && used == 0)'
Find VGs with more than 2 PVs and with more than 400 extents
# vgs --filter 'pv_count > 2 && free_count > 400'
Find active snapshot LVs
# lvs --filter 'attr =~ /^s...a/'
Find LVs using /dev/sdc
# lvs --filter 'devices =~ |/dev/sdc|'
..
Possible future enhancements:
- Cope with units.
I.e. lvs --filter 'size > 4G' to find LVs larger than 4GB
Currently, the filter can handle only the raw value of the field.
Thanks,
--
Jun'ichi Nomura, NEC Corporation of America
-------------- next part --------------
A non-text attachment was scrubbed...
Name: lvm2-add-filter.patch
Type: text/x-patch
Size: 7144 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/lvm-devel/attachments/20070418/97908cf7/attachment.bin>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2007-04-18 19:32 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-18 16:41 [PATCH] libdevmapper: (0/6) Filtering feature in dm_report Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (1/6) Tidy up _field_match() and _key_match() Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (2/6) Fix trailing separator Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (3/6) Move lib/regex from LVM2 Jun'ichi Nomura
2007-04-18 19:23 ` [PATCH] LVM2: (1/2) Use dm_regex Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (4/6) Add filtering feature to dm_report Jun'ichi Nomura
2007-04-18 19:32 ` [PATCH] LVM2: (2/2) Use dm_report filter Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (5/6) Add '--filter' option to dmsetup Jun'ichi Nomura
2007-04-18 16:47 ` [PATCH] libdevmapper: (6/6) Add deps and treenode fields for dmsetup info -c Jun'ichi Nomura
2007-04-18 19:23 ` [PATCH] libdevmapper: (7/6) Add dm_report_get_report_types() Jun'ichi Nomura
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.