1 /*
2 * SysDB - src/core/store_lookup.c
3 * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 /*
29 * This module implements operators which may be used to select contents of
30 * the store by matching various attributes of the stored objects. For now, a
31 * simple full table scan is supported only.
32 */
34 #if HAVE_CONFIG_H
35 # include "config.h"
36 #endif /* HAVE_CONFIG_H */
38 #include "sysdb.h"
39 #include "core/store-private.h"
40 #include "core/object.h"
42 #include <assert.h>
44 #include <sys/types.h>
45 #include <regex.h>
47 #include <stdlib.h>
48 #include <string.h>
50 /*
51 * private data types
52 */
54 typedef struct {
55 sdb_store_matcher_t *m;
56 sdb_store_lookup_cb cb;
57 void *user_data;
58 } lookup_iter_data_t;
60 /*
61 * private helper functions
62 */
64 static int
65 lookup_iter(sdb_store_base_t *obj, void *user_data)
66 {
67 lookup_iter_data_t *d = user_data;
69 if (sdb_store_matcher_matches(d->m, obj))
70 return d->cb(obj, d->user_data);
71 return 0;
72 } /* lookup_iter */
74 /*
75 * matcher implementations
76 */
78 static int
79 match_string(string_matcher_t *m, const char *name)
80 {
81 if ((! m->name) && (! m->name_re))
82 return 1;
84 if (! name)
85 name = "";
87 if (m->name && strcasecmp(m->name, name))
88 return 0;
89 if (m->name_re && regexec(m->name_re, name,
90 /* matches */ 0, NULL, /* flags = */ 0))
91 return 0;
92 return 1;
93 } /* match_string */
95 static int
96 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj)
97 {
98 int status;
100 assert((m->type == MATCHER_AND) || (m->type == MATCHER_OR));
101 assert(OP_M(m)->left && OP_M(m)->right);
103 status = sdb_store_matcher_matches(OP_M(m)->left, obj);
104 /* lazy evaluation */
105 if ((! status) && (m->type == MATCHER_AND))
106 return status;
107 else if (status && (m->type == MATCHER_OR))
108 return status;
110 return sdb_store_matcher_matches(OP_M(m)->right, obj);
111 } /* match_logical */
113 static int
114 match_unary(sdb_store_matcher_t *m, sdb_store_base_t *obj)
115 {
116 assert(m->type == MATCHER_NOT);
117 assert(UOP_M(m)->op);
119 return !sdb_store_matcher_matches(UOP_M(m)->op, obj);
120 } /* match_unary */
122 static int
123 match_name(sdb_store_matcher_t *m, sdb_store_base_t *obj)
124 {
125 sdb_llist_iter_t *iter = NULL;
126 int status = 0;
128 assert(m->type == MATCHER_NAME);
130 switch (NAME_M(m)->obj_type) {
131 case SDB_HOST:
132 return match_string(&NAME_M(m)->name, obj->super.name);
133 break;
134 case SDB_SERVICE:
135 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->children);
136 break;
137 case SDB_ATTRIBUTE:
138 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
139 break;
140 }
142 while (sdb_llist_iter_has_next(iter)) {
143 sdb_store_base_t *child = STORE_BASE(sdb_llist_iter_get_next(iter));
144 if (match_string(&NAME_M(m)->name, child->super.name)) {
145 status = 1;
146 break;
147 }
148 }
149 sdb_llist_iter_destroy(iter);
150 return status;
151 } /* match_name */
153 static int
154 match_attr(sdb_store_matcher_t *m, sdb_store_base_t *obj)
155 {
156 sdb_llist_iter_t *iter = NULL;
157 int status = 0;
159 assert(m->type == MATCHER_ATTR);
160 assert(ATTR_M(m)->name);
162 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
163 while (sdb_llist_iter_has_next(iter)) {
164 sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(iter));
165 char buf[sdb_data_strlen(&attr->value) + 1];
167 if (strcasecmp(ATTR_M(m)->name, SDB_OBJ(attr)->name))
168 continue;
170 if (sdb_data_format(&attr->value, buf, sizeof(buf), SDB_UNQUOTED) <= 0)
171 return 0;
172 if (match_string(&ATTR_M(m)->value, buf)) {
173 status = 1;
174 break;
175 }
176 }
177 sdb_llist_iter_destroy(iter);
178 return status;
179 } /* match_attr */
181 typedef int (*matcher_cb)(sdb_store_matcher_t *, sdb_store_base_t *);
183 /* this array needs to be indexable by the matcher types;
184 * -> update the enum in store-private.h when updating this */
185 static matcher_cb matchers[] = {
186 match_logical,
187 match_logical,
188 match_unary,
189 match_name,
190 match_attr,
191 };
193 /*
194 * private matcher types
195 */
197 /* initializes a string matcher consuming two elements from ap */
198 static int
199 string_matcher_init(string_matcher_t *m, va_list ap)
200 {
201 const char *name = va_arg(ap, const char *);
202 const char *name_re = va_arg(ap, const char *);
204 if (name) {
205 m->name = strdup(name);
206 if (! m->name)
207 return -1;
208 }
209 if (name_re) {
210 m->name_re = malloc(sizeof(*m->name_re));
211 if (! m->name_re)
212 return -1;
213 if (regcomp(m->name_re, name_re, REG_EXTENDED | REG_ICASE | REG_NOSUB))
214 return -1;
215 }
216 return 0;
217 } /* string_matcher_init */
219 static void
220 string_matcher_destroy(string_matcher_t *m)
221 {
222 if (m->name)
223 free(m->name);
224 if (m->name_re) {
225 regfree(m->name_re);
226 free(m->name_re);
227 }
228 } /* string_matcher_destroy */
230 static char *
231 string_tostring(string_matcher_t *m, char *buf, size_t buflen)
232 {
233 snprintf(buf, buflen, "{ %s%s%s, %p }",
234 m->name ? "'" : "", m->name ? m->name : "NULL", m->name ? "'" : "",
235 m->name_re);
236 return buf;
237 } /* string_tostring */
239 /* initializes a name matcher */
240 static int
241 name_matcher_init(sdb_object_t *obj, va_list ap)
242 {
243 name_matcher_t *m = NAME_M(obj);
244 M(obj)->type = MATCHER_NAME;
245 return string_matcher_init(&m->name, ap);
246 } /* name_matcher_init */
248 static void
249 name_matcher_destroy(sdb_object_t *obj)
250 {
251 name_matcher_t *m = NAME_M(obj);
252 string_matcher_destroy(&m->name);
253 } /* name_matcher_destroy */
255 static char *
256 name_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
257 {
258 char name[buflen + 1];
259 assert(m->type == MATCHER_NAME);
260 snprintf(buf, buflen, "OBJ[%s]{ NAME%s }",
261 SDB_STORE_TYPE_TO_NAME(NAME_M(m)->obj_type),
262 string_tostring(&NAME_M(m)->name, name, sizeof(name)));
263 return buf;
264 } /* name_tostring */
266 static int
267 attr_matcher_init(sdb_object_t *obj, va_list ap)
268 {
269 attr_matcher_t *attr = ATTR_M(obj);
270 const char *name = va_arg(ap, const char *);
272 M(obj)->type = MATCHER_ATTR;
273 if (name) {
274 attr->name = strdup(name);
275 if (! attr->name)
276 return -1;
277 }
278 return string_matcher_init(&attr->value, ap);
279 } /* attr_matcher_init */
281 static void
282 attr_matcher_destroy(sdb_object_t *obj)
283 {
284 attr_matcher_t *attr = ATTR_M(obj);
285 if (attr->name)
286 free(attr->name);
287 attr->name = NULL;
288 string_matcher_destroy(&attr->value);
289 } /* attr_matcher_destroy */
291 static char *
292 attr_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
293 {
294 char value[buflen + 1];
296 if (! m) {
297 snprintf(buf, buflen, "ATTR{}");
298 return buf;
299 }
301 assert(m->type == MATCHER_ATTR);
302 snprintf(buf, buflen, "ATTR[%s]{ VALUE%s }", ATTR_M(m)->name,
303 string_tostring(&ATTR_M(m)->value, value, sizeof(value)));
304 return buf;
305 } /* attr_tostring */
307 static int
308 op_matcher_init(sdb_object_t *obj, va_list ap)
309 {
310 M(obj)->type = va_arg(ap, int);
311 if ((M(obj)->type != MATCHER_OR) && (M(obj)->type != MATCHER_AND))
312 return -1;
314 OP_M(obj)->left = va_arg(ap, sdb_store_matcher_t *);
315 sdb_object_ref(SDB_OBJ(OP_M(obj)->left));
316 OP_M(obj)->right = va_arg(ap, sdb_store_matcher_t *);
317 sdb_object_ref(SDB_OBJ(OP_M(obj)->right));
319 if ((! OP_M(obj)->left) || (! OP_M(obj)->right))
320 return -1;
321 return 0;
322 } /* op_matcher_init */
324 static void
325 op_matcher_destroy(sdb_object_t *obj)
326 {
327 if (OP_M(obj)->left)
328 sdb_object_deref(SDB_OBJ(OP_M(obj)->left));
329 if (OP_M(obj)->right)
330 sdb_object_deref(SDB_OBJ(OP_M(obj)->right));
331 } /* op_matcher_destroy */
333 static char *
334 op_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
335 {
336 char left[buflen + 1], right[buflen + 1];
338 if (! m) {
339 /* this should not happen */
340 snprintf(buf, buflen, "()");
341 return buf;
342 }
344 assert((m->type == MATCHER_OR) || (m->type == MATCHER_AND));
345 snprintf(buf, buflen, "(%s, %s, %s)",
346 m->type == MATCHER_OR ? "OR" : "AND",
347 sdb_store_matcher_tostring(OP_M(m)->left, left, sizeof(left)),
348 sdb_store_matcher_tostring(OP_M(m)->right, right, sizeof(right)));
349 return buf;
350 } /* op_tostring */
352 static int
353 uop_matcher_init(sdb_object_t *obj, va_list ap)
354 {
355 M(obj)->type = va_arg(ap, int);
356 if (M(obj)->type != MATCHER_NOT)
357 return -1;
359 UOP_M(obj)->op = va_arg(ap, sdb_store_matcher_t *);
360 sdb_object_ref(SDB_OBJ(UOP_M(obj)->op));
362 if (! UOP_M(obj)->op)
363 return -1;
364 return 0;
365 } /* uop_matcher_init */
367 static void
368 uop_matcher_destroy(sdb_object_t *obj)
369 {
370 if (UOP_M(obj)->op)
371 sdb_object_deref(SDB_OBJ(UOP_M(obj)->op));
372 } /* uop_matcher_destroy */
374 static char *
375 uop_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
376 {
377 char op[buflen + 1];
379 if (! m) {
380 /* this should not happen */
381 snprintf(buf, buflen, "()");
382 return buf;
383 }
385 assert(m->type == MATCHER_NOT);
386 snprintf(buf, buflen, "(NOT, %s)",
387 sdb_store_matcher_tostring(UOP_M(m)->op, op, sizeof(op)));
388 return buf;
389 } /* uop_tostring */
391 static sdb_type_t name_type = {
392 /* size = */ sizeof(name_matcher_t),
393 /* init = */ name_matcher_init,
394 /* destroy = */ name_matcher_destroy,
395 };
397 static sdb_type_t attr_type = {
398 /* size = */ sizeof(attr_matcher_t),
399 /* init = */ attr_matcher_init,
400 /* destroy = */ attr_matcher_destroy,
401 };
403 static sdb_type_t op_type = {
404 /* size = */ sizeof(op_matcher_t),
405 /* init = */ op_matcher_init,
406 /* destroy = */ op_matcher_destroy,
407 };
409 static sdb_type_t uop_type = {
410 /* size = */ sizeof(uop_matcher_t),
411 /* init = */ uop_matcher_init,
412 /* destroy = */ uop_matcher_destroy,
413 };
415 typedef char *(*matcher_tostring_cb)(sdb_store_matcher_t *, char *, size_t);
417 /* this array needs to be indexable by the matcher types;
418 * -> update the enum in store-private.h when updating this */
419 static matcher_tostring_cb matchers_tostring[] = {
420 op_tostring,
421 op_tostring,
422 uop_tostring,
423 name_tostring,
424 attr_tostring,
425 };
427 /*
428 * public API
429 */
431 sdb_store_matcher_t *
432 sdb_store_name_matcher(int type, const char *name, _Bool re)
433 {
434 sdb_store_matcher_t *m;
436 if (re)
437 m = M(sdb_object_create("name-matcher", name_type,
438 NULL, name));
439 else
440 m = M(sdb_object_create("name-matcher", name_type,
441 name, NULL));
443 if (! m)
444 return NULL;
446 NAME_M(m)->obj_type = type;
447 return m;
448 } /* sdb_store_name_matcher */
450 sdb_store_matcher_t *
451 sdb_store_attr_matcher(const char *name, const char *value, _Bool re)
452 {
453 if (! name)
454 return NULL;
456 if (re)
457 return M(sdb_object_create("attr-matcher", attr_type,
458 name, NULL, value));
459 return M(sdb_object_create("attr-matcher", attr_type,
460 name, value, NULL));
461 } /* sdb_store_attr_matcher */
463 sdb_store_matcher_t *
464 sdb_store_matcher_parse_cmp(const char *obj_type, const char *attr,
465 const char *op, const char *value)
466 {
467 int type = -1;
468 _Bool inv = 0;
469 _Bool re = 0;
471 sdb_store_matcher_t *m = NULL;
473 if (! strcasecmp(obj_type, "host"))
474 type = SDB_HOST;
475 else if (! strcasecmp(obj_type, "service"))
476 type = SDB_SERVICE;
477 else if (! strcasecmp(obj_type, "attribute"))
478 type = SDB_ATTRIBUTE;
480 /* TODO: support other operators */
481 if (! strcasecmp(op, "=")) {
482 /* nothing to do */
483 }
484 else if (! strcasecmp(op, "!=")) {
485 inv = 1;
486 }
487 else if (! strcasecmp(op, "=~")) {
488 re = 1;
489 }
490 else if (! strcasecmp(op, "!~")) {
491 inv = 1;
492 re = 1;
493 }
494 else
495 return NULL;
497 if (! strcasecmp(attr, "name"))
498 m = sdb_store_name_matcher(type, value, re);
499 else if (type == SDB_ATTRIBUTE)
500 m = sdb_store_attr_matcher(attr, value, re);
502 if (! m)
503 return NULL;
505 if (inv) {
506 sdb_store_matcher_t *tmp;
507 tmp = sdb_store_inv_matcher(m);
508 /* pass ownership to the inverse matcher */
509 sdb_object_deref(SDB_OBJ(m));
510 m = tmp;
511 }
512 return m;
513 } /* sdb_store_matcher_parse_cmp */
515 sdb_store_matcher_t *
516 sdb_store_dis_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
517 {
518 return M(sdb_object_create("dis-matcher", op_type, MATCHER_OR,
519 left, right));
520 } /* sdb_store_dis_matcher */
522 sdb_store_matcher_t *
523 sdb_store_con_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
524 {
525 return M(sdb_object_create("con-matcher", op_type, MATCHER_AND,
526 left, right));
527 } /* sdb_store_con_matcher */
529 sdb_store_matcher_t *
530 sdb_store_inv_matcher(sdb_store_matcher_t *m)
531 {
532 return M(sdb_object_create("inv-matcher", uop_type, MATCHER_NOT, m));
533 } /* sdb_store_inv_matcher */
535 int
536 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
537 {
538 if (obj->type != SDB_HOST)
539 return 0;
541 /* "NULL" always matches */
542 if ((! m) || (! obj))
543 return 1;
545 if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers)))
546 return 0;
548 return matchers[m->type](m, obj);
549 } /* sdb_store_matcher_matches */
551 char *
552 sdb_store_matcher_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
553 {
554 if (! m)
555 return NULL;
557 if ((m->type < 0)
558 || (((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers_tostring))))
559 return NULL;
560 return matchers_tostring[m->type](m, buf, buflen);
561 } /* sdb_store_matcher_tostring */
563 int
564 sdb_store_lookup(sdb_store_matcher_t *m, sdb_store_lookup_cb cb,
565 void *user_data)
566 {
567 lookup_iter_data_t data = { m, cb, user_data };
569 if (! cb)
570 return -1;
571 return sdb_store_iterate(lookup_iter, &data);
572 } /* sdb_store_lookup */
574 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */