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 sdb_type_t attr_type = {
437 /* size = */ sizeof(attr_matcher_t),
438 /* init = */ attr_matcher_init,
439 /* destroy = */ attr_matcher_destroy,
440 };
442 static sdb_type_t service_type = {
443 /* size = */ sizeof(service_matcher_t),
444 /* init = */ service_matcher_init,
445 /* destroy = */ service_matcher_destroy,
446 };
448 static sdb_type_t host_type = {
449 /* size = */ sizeof(host_matcher_t),
450 /* init = */ host_matcher_init,
451 /* destroy = */ host_matcher_destroy,
452 };
454 /*
455 * public API
456 */
458 sdb_store_matcher_t *
459 sdb_store_attr_matcher(const char *attr_name, const char *attr_name_re,
460 const char *attr_value, const char *attr_value_re)
461 {
462 return M(sdb_object_create("attr-matcher", attr_type,
463 attr_name, attr_name_re, attr_value, attr_value_re));
464 } /* sdb_store_attr_matcher */
466 sdb_store_matcher_t *
467 sdb_store_service_matcher(const char *service_name, const char *service_name_re,
468 sdb_store_matcher_t *attr_matcher)
469 {
470 return M(sdb_object_create("service-matcher", service_type,
471 service_name, service_name_re, attr_matcher));
472 } /* sdb_store_service_matcher */
474 sdb_store_matcher_t *
475 sdb_store_host_matcher(const char *host_name, const char *host_name_re,
476 sdb_store_matcher_t *service_matcher,
477 sdb_store_matcher_t *attr_matcher)
478 {
479 return M(sdb_object_create("host-matcher", host_type,
480 host_name, host_name_re, service_matcher, attr_matcher));
481 } /* sdb_store_host_matcher */
483 int
484 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
485 {
486 /* "NULL" always matches */
487 if ((! m) || (! obj))
488 return 0;
490 if ((m->type < 0) || ((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers)))
491 return -1;
493 return matchers[m->type](m, obj);
494 } /* sdb_store_matcher_matches */
496 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */