35c53e5b0b84c9818ac11a7a4004483a4ff27ac3
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 /* match the name of something */
55 typedef struct {
56 char *name;
57 regex_t *name_re;
58 } name_matcher_t;
60 /* matcher base type */
61 struct sdb_store_matcher {
62 sdb_object_t super;
63 /* type of the matcher */
64 int type;
65 };
66 #define M(m) ((sdb_store_matcher_t *)(m))
68 /* logical operator matcher */
69 typedef struct {
70 sdb_store_matcher_t super;
72 /* left and right hand operands */
73 sdb_store_matcher_t *left;
74 sdb_store_matcher_t *right;
75 } op_matcher_t;
76 #define OP_M(m) ((op_matcher_t *)(m))
78 /* match any type of object by it's base information */
79 typedef struct {
80 sdb_store_matcher_t super;
82 /* match by the name of the object */
83 name_matcher_t name;
84 } obj_matcher_t;
85 #define OBJ_M(m) ((obj_matcher_t *)(m))
87 /* match attributes */
88 typedef struct {
89 obj_matcher_t super;
90 /* XXX: this needs to be more flexible;
91 * add support for type-specific operators */
92 name_matcher_t value;
93 } attr_matcher_t;
94 #define ATTR_M(m) ((attr_matcher_t *)(m))
96 /* match services */
97 typedef struct {
98 obj_matcher_t super;
99 /* match by attributes assigned to the service */
100 attr_matcher_t *attr;
101 } service_matcher_t;
102 #define SERVICE_M(m) ((service_matcher_t *)(m))
104 /* match hosts */
105 typedef struct {
106 obj_matcher_t super;
107 /* match by services assigned to the host */
108 service_matcher_t *service;
109 /* match by attributes assigned to the host */
110 attr_matcher_t *attr;
111 } host_matcher_t;
112 #define HOST_M(m) ((host_matcher_t *)(m))
114 typedef struct {
115 sdb_store_matcher_t *m;
116 sdb_store_lookup_cb cb;
117 void *user_data;
118 } lookup_iter_data_t;
120 /*
121 * private helper functions
122 */
124 static int
125 lookup_iter(sdb_store_base_t *obj, void *user_data)
126 {
127 lookup_iter_data_t *d = user_data;
129 if (! sdb_store_matcher_matches(d->m, obj))
130 return d->cb(obj, d->user_data);
131 return 0;
132 } /* lookup_iter */
134 /*
135 * matcher implementations
136 */
138 static int
139 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj);
140 static int
141 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj);
143 /* specific matchers */
145 static int
146 match_name(name_matcher_t *m, const char *name)
147 {
148 assert(m);
150 if ((! m->name) && (! m->name_re))
151 return 0;
153 if (! name)
154 name = "";
156 if (m->name && strcasecmp(m->name, name))
157 return -1;
158 if (m->name_re && regexec(m->name_re, name,
159 /* matches */ 0, NULL, /* flags = */ 0))
160 return -1;
161 return 0;
162 } /* match_name */
164 /* match attribute specific values;
165 * always call this function through match_obj() */
166 static int
167 match_attr(attr_matcher_t *m, sdb_store_base_t *obj)
168 {
169 assert(m && obj);
171 if (obj->type != SDB_ATTRIBUTE)
172 return -1;
174 {
175 sdb_attribute_t *attr = SDB_ATTR(obj);
176 char buf[sdb_data_strlen(&attr->value) + 1];
178 if (sdb_data_format(&attr->value, buf, sizeof(buf), SDB_UNQUOTED) <= 0)
179 return -1;
180 return match_name(&m->value, buf);
181 }
182 } /* match_attr */
184 /* match service specific values;
185 * always call this function through match_obj() */
186 static int
187 match_service(service_matcher_t *m, sdb_store_base_t *obj)
188 {
189 sdb_llist_iter_t *iter;
191 assert(m && obj);
193 if (obj->type != SDB_SERVICE)
194 return -1;
196 if (! m->attr)
197 return 0;
199 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
200 while (sdb_llist_iter_has_next(iter)) {
201 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
203 /* if any of the attributes matches we found a matching service */
204 if (! match_obj(M(m->attr), attr)) {
205 sdb_llist_iter_destroy(iter);
206 return 0;
207 }
208 }
209 sdb_llist_iter_destroy(iter);
210 return -1;
211 } /* match_service */
213 /* match host specific values;
214 * always call this function through match_obj() */
215 static int
216 match_host(host_matcher_t *m, sdb_store_base_t *obj)
217 {
218 sdb_llist_iter_t *iter;
219 int status;
221 assert(m && obj);
223 if (obj->type != SDB_HOST)
224 return -1;
226 if (m->service) {
227 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->children);
228 status = -1;
229 }
230 else {
231 iter = NULL;
232 status = 0;
233 }
234 while (sdb_llist_iter_has_next(iter)) {
235 sdb_store_base_t *service = STORE_BASE(sdb_llist_iter_get_next(iter));
237 /* found a matching service */
238 if (! match_obj(M(m->service), service)) {
239 status = 0;
240 break;
241 }
242 }
243 sdb_llist_iter_destroy(iter);
245 if (status)
246 return status;
247 else if (! m->attr)
248 return 0;
250 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
251 while (sdb_llist_iter_has_next(iter)) {
252 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
254 /* if any attribute matches, we found a matching host */
255 if (! match_obj(M(m->attr), attr)) {
256 sdb_llist_iter_destroy(iter);
257 return 0;
258 }
259 }
260 sdb_llist_iter_destroy(iter);
261 return -1;
262 } /* match_host */
264 /* generic matchers */
266 enum {
267 MATCHER_OR,
268 MATCHER_AND,
269 MATCHER_ATTR,
270 MATCHER_SERVICE,
271 MATCHER_HOST,
272 };
274 typedef int (*matcher_cb)(sdb_store_matcher_t *, sdb_store_base_t *);
276 /* this array needs to be indexable by the matcher types */
277 static matcher_cb matchers[] = {
278 match_logical,
279 match_logical,
280 match_obj,
281 match_obj,
282 match_obj,
283 };
285 static int
286 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj)
287 {
288 int status;
290 assert(m && obj);
291 assert(OP_M(m)->left && OP_M(m)->right);
293 status = sdb_store_matcher_matches(OP_M(m)->left, obj);
294 /* lazy evaluation */
295 if (status && (m->type == MATCHER_AND))
296 return status;
297 else if ((! status) && (m->type == MATCHER_OR))
298 return status;
300 return sdb_store_matcher_matches(OP_M(m)->right, obj);
301 } /* match_logical */
303 static int
304 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj)
305 {
306 int status;
308 assert(m && obj);
310 status = match_name(&OBJ_M(m)->name, obj->super.name);
311 if (status)
312 return status;
314 switch (m->type) {
315 case MATCHER_ATTR:
316 return match_attr(ATTR_M(m), obj);
317 break;
318 case MATCHER_SERVICE:
319 return match_service(SERVICE_M(m), obj);
320 break;
321 case MATCHER_HOST:
322 return match_host(HOST_M(m), obj);
323 break;
324 }
325 return -1;
326 } /* match_obj */
328 /*
329 * private matcher types
330 */
332 /* initializes a name matcher consuming two elements from ap */
333 static int
334 name_matcher_init(name_matcher_t *m, va_list ap)
335 {
336 const char *name = va_arg(ap, const char *);
337 const char *name_re = va_arg(ap, const char *);
339 if (name) {
340 m->name = strdup(name);
341 if (! m->name)
342 return -1;
343 }
344 if (name_re) {
345 m->name_re = malloc(sizeof(*m->name_re));
346 if (! m->name_re)
347 return -1;
348 if (regcomp(m->name_re, name_re, REG_EXTENDED | REG_ICASE | REG_NOSUB))
349 return -1;
350 }
351 return 0;
352 } /* name_matcher_init */
354 static void
355 name_matcher_destroy(name_matcher_t *m)
356 {
357 if (m->name)
358 free(m->name);
359 if (m->name_re) {
360 regfree(m->name_re);
361 free(m->name_re);
362 }
363 } /* name_matcher_destroy */
365 /* initializes an object matcher consuming two elements from ap */
366 static int
367 obj_matcher_init(sdb_object_t *obj, va_list ap)
368 {
369 obj_matcher_t *m = OBJ_M(obj);
370 return name_matcher_init(&m->name, ap);
371 } /* obj_matcher_init */
373 static void
374 obj_matcher_destroy(sdb_object_t *obj)
375 {
376 obj_matcher_t *m = OBJ_M(obj);
377 name_matcher_destroy(&m->name);
378 } /* obj_matcher_destroy */
380 static int
381 attr_matcher_init(sdb_object_t *obj, va_list ap)
382 {
383 attr_matcher_t *attr = ATTR_M(obj);
384 int status;
386 M(obj)->type = MATCHER_ATTR;
388 status = obj_matcher_init(obj, ap);
389 if (! status)
390 status = name_matcher_init(&attr->value, ap);
391 return status;
392 } /* attr_matcher_init */
394 static void
395 attr_matcher_destroy(sdb_object_t *obj)
396 {
397 attr_matcher_t *attr = ATTR_M(obj);
399 obj_matcher_destroy(obj);
400 name_matcher_destroy(&attr->value);
401 } /* attr_matcher_destroy */
403 static int
404 service_matcher_init(sdb_object_t *obj, va_list ap)
405 {
406 attr_matcher_t *attr;
407 int status;
409 M(obj)->type = MATCHER_SERVICE;
411 status = obj_matcher_init(obj, ap);
412 if (status)
413 return status;
415 attr = va_arg(ap, attr_matcher_t *);
417 sdb_object_ref(SDB_OBJ(attr));
418 SERVICE_M(obj)->attr = attr;
419 return 0;
420 } /* service_matcher_init */
422 static void
423 service_matcher_destroy(sdb_object_t *obj)
424 {
425 obj_matcher_destroy(obj);
426 sdb_object_deref(SDB_OBJ(SERVICE_M(obj)->attr));
427 } /* service_matcher_destroy */
429 static int
430 host_matcher_init(sdb_object_t *obj, va_list ap)
431 {
432 service_matcher_t *service;
433 attr_matcher_t *attr;
434 int status;
436 M(obj)->type = MATCHER_HOST;
438 status = obj_matcher_init(obj, ap);
439 if (status)
440 return status;
442 service = va_arg(ap, service_matcher_t *);
443 attr = va_arg(ap, attr_matcher_t *);
445 sdb_object_ref(SDB_OBJ(service));
446 HOST_M(obj)->service = service;
447 sdb_object_ref(SDB_OBJ(attr));
448 HOST_M(obj)->attr = attr;
449 return 0;
450 } /* host_matcher_init */
452 static void
453 host_matcher_destroy(sdb_object_t *obj)
454 {
455 obj_matcher_destroy(obj);
456 sdb_object_deref(SDB_OBJ(HOST_M(obj)->service));
457 sdb_object_deref(SDB_OBJ(HOST_M(obj)->attr));
458 } /* host_matcher_destroy */
460 static int
461 op_matcher_init(sdb_object_t *obj, va_list ap)
462 {
463 M(obj)->type = va_arg(ap, int);
464 if ((M(obj)->type != MATCHER_OR) && (M(obj)->type != MATCHER_AND))
465 return -1;
467 OP_M(obj)->left = va_arg(ap, sdb_store_matcher_t *);
468 sdb_object_ref(SDB_OBJ(OP_M(obj)->left));
469 OP_M(obj)->right = va_arg(ap, sdb_store_matcher_t *);
470 sdb_object_ref(SDB_OBJ(OP_M(obj)->right));
472 if ((! OP_M(obj)->left) || (! OP_M(obj)->right))
473 return -1;
474 return 0;
475 } /* op_matcher_init */
477 static void
478 op_matcher_destroy(sdb_object_t *obj)
479 {
480 if (OP_M(obj)->left)
481 sdb_object_deref(SDB_OBJ(OP_M(obj)->left));
482 if (OP_M(obj)->right)
483 sdb_object_deref(SDB_OBJ(OP_M(obj)->right));
484 } /* op_matcher_destroy */
486 static sdb_type_t attr_type = {
487 /* size = */ sizeof(attr_matcher_t),
488 /* init = */ attr_matcher_init,
489 /* destroy = */ attr_matcher_destroy,
490 };
492 static sdb_type_t service_type = {
493 /* size = */ sizeof(service_matcher_t),
494 /* init = */ service_matcher_init,
495 /* destroy = */ service_matcher_destroy,
496 };
498 static sdb_type_t host_type = {
499 /* size = */ sizeof(host_matcher_t),
500 /* init = */ host_matcher_init,
501 /* destroy = */ host_matcher_destroy,
502 };
504 static sdb_type_t op_type = {
505 /* size = */ sizeof(op_matcher_t),
506 /* init = */ op_matcher_init,
507 /* destroy = */ op_matcher_destroy,
508 };
510 /*
511 * public API
512 */
514 sdb_store_matcher_t *
515 sdb_store_attr_matcher(const char *attr_name, const char *attr_name_re,
516 const char *attr_value, const char *attr_value_re)
517 {
518 return M(sdb_object_create("attr-matcher", attr_type,
519 attr_name, attr_name_re, attr_value, attr_value_re));
520 } /* sdb_store_attr_matcher */
522 sdb_store_matcher_t *
523 sdb_store_service_matcher(const char *service_name, const char *service_name_re,
524 sdb_store_matcher_t *attr_matcher)
525 {
526 return M(sdb_object_create("service-matcher", service_type,
527 service_name, service_name_re, attr_matcher));
528 } /* sdb_store_service_matcher */
530 sdb_store_matcher_t *
531 sdb_store_host_matcher(const char *host_name, const char *host_name_re,
532 sdb_store_matcher_t *service_matcher,
533 sdb_store_matcher_t *attr_matcher)
534 {
535 return M(sdb_object_create("host-matcher", host_type,
536 host_name, host_name_re, service_matcher, attr_matcher));
537 } /* sdb_store_host_matcher */
539 sdb_store_matcher_t *
540 sdb_store_dis_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
541 {
542 return M(sdb_object_create("dis-matcher", op_type, MATCHER_OR,
543 left, right));
544 } /* sdb_store_dis_matcher */
546 sdb_store_matcher_t *
547 sdb_store_con_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
548 {
549 return M(sdb_object_create("con-matcher", op_type, MATCHER_AND,
550 left, right));
551 } /* sdb_store_con_matcher */
553 int
554 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
555 {
556 /* "NULL" always matches */
557 if ((! m) || (! obj))
558 return 0;
560 if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers)))
561 return -1;
563 return matchers[m->type](m, obj);
564 } /* sdb_store_matcher_matches */
566 int
567 sdb_store_lookup(sdb_store_matcher_t *m, sdb_store_lookup_cb cb,
568 void *user_data)
569 {
570 lookup_iter_data_t data = { m, cb, user_data };
572 if (! cb)
573 return -1;
574 return sdb_store_iterate(lookup_iter, &data);
575 } /* sdb_store_lookup */
577 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */