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_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj);
80 static int
81 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj);
83 /* specific matchers */
85 static int
86 match_name(name_matcher_t *m, const char *name)
87 {
88 assert(m);
90 if ((! m->name) && (! m->name_re))
91 return 1;
93 if (! name)
94 name = "";
96 if (m->name && strcasecmp(m->name, name))
97 return 0;
98 if (m->name_re && regexec(m->name_re, name,
99 /* matches */ 0, NULL, /* flags = */ 0))
100 return 0;
101 return 1;
102 } /* match_name */
104 /* match attribute specific values;
105 * always call this function through match_obj() */
106 static int
107 match_attr(attr_matcher_t *m, sdb_store_base_t *obj)
108 {
109 assert(m && obj);
111 if (obj->type != SDB_ATTRIBUTE)
112 return 0;
114 {
115 sdb_attribute_t *attr = SDB_ATTR(obj);
116 char buf[sdb_data_strlen(&attr->value) + 1];
118 if (sdb_data_format(&attr->value, buf, sizeof(buf), SDB_UNQUOTED) <= 0)
119 return 0;
120 return match_name(&m->value, buf);
121 }
122 } /* match_attr */
124 /* match service specific values;
125 * always call this function through match_obj() */
126 static int
127 match_service(service_matcher_t *m, sdb_store_base_t *obj)
128 {
129 sdb_llist_iter_t *iter;
131 assert(m && obj);
133 if (obj->type != SDB_SERVICE)
134 return 0;
136 if (! m->attr)
137 return 1;
139 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
140 while (sdb_llist_iter_has_next(iter)) {
141 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
143 /* if any of the attributes matches we found a matching service */
144 if (match_obj(M(m->attr), attr)) {
145 sdb_llist_iter_destroy(iter);
146 return 1;
147 }
148 }
149 sdb_llist_iter_destroy(iter);
150 return 0;
151 } /* match_service */
153 /* match host specific values;
154 * always call this function through match_obj() */
155 static int
156 match_host(host_matcher_t *m, sdb_store_base_t *obj)
157 {
158 sdb_llist_iter_t *iter;
159 int status;
161 assert(m && obj);
163 if (obj->type != SDB_HOST)
164 return 0;
166 if (m->service) {
167 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->children);
168 status = 0;
169 }
170 else {
171 iter = NULL;
172 status = 1;
173 }
174 while (sdb_llist_iter_has_next(iter)) {
175 sdb_store_base_t *service = STORE_BASE(sdb_llist_iter_get_next(iter));
177 /* found a matching service */
178 if (match_obj(M(m->service), service)) {
179 status = 1;
180 break;
181 }
182 }
183 sdb_llist_iter_destroy(iter);
185 if (! status)
186 return status;
187 else if (! m->attr)
188 return 1;
190 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
191 while (sdb_llist_iter_has_next(iter)) {
192 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
194 /* if any attribute matches, we found a matching host */
195 if (match_obj(M(m->attr), attr)) {
196 sdb_llist_iter_destroy(iter);
197 return 1;
198 }
199 }
200 sdb_llist_iter_destroy(iter);
201 return 0;
202 } /* match_host */
204 /* generic matchers */
206 typedef int (*matcher_cb)(sdb_store_matcher_t *, sdb_store_base_t *);
208 /* this array needs to be indexable by the matcher types;
209 * -> update the enum in store-private.h when updating this */
210 static matcher_cb matchers[] = {
211 match_logical,
212 match_logical,
213 match_obj,
214 match_obj,
215 match_obj,
216 };
218 static int
219 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj)
220 {
221 int status;
223 assert(m && obj);
224 assert(OP_M(m)->left && OP_M(m)->right);
226 status = sdb_store_matcher_matches(OP_M(m)->left, obj);
227 /* lazy evaluation */
228 if ((! status) && (m->type == MATCHER_AND))
229 return status;
230 else if (status && (m->type == MATCHER_OR))
231 return status;
233 return sdb_store_matcher_matches(OP_M(m)->right, obj);
234 } /* match_logical */
236 static int
237 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj)
238 {
239 int status;
241 assert(m && obj);
243 status = match_name(&OBJ_M(m)->name, obj->super.name);
244 if (! status)
245 return status;
247 switch (m->type) {
248 case MATCHER_ATTR:
249 return match_attr(ATTR_M(m), obj);
250 break;
251 case MATCHER_SERVICE:
252 return match_service(SERVICE_M(m), obj);
253 break;
254 case MATCHER_HOST:
255 return match_host(HOST_M(m), obj);
256 break;
257 }
258 return 0;
259 } /* match_obj */
261 /*
262 * private matcher types
263 */
265 /* initializes a name matcher consuming two elements from ap */
266 static int
267 name_matcher_init(name_matcher_t *m, va_list ap)
268 {
269 const char *name = va_arg(ap, const char *);
270 const char *name_re = va_arg(ap, const char *);
272 if (name) {
273 m->name = strdup(name);
274 if (! m->name)
275 return -1;
276 }
277 if (name_re) {
278 m->name_re = malloc(sizeof(*m->name_re));
279 if (! m->name_re)
280 return -1;
281 if (regcomp(m->name_re, name_re, REG_EXTENDED | REG_ICASE | REG_NOSUB))
282 return -1;
283 }
284 return 0;
285 } /* name_matcher_init */
287 static void
288 name_matcher_destroy(name_matcher_t *m)
289 {
290 if (m->name)
291 free(m->name);
292 if (m->name_re) {
293 regfree(m->name_re);
294 free(m->name_re);
295 }
296 } /* name_matcher_destroy */
298 /* initializes an object matcher consuming two elements from ap */
299 static int
300 obj_matcher_init(sdb_object_t *obj, va_list ap)
301 {
302 obj_matcher_t *m = OBJ_M(obj);
303 return name_matcher_init(&m->name, ap);
304 } /* obj_matcher_init */
306 static void
307 obj_matcher_destroy(sdb_object_t *obj)
308 {
309 obj_matcher_t *m = OBJ_M(obj);
310 name_matcher_destroy(&m->name);
311 } /* obj_matcher_destroy */
313 static int
314 attr_matcher_init(sdb_object_t *obj, va_list ap)
315 {
316 attr_matcher_t *attr = ATTR_M(obj);
317 int status;
319 M(obj)->type = MATCHER_ATTR;
321 status = obj_matcher_init(obj, ap);
322 if (! status)
323 status = name_matcher_init(&attr->value, ap);
324 return status;
325 } /* attr_matcher_init */
327 static void
328 attr_matcher_destroy(sdb_object_t *obj)
329 {
330 attr_matcher_t *attr = ATTR_M(obj);
332 obj_matcher_destroy(obj);
333 name_matcher_destroy(&attr->value);
334 } /* attr_matcher_destroy */
336 static int
337 service_matcher_init(sdb_object_t *obj, va_list ap)
338 {
339 attr_matcher_t *attr;
340 int status;
342 M(obj)->type = MATCHER_SERVICE;
344 status = obj_matcher_init(obj, ap);
345 if (status)
346 return status;
348 attr = va_arg(ap, attr_matcher_t *);
350 sdb_object_ref(SDB_OBJ(attr));
351 SERVICE_M(obj)->attr = attr;
352 return 0;
353 } /* service_matcher_init */
355 static void
356 service_matcher_destroy(sdb_object_t *obj)
357 {
358 obj_matcher_destroy(obj);
359 sdb_object_deref(SDB_OBJ(SERVICE_M(obj)->attr));
360 } /* service_matcher_destroy */
362 static int
363 host_matcher_init(sdb_object_t *obj, va_list ap)
364 {
365 service_matcher_t *service;
366 attr_matcher_t *attr;
367 int status;
369 M(obj)->type = MATCHER_HOST;
371 status = obj_matcher_init(obj, ap);
372 if (status)
373 return status;
375 service = va_arg(ap, service_matcher_t *);
376 attr = va_arg(ap, attr_matcher_t *);
378 sdb_object_ref(SDB_OBJ(service));
379 HOST_M(obj)->service = service;
380 sdb_object_ref(SDB_OBJ(attr));
381 HOST_M(obj)->attr = attr;
382 return 0;
383 } /* host_matcher_init */
385 static void
386 host_matcher_destroy(sdb_object_t *obj)
387 {
388 obj_matcher_destroy(obj);
389 sdb_object_deref(SDB_OBJ(HOST_M(obj)->service));
390 sdb_object_deref(SDB_OBJ(HOST_M(obj)->attr));
391 } /* host_matcher_destroy */
393 static int
394 op_matcher_init(sdb_object_t *obj, va_list ap)
395 {
396 M(obj)->type = va_arg(ap, int);
397 if ((M(obj)->type != MATCHER_OR) && (M(obj)->type != MATCHER_AND))
398 return -1;
400 OP_M(obj)->left = va_arg(ap, sdb_store_matcher_t *);
401 sdb_object_ref(SDB_OBJ(OP_M(obj)->left));
402 OP_M(obj)->right = va_arg(ap, sdb_store_matcher_t *);
403 sdb_object_ref(SDB_OBJ(OP_M(obj)->right));
405 if ((! OP_M(obj)->left) || (! OP_M(obj)->right))
406 return -1;
407 return 0;
408 } /* op_matcher_init */
410 static void
411 op_matcher_destroy(sdb_object_t *obj)
412 {
413 if (OP_M(obj)->left)
414 sdb_object_deref(SDB_OBJ(OP_M(obj)->left));
415 if (OP_M(obj)->right)
416 sdb_object_deref(SDB_OBJ(OP_M(obj)->right));
417 } /* op_matcher_destroy */
419 static sdb_type_t attr_type = {
420 /* size = */ sizeof(attr_matcher_t),
421 /* init = */ attr_matcher_init,
422 /* destroy = */ attr_matcher_destroy,
423 };
425 static sdb_type_t service_type = {
426 /* size = */ sizeof(service_matcher_t),
427 /* init = */ service_matcher_init,
428 /* destroy = */ service_matcher_destroy,
429 };
431 static sdb_type_t host_type = {
432 /* size = */ sizeof(host_matcher_t),
433 /* init = */ host_matcher_init,
434 /* destroy = */ host_matcher_destroy,
435 };
437 static sdb_type_t op_type = {
438 /* size = */ sizeof(op_matcher_t),
439 /* init = */ op_matcher_init,
440 /* destroy = */ op_matcher_destroy,
441 };
443 /*
444 * public API
445 */
447 sdb_store_matcher_t *
448 sdb_store_attr_matcher(const char *attr_name, const char *attr_name_re,
449 const char *attr_value, const char *attr_value_re)
450 {
451 return M(sdb_object_create("attr-matcher", attr_type,
452 attr_name, attr_name_re, attr_value, attr_value_re));
453 } /* sdb_store_attr_matcher */
455 sdb_store_matcher_t *
456 sdb_store_service_matcher(const char *service_name, const char *service_name_re,
457 sdb_store_matcher_t *attr_matcher)
458 {
459 return M(sdb_object_create("service-matcher", service_type,
460 service_name, service_name_re, attr_matcher));
461 } /* sdb_store_service_matcher */
463 sdb_store_matcher_t *
464 sdb_store_host_matcher(const char *host_name, const char *host_name_re,
465 sdb_store_matcher_t *service_matcher,
466 sdb_store_matcher_t *attr_matcher)
467 {
468 return M(sdb_object_create("host-matcher", host_type,
469 host_name, host_name_re, service_matcher, attr_matcher));
470 } /* sdb_store_host_matcher */
472 sdb_store_matcher_t *
473 sdb_store_matcher_parse_cmp(const char *obj_type, const char *attr,
474 const char *op, const char *value)
475 {
476 int typ = -1;
478 const char *matcher = NULL;
479 const char *matcher_re = NULL;
481 if (! strcasecmp(obj_type, "host"))
482 typ = SDB_HOST;
483 else if (! strcasecmp(obj_type, "service"))
484 typ = SDB_SERVICE;
485 else if (! strcasecmp(obj_type, "attribute"))
486 typ = SDB_ATTRIBUTE;
488 /* TODO: support other operators */
489 if (! strcasecmp(op, "="))
490 matcher = value;
491 else if (! strcasecmp(op, "=~"))
492 matcher_re = value;
493 else
494 return NULL;
496 if (! strcasecmp(attr, "name")) {
497 /* accept */
498 }
499 else if (typ == SDB_ATTRIBUTE)
500 return sdb_store_attr_matcher(attr, NULL, matcher, matcher_re);
501 else
502 return NULL;
504 if (typ == SDB_HOST)
505 return sdb_store_host_matcher(matcher, matcher_re, NULL, NULL);
506 else if (typ == SDB_SERVICE)
507 return sdb_store_service_matcher(matcher, matcher_re, NULL);
508 else if (typ == SDB_ATTRIBUTE)
509 return sdb_store_attr_matcher(matcher, matcher_re, NULL, NULL);
510 return NULL;
511 } /* sdb_store_matcher_parse_cmp */
513 sdb_store_matcher_t *
514 sdb_store_dis_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
515 {
516 return M(sdb_object_create("dis-matcher", op_type, MATCHER_OR,
517 left, right));
518 } /* sdb_store_dis_matcher */
520 sdb_store_matcher_t *
521 sdb_store_con_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
522 {
523 return M(sdb_object_create("con-matcher", op_type, MATCHER_AND,
524 left, right));
525 } /* sdb_store_con_matcher */
527 int
528 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
529 {
530 /* "NULL" always matches */
531 if ((! m) || (! obj))
532 return 1;
534 if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers)))
535 return 0;
537 return matchers[m->type](m, obj);
538 } /* sdb_store_matcher_matches */
540 int
541 sdb_store_lookup(sdb_store_matcher_t *m, sdb_store_lookup_cb cb,
542 void *user_data)
543 {
544 lookup_iter_data_t data = { m, cb, user_data };
546 if (! cb)
547 return -1;
548 return sdb_store_iterate(lookup_iter, &data);
549 } /* sdb_store_lookup */
551 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */