Code

store: Added sdb_store_lookup().
[sysdb.git] / src / core / store_lookup.c
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */