Code

store: Added public functions to create and execute matchers.
[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 #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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */