d4fef03c39edc9015e37af5ad526d3e80b36c479
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 #include "sysdb.h"
35 #include "core/store-private.h"
36 #include "core/object.h"
38 #include <assert.h>
40 #include <sys/types.h>
41 #include <regex.h>
43 #include <stdlib.h>
44 #include <string.h>
46 /*
47 * private data types
48 */
50 /* match the name of something */
51 typedef struct {
52 char *name;
53 regex_t *name_re;
54 } name_matcher_t;
56 /* matcher base type */
57 struct sdb_store_matcher {
58 sdb_object_t super;
59 /* type of the matcher */
60 int type;
61 };
62 #define M(m) ((sdb_store_matcher_t *)(m))
64 /* logical operator matcher */
65 typedef struct {
66 sdb_store_matcher_t super;
68 /* left and right hand operands */
69 sdb_store_matcher_t *left;
70 sdb_store_matcher_t *right;
71 } op_matcher_t;
72 #define OP_M(m) ((op_matcher_t *)(m))
74 /* match any type of object by it's base information */
75 typedef struct {
76 sdb_store_matcher_t super;
78 /* match by the name of the object */
79 name_matcher_t name;
80 } obj_matcher_t;
81 #define OBJ_M(m) ((obj_matcher_t *)(m))
83 /* match attributes */
84 typedef struct {
85 obj_matcher_t super;
86 /* XXX: this needs to be more flexible;
87 * add support for type-specific operators */
88 name_matcher_t value;
89 } attr_matcher_t;
90 #define ATTR_M(m) ((attr_matcher_t *)(m))
92 /* match services */
93 typedef struct {
94 obj_matcher_t super;
95 /* match by attributes assigned to the service */
96 attr_matcher_t *attr;
97 } service_matcher_t;
98 #define SERVICE_M(m) ((service_matcher_t *)(m))
100 /* match hosts */
101 typedef struct {
102 obj_matcher_t super;
103 /* match by services assigned to the host */
104 service_matcher_t *service;
105 /* match by attributes assigned to the host */
106 attr_matcher_t *attr;
107 } host_matcher_t;
108 #define HOST_M(m) ((host_matcher_t *)(m))
110 /*
111 * matcher implementations
112 */
114 static int
115 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj);
116 static int
117 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj);
119 /* specific matchers */
121 static int
122 match_name(name_matcher_t *m, const char *name)
123 {
124 assert(m);
126 if ((! m->name) && (! m->name_re))
127 return 0;
129 if (! name)
130 name = "";
132 if (m->name && strcasecmp(m->name, name))
133 return -1;
134 if (m->name_re && regexec(m->name_re, name,
135 /* matches */ 0, NULL, /* flags = */ 0))
136 return -1;
137 return 0;
138 } /* match_name */
140 /* match attribute specific values;
141 * always call this function through match_obj() */
142 static int
143 match_attr(attr_matcher_t *m, sdb_store_base_t *obj)
144 {
145 assert(m && obj);
147 if (obj->type != SDB_ATTRIBUTE)
148 return -1;
150 {
151 sdb_attribute_t *attr = SDB_ATTR(obj);
152 char buf[sdb_data_strlen(&attr->value) + 1];
154 if (sdb_data_format(&attr->value, buf, sizeof(buf), SDB_UNQUOTED) <= 0)
155 return -1;
156 return match_name(&m->value, buf);
157 }
158 } /* match_attr */
160 /* match service specific values;
161 * always call this function through match_obj() */
162 static int
163 match_service(service_matcher_t *m, sdb_store_base_t *obj)
164 {
165 sdb_llist_iter_t *iter;
167 assert(m && obj);
169 if (obj->type != SDB_SERVICE)
170 return -1;
172 if (! m->attr)
173 return 0;
175 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
176 while (sdb_llist_iter_has_next(iter)) {
177 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
179 /* if any of the attributes matches we found a matching service */
180 if (! match_obj(M(m->attr), attr)) {
181 sdb_llist_iter_destroy(iter);
182 return 0;
183 }
184 }
185 sdb_llist_iter_destroy(iter);
186 return -1;
187 } /* match_service */
189 /* match host specific values;
190 * always call this function through match_obj() */
191 static int
192 match_host(host_matcher_t *m, sdb_store_base_t *obj)
193 {
194 sdb_llist_iter_t *iter;
195 int status;
197 assert(m && obj);
199 if (obj->type != SDB_HOST)
200 return -1;
202 if (m->service) {
203 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->children);
204 status = -1;
205 }
206 else {
207 iter = NULL;
208 status = 0;
209 }
210 while (sdb_llist_iter_has_next(iter)) {
211 sdb_store_base_t *service = STORE_BASE(sdb_llist_iter_get_next(iter));
213 /* found a matching service */
214 if (! match_obj(M(m->service), service)) {
215 status = 0;
216 break;
217 }
218 }
219 sdb_llist_iter_destroy(iter);
221 if (status)
222 return status;
223 else if (! m->attr)
224 return 0;
226 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
227 while (sdb_llist_iter_has_next(iter)) {
228 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
230 /* if any attribute matches, we found a matching host */
231 if (! match_obj(M(m->attr), attr)) {
232 sdb_llist_iter_destroy(iter);
233 return 0;
234 }
235 }
236 sdb_llist_iter_destroy(iter);
237 return -1;
238 } /* match_host */
240 /* generic matchers */
242 enum {
243 MATCHER_OR,
244 MATCHER_AND,
245 MATCHER_ATTR,
246 MATCHER_SERVICE,
247 MATCHER_HOST,
248 };
250 typedef int (*matcher_cb)(sdb_store_matcher_t *, sdb_store_base_t *);
252 /* this array needs to be indexable by the matcher types */
253 static matcher_cb matchers[] = {
254 match_logical,
255 match_logical,
256 match_obj,
257 match_obj,
258 match_obj,
259 };
261 static int
262 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj)
263 {
264 int status;
266 assert(m && obj);
267 assert(OP_M(m)->left && OP_M(m)->right);
269 status = sdb_store_matcher_matches(OP_M(m)->left, obj);
270 /* lazy evaluation */
271 if (status && (m->type == MATCHER_AND))
272 return status;
273 else if ((! status) && (m->type == MATCHER_OR))
274 return status;
276 return sdb_store_matcher_matches(OP_M(m)->right, obj);
277 } /* match_logical */
279 static int
280 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj)
281 {
282 int status;
284 assert(m && obj);
286 status = match_name(&OBJ_M(m)->name, obj->super.name);
287 if (status)
288 return status;
290 switch (m->type) {
291 case MATCHER_ATTR:
292 return match_attr(ATTR_M(m), obj);
293 break;
294 case MATCHER_SERVICE:
295 return match_service(SERVICE_M(m), obj);
296 break;
297 case MATCHER_HOST:
298 return match_host(HOST_M(m), obj);
299 break;
300 }
301 return -1;
302 } /* match_obj */
304 /*
305 * private matcher types
306 */
308 /* initializes a name matcher consuming two elements from ap */
309 static int
310 name_matcher_init(name_matcher_t *m, va_list ap)
311 {
312 const char *name = va_arg(ap, const char *);
313 const char *name_re = va_arg(ap, const char *);
315 if (name) {
316 m->name = strdup(name);
317 if (! m->name)
318 return -1;
319 }
320 if (name_re) {
321 m->name_re = malloc(sizeof(*m->name_re));
322 if (! m->name_re)
323 return -1;
324 if (regcomp(m->name_re, name_re, REG_EXTENDED | REG_ICASE | REG_NOSUB))
325 return -1;
326 }
327 return 0;
328 } /* name_matcher_init */
330 static void
331 name_matcher_destroy(name_matcher_t *m)
332 {
333 if (m->name)
334 free(m->name);
335 if (m->name_re) {
336 regfree(m->name_re);
337 free(m->name_re);
338 }
339 } /* name_matcher_destroy */
341 /* initializes an object matcher consuming two elements from ap */
342 static int
343 obj_matcher_init(sdb_object_t *obj, va_list ap)
344 {
345 obj_matcher_t *m = OBJ_M(obj);
346 return name_matcher_init(&m->name, ap);
347 } /* obj_matcher_init */
349 static void
350 obj_matcher_destroy(sdb_object_t *obj)
351 {
352 obj_matcher_t *m = OBJ_M(obj);
353 name_matcher_destroy(&m->name);
354 } /* obj_matcher_destroy */
356 static int
357 attr_matcher_init(sdb_object_t *obj, va_list ap)
358 {
359 attr_matcher_t *attr = ATTR_M(obj);
360 int status;
362 M(obj)->type = MATCHER_ATTR;
364 status = obj_matcher_init(obj, ap);
365 if (! status)
366 status = name_matcher_init(&attr->value, ap);
367 return status;
368 } /* attr_matcher_init */
370 static void
371 attr_matcher_destroy(sdb_object_t *obj)
372 {
373 attr_matcher_t *attr = ATTR_M(obj);
375 obj_matcher_destroy(obj);
376 name_matcher_destroy(&attr->value);
377 } /* attr_matcher_destroy */
379 static int
380 service_matcher_init(sdb_object_t *obj, va_list ap)
381 {
382 attr_matcher_t *attr;
383 int status;
385 M(obj)->type = MATCHER_SERVICE;
387 status = obj_matcher_init(obj, ap);
388 if (status)
389 return status;
391 attr = va_arg(ap, attr_matcher_t *);
393 sdb_object_ref(SDB_OBJ(attr));
394 SERVICE_M(obj)->attr = attr;
395 return 0;
396 } /* service_matcher_init */
398 static void
399 service_matcher_destroy(sdb_object_t *obj)
400 {
401 obj_matcher_destroy(obj);
402 sdb_object_deref(SDB_OBJ(SERVICE_M(obj)->attr));
403 } /* service_matcher_destroy */
405 static int
406 host_matcher_init(sdb_object_t *obj, va_list ap)
407 {
408 service_matcher_t *service;
409 attr_matcher_t *attr;
410 int status;
412 M(obj)->type = MATCHER_HOST;
414 status = obj_matcher_init(obj, ap);
415 if (status)
416 return status;
418 service = va_arg(ap, service_matcher_t *);
419 attr = va_arg(ap, attr_matcher_t *);
421 sdb_object_ref(SDB_OBJ(service));
422 HOST_M(obj)->service = service;
423 sdb_object_ref(SDB_OBJ(attr));
424 HOST_M(obj)->attr = attr;
425 return 0;
426 } /* host_matcher_init */
428 static void
429 host_matcher_destroy(sdb_object_t *obj)
430 {
431 obj_matcher_destroy(obj);
432 sdb_object_deref(SDB_OBJ(HOST_M(obj)->service));
433 sdb_object_deref(SDB_OBJ(HOST_M(obj)->attr));
434 } /* host_matcher_destroy */
436 static int
437 op_matcher_init(sdb_object_t *obj, va_list ap)
438 {
439 M(obj)->type = va_arg(ap, int);
440 if ((M(obj)->type != MATCHER_OR) && (M(obj)->type != MATCHER_AND))
441 return -1;
443 OP_M(obj)->left = va_arg(ap, sdb_store_matcher_t *);
444 sdb_object_ref(SDB_OBJ(OP_M(obj)->left));
445 OP_M(obj)->right = va_arg(ap, sdb_store_matcher_t *);
446 sdb_object_ref(SDB_OBJ(OP_M(obj)->right));
448 if ((! OP_M(obj)->left) || (! OP_M(obj)->right))
449 return -1;
450 return 0;
451 } /* op_matcher_init */
453 static void
454 op_matcher_destroy(sdb_object_t *obj)
455 {
456 if (OP_M(obj)->left)
457 sdb_object_deref(SDB_OBJ(OP_M(obj)->left));
458 if (OP_M(obj)->right)
459 sdb_object_deref(SDB_OBJ(OP_M(obj)->right));
460 } /* op_matcher_destroy */
462 static sdb_type_t attr_type = {
463 /* size = */ sizeof(attr_matcher_t),
464 /* init = */ attr_matcher_init,
465 /* destroy = */ attr_matcher_destroy,
466 };
468 static sdb_type_t service_type = {
469 /* size = */ sizeof(service_matcher_t),
470 /* init = */ service_matcher_init,
471 /* destroy = */ service_matcher_destroy,
472 };
474 static sdb_type_t host_type = {
475 /* size = */ sizeof(host_matcher_t),
476 /* init = */ host_matcher_init,
477 /* destroy = */ host_matcher_destroy,
478 };
480 static sdb_type_t op_type = {
481 /* size = */ sizeof(op_matcher_t),
482 /* init = */ op_matcher_init,
483 /* destroy = */ op_matcher_destroy,
484 };
486 /*
487 * public API
488 */
490 sdb_store_matcher_t *
491 sdb_store_attr_matcher(const char *attr_name, const char *attr_name_re,
492 const char *attr_value, const char *attr_value_re)
493 {
494 return M(sdb_object_create("attr-matcher", attr_type,
495 attr_name, attr_name_re, attr_value, attr_value_re));
496 } /* sdb_store_attr_matcher */
498 sdb_store_matcher_t *
499 sdb_store_service_matcher(const char *service_name, const char *service_name_re,
500 sdb_store_matcher_t *attr_matcher)
501 {
502 return M(sdb_object_create("service-matcher", service_type,
503 service_name, service_name_re, attr_matcher));
504 } /* sdb_store_service_matcher */
506 sdb_store_matcher_t *
507 sdb_store_host_matcher(const char *host_name, const char *host_name_re,
508 sdb_store_matcher_t *service_matcher,
509 sdb_store_matcher_t *attr_matcher)
510 {
511 return M(sdb_object_create("host-matcher", host_type,
512 host_name, host_name_re, service_matcher, attr_matcher));
513 } /* sdb_store_host_matcher */
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 int
530 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
531 {
532 /* "NULL" always matches */
533 if ((! m) || (! obj))
534 return 0;
536 if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers)))
537 return -1;
539 return matchers[m->type](m, obj);
540 } /* sdb_store_matcher_matches */
542 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */