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 /*
115 * matcher implementations
116 */
118 static int
119 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj);
120 static int
121 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj);
123 /* specific matchers */
125 static int
126 match_name(name_matcher_t *m, const char *name)
127 {
128 assert(m);
130 if ((! m->name) && (! m->name_re))
131 return 0;
133 if (! name)
134 name = "";
136 if (m->name && strcasecmp(m->name, name))
137 return -1;
138 if (m->name_re && regexec(m->name_re, name,
139 /* matches */ 0, NULL, /* flags = */ 0))
140 return -1;
141 return 0;
142 } /* match_name */
144 /* match attribute specific values;
145 * always call this function through match_obj() */
146 static int
147 match_attr(attr_matcher_t *m, sdb_store_base_t *obj)
148 {
149 assert(m && obj);
151 if (obj->type != SDB_ATTRIBUTE)
152 return -1;
154 {
155 sdb_attribute_t *attr = SDB_ATTR(obj);
156 char buf[sdb_data_strlen(&attr->value) + 1];
158 if (sdb_data_format(&attr->value, buf, sizeof(buf), SDB_UNQUOTED) <= 0)
159 return -1;
160 return match_name(&m->value, buf);
161 }
162 } /* match_attr */
164 /* match service specific values;
165 * always call this function through match_obj() */
166 static int
167 match_service(service_matcher_t *m, sdb_store_base_t *obj)
168 {
169 sdb_llist_iter_t *iter;
171 assert(m && obj);
173 if (obj->type != SDB_SERVICE)
174 return -1;
176 if (! m->attr)
177 return 0;
179 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
180 while (sdb_llist_iter_has_next(iter)) {
181 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
183 /* if any of the attributes matches we found a matching service */
184 if (! match_obj(M(m->attr), attr)) {
185 sdb_llist_iter_destroy(iter);
186 return 0;
187 }
188 }
189 sdb_llist_iter_destroy(iter);
190 return -1;
191 } /* match_service */
193 /* match host specific values;
194 * always call this function through match_obj() */
195 static int
196 match_host(host_matcher_t *m, sdb_store_base_t *obj)
197 {
198 sdb_llist_iter_t *iter;
199 int status;
201 assert(m && obj);
203 if (obj->type != SDB_HOST)
204 return -1;
206 if (m->service) {
207 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->children);
208 status = -1;
209 }
210 else {
211 iter = NULL;
212 status = 0;
213 }
214 while (sdb_llist_iter_has_next(iter)) {
215 sdb_store_base_t *service = STORE_BASE(sdb_llist_iter_get_next(iter));
217 /* found a matching service */
218 if (! match_obj(M(m->service), service)) {
219 status = 0;
220 break;
221 }
222 }
223 sdb_llist_iter_destroy(iter);
225 if (status)
226 return status;
227 else if (! m->attr)
228 return 0;
230 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
231 while (sdb_llist_iter_has_next(iter)) {
232 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
234 /* if any attribute matches, we found a matching host */
235 if (! match_obj(M(m->attr), attr)) {
236 sdb_llist_iter_destroy(iter);
237 return 0;
238 }
239 }
240 sdb_llist_iter_destroy(iter);
241 return -1;
242 } /* match_host */
244 /* generic matchers */
246 enum {
247 MATCHER_OR,
248 MATCHER_AND,
249 MATCHER_ATTR,
250 MATCHER_SERVICE,
251 MATCHER_HOST,
252 };
254 typedef int (*matcher_cb)(sdb_store_matcher_t *, sdb_store_base_t *);
256 /* this array needs to be indexable by the matcher types */
257 static matcher_cb matchers[] = {
258 match_logical,
259 match_logical,
260 match_obj,
261 match_obj,
262 match_obj,
263 };
265 static int
266 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj)
267 {
268 int status;
270 assert(m && obj);
271 assert(OP_M(m)->left && OP_M(m)->right);
273 status = sdb_store_matcher_matches(OP_M(m)->left, obj);
274 /* lazy evaluation */
275 if (status && (m->type == MATCHER_AND))
276 return status;
277 else if ((! status) && (m->type == MATCHER_OR))
278 return status;
280 return sdb_store_matcher_matches(OP_M(m)->right, obj);
281 } /* match_logical */
283 static int
284 match_obj(sdb_store_matcher_t *m, sdb_store_base_t *obj)
285 {
286 int status;
288 assert(m && obj);
290 status = match_name(&OBJ_M(m)->name, obj->super.name);
291 if (status)
292 return status;
294 switch (m->type) {
295 case MATCHER_ATTR:
296 return match_attr(ATTR_M(m), obj);
297 break;
298 case MATCHER_SERVICE:
299 return match_service(SERVICE_M(m), obj);
300 break;
301 case MATCHER_HOST:
302 return match_host(HOST_M(m), obj);
303 break;
304 }
305 return -1;
306 } /* match_obj */
308 /*
309 * private matcher types
310 */
312 /* initializes a name matcher consuming two elements from ap */
313 static int
314 name_matcher_init(name_matcher_t *m, va_list ap)
315 {
316 const char *name = va_arg(ap, const char *);
317 const char *name_re = va_arg(ap, const char *);
319 if (name) {
320 m->name = strdup(name);
321 if (! m->name)
322 return -1;
323 }
324 if (name_re) {
325 m->name_re = malloc(sizeof(*m->name_re));
326 if (! m->name_re)
327 return -1;
328 if (regcomp(m->name_re, name_re, REG_EXTENDED | REG_ICASE | REG_NOSUB))
329 return -1;
330 }
331 return 0;
332 } /* name_matcher_init */
334 static void
335 name_matcher_destroy(name_matcher_t *m)
336 {
337 if (m->name)
338 free(m->name);
339 if (m->name_re) {
340 regfree(m->name_re);
341 free(m->name_re);
342 }
343 } /* name_matcher_destroy */
345 /* initializes an object matcher consuming two elements from ap */
346 static int
347 obj_matcher_init(sdb_object_t *obj, va_list ap)
348 {
349 obj_matcher_t *m = OBJ_M(obj);
350 return name_matcher_init(&m->name, ap);
351 } /* obj_matcher_init */
353 static void
354 obj_matcher_destroy(sdb_object_t *obj)
355 {
356 obj_matcher_t *m = OBJ_M(obj);
357 name_matcher_destroy(&m->name);
358 } /* obj_matcher_destroy */
360 static int
361 attr_matcher_init(sdb_object_t *obj, va_list ap)
362 {
363 attr_matcher_t *attr = ATTR_M(obj);
364 int status;
366 M(obj)->type = MATCHER_ATTR;
368 status = obj_matcher_init(obj, ap);
369 if (! status)
370 status = name_matcher_init(&attr->value, ap);
371 return status;
372 } /* attr_matcher_init */
374 static void
375 attr_matcher_destroy(sdb_object_t *obj)
376 {
377 attr_matcher_t *attr = ATTR_M(obj);
379 obj_matcher_destroy(obj);
380 name_matcher_destroy(&attr->value);
381 } /* attr_matcher_destroy */
383 static int
384 service_matcher_init(sdb_object_t *obj, va_list ap)
385 {
386 attr_matcher_t *attr;
387 int status;
389 M(obj)->type = MATCHER_SERVICE;
391 status = obj_matcher_init(obj, ap);
392 if (status)
393 return status;
395 attr = va_arg(ap, attr_matcher_t *);
397 sdb_object_ref(SDB_OBJ(attr));
398 SERVICE_M(obj)->attr = attr;
399 return 0;
400 } /* service_matcher_init */
402 static void
403 service_matcher_destroy(sdb_object_t *obj)
404 {
405 obj_matcher_destroy(obj);
406 sdb_object_deref(SDB_OBJ(SERVICE_M(obj)->attr));
407 } /* service_matcher_destroy */
409 static int
410 host_matcher_init(sdb_object_t *obj, va_list ap)
411 {
412 service_matcher_t *service;
413 attr_matcher_t *attr;
414 int status;
416 M(obj)->type = MATCHER_HOST;
418 status = obj_matcher_init(obj, ap);
419 if (status)
420 return status;
422 service = va_arg(ap, service_matcher_t *);
423 attr = va_arg(ap, attr_matcher_t *);
425 sdb_object_ref(SDB_OBJ(service));
426 HOST_M(obj)->service = service;
427 sdb_object_ref(SDB_OBJ(attr));
428 HOST_M(obj)->attr = attr;
429 return 0;
430 } /* host_matcher_init */
432 static void
433 host_matcher_destroy(sdb_object_t *obj)
434 {
435 obj_matcher_destroy(obj);
436 sdb_object_deref(SDB_OBJ(HOST_M(obj)->service));
437 sdb_object_deref(SDB_OBJ(HOST_M(obj)->attr));
438 } /* host_matcher_destroy */
440 static int
441 op_matcher_init(sdb_object_t *obj, va_list ap)
442 {
443 M(obj)->type = va_arg(ap, int);
444 if ((M(obj)->type != MATCHER_OR) && (M(obj)->type != MATCHER_AND))
445 return -1;
447 OP_M(obj)->left = va_arg(ap, sdb_store_matcher_t *);
448 sdb_object_ref(SDB_OBJ(OP_M(obj)->left));
449 OP_M(obj)->right = va_arg(ap, sdb_store_matcher_t *);
450 sdb_object_ref(SDB_OBJ(OP_M(obj)->right));
452 if ((! OP_M(obj)->left) || (! OP_M(obj)->right))
453 return -1;
454 return 0;
455 } /* op_matcher_init */
457 static void
458 op_matcher_destroy(sdb_object_t *obj)
459 {
460 if (OP_M(obj)->left)
461 sdb_object_deref(SDB_OBJ(OP_M(obj)->left));
462 if (OP_M(obj)->right)
463 sdb_object_deref(SDB_OBJ(OP_M(obj)->right));
464 } /* op_matcher_destroy */
466 static sdb_type_t attr_type = {
467 /* size = */ sizeof(attr_matcher_t),
468 /* init = */ attr_matcher_init,
469 /* destroy = */ attr_matcher_destroy,
470 };
472 static sdb_type_t service_type = {
473 /* size = */ sizeof(service_matcher_t),
474 /* init = */ service_matcher_init,
475 /* destroy = */ service_matcher_destroy,
476 };
478 static sdb_type_t host_type = {
479 /* size = */ sizeof(host_matcher_t),
480 /* init = */ host_matcher_init,
481 /* destroy = */ host_matcher_destroy,
482 };
484 static sdb_type_t op_type = {
485 /* size = */ sizeof(op_matcher_t),
486 /* init = */ op_matcher_init,
487 /* destroy = */ op_matcher_destroy,
488 };
490 /*
491 * public API
492 */
494 sdb_store_matcher_t *
495 sdb_store_attr_matcher(const char *attr_name, const char *attr_name_re,
496 const char *attr_value, const char *attr_value_re)
497 {
498 return M(sdb_object_create("attr-matcher", attr_type,
499 attr_name, attr_name_re, attr_value, attr_value_re));
500 } /* sdb_store_attr_matcher */
502 sdb_store_matcher_t *
503 sdb_store_service_matcher(const char *service_name, const char *service_name_re,
504 sdb_store_matcher_t *attr_matcher)
505 {
506 return M(sdb_object_create("service-matcher", service_type,
507 service_name, service_name_re, attr_matcher));
508 } /* sdb_store_service_matcher */
510 sdb_store_matcher_t *
511 sdb_store_host_matcher(const char *host_name, const char *host_name_re,
512 sdb_store_matcher_t *service_matcher,
513 sdb_store_matcher_t *attr_matcher)
514 {
515 return M(sdb_object_create("host-matcher", host_type,
516 host_name, host_name_re, service_matcher, attr_matcher));
517 } /* sdb_store_host_matcher */
519 sdb_store_matcher_t *
520 sdb_store_dis_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
521 {
522 return M(sdb_object_create("dis-matcher", op_type, MATCHER_OR,
523 left, right));
524 } /* sdb_store_dis_matcher */
526 sdb_store_matcher_t *
527 sdb_store_con_matcher(sdb_store_matcher_t *left, sdb_store_matcher_t *right)
528 {
529 return M(sdb_object_create("con-matcher", op_type, MATCHER_AND,
530 left, right));
531 } /* sdb_store_con_matcher */
533 int
534 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
535 {
536 /* "NULL" always matches */
537 if ((! m) || (! obj))
538 return 0;
540 if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers)))
541 return -1;
543 return matchers[m->type](m, obj);
544 } /* sdb_store_matcher_matches */
546 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */