Code

store_lookup: Fix matching of services with empty attr matcher.
[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"
37 #include <assert.h>
39 #include <sys/types.h>
40 #include <regex.h>
42 #include <string.h>
44 /*
45  * private data types
46  */
48 /* match the name of something */
49 typedef struct {
50         const char *name;
51         regex_t    *name_re;
52 } name_matcher_t;
54 /* matcher base type */
55 typedef struct {
56         /* type of the matcher */
57         int type;
58 } matcher_t;
59 #define M(m) ((matcher_t *)(m))
61 /* logical operator matcher */
62 typedef struct {
63         matcher_t super;
65         /* left and right hand operands */
66         matcher_t *left;
67         matcher_t *right;
68 } op_matcher_t;
69 #define OP_M(m) ((op_matcher_t *)(m))
71 /* match any type of object by it's base information */
72 typedef struct {
73         matcher_t super;
75         /* match by the name of the object */
76         name_matcher_t name;
77 } obj_matcher_t;
78 #define OBJ_M(m) ((obj_matcher_t *)(m))
80 /* match attributes */
81 typedef struct {
82         obj_matcher_t super;
83         /* XXX: this needs to be more flexible;
84          *      add support for type-specific operators */
85         name_matcher_t value;
86 } attr_matcher_t;
87 #define ATTR_M(m) ((attr_matcher_t *)(m))
89 /* match services */
90 typedef struct {
91         obj_matcher_t super;
92         /* match by attributes assigned to the service */
93         attr_matcher_t *attr;
94 } service_matcher_t;
95 #define SERVICE_M(m) ((service_matcher_t *)(m))
97 /* match hosts */
98 typedef struct {
99         obj_matcher_t super;
100         /* match by services assigned to the host */
101         service_matcher_t *service;
102         /* match by attributes assigned to the host */
103         attr_matcher_t *attr;
104 } host_matcher_t;
105 #define HOST_M(m) ((host_matcher_t *)(m))
107 /*
108  * matcher implementations
109  */
111 static int
112 match_logical(matcher_t *m, sdb_store_base_t *obj);
113 static int
114 match_obj(matcher_t *m, sdb_store_base_t *obj);
116 /* specific matchers */
118 static int
119 match_name(name_matcher_t *m, const char *name)
121         assert(m);
123         if ((! m->name) && (! m->name_re))
124                 return 0;
126         if (! name)
127                 name = "";
129         if (m->name && strcasecmp(m->name, name))
130                 return -1;
131         if (m->name_re && regexec(m->name_re, name,
132                                         /* matches */ 0, NULL, /* flags = */ 0))
133                 return -1;
134         return 0;
135 } /* match_name */
137 /* match attribute specific values;
138  * always call this function through match_obj() */
139 static int
140 match_attr(attr_matcher_t *m, sdb_store_base_t *obj)
142         assert(m && obj);
144         if (obj->type != SDB_ATTRIBUTE)
145                 return -1;
147         {
148                 sdb_attribute_t *attr = SDB_ATTR(obj);
149                 char buf[sdb_data_strlen(&attr->value) + 1];
151                 if (sdb_data_format(&attr->value, buf, sizeof(buf), SDB_UNQUOTED) <= 0)
152                         return -1;
153                 return match_name(&m->value, buf);
154         }
155 } /* match_attr */
157 /* match service specific values;
158  * always call this function through match_obj() */
159 static int
160 match_service(service_matcher_t *m, sdb_store_base_t *obj)
162         sdb_llist_iter_t *iter;
164         assert(m && obj);
166         if (obj->type != SDB_SERVICE)
167                 return -1;
169         if (! m->attr)
170                 return 0;
172         iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
173         while (sdb_llist_iter_has_next(iter)) {
174                 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
176                 /* if any of the attributes matches we found a matching service */
177                 if (! match_obj(M(m->attr), attr)) {
178                         sdb_llist_iter_destroy(iter);
179                         return 0;
180                 }
181         }
182         sdb_llist_iter_destroy(iter);
183         return -1;
184 } /* match_service */
186 /* match host specific values;
187  * always call this function through match_obj() */
188 static int
189 match_host(host_matcher_t *m, sdb_store_base_t *obj)
191         sdb_llist_iter_t *iter;
192         int status;
194         assert(m && obj);
196         if (obj->type != SDB_HOST)
197                 return -1;
199         if (m->service) {
200                 iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->children);
201                 status = -1;
202         }
203         else {
204                 iter = NULL;
205                 status = 0;
206         }
207         while (sdb_llist_iter_has_next(iter)) {
208                 sdb_store_base_t *service = STORE_BASE(sdb_llist_iter_get_next(iter));
210                 /* found a matching service */
211                 if (! match_obj(M(m->service), service)) {
212                         status = 0;
213                         break;
214                 }
215         }
216         sdb_llist_iter_destroy(iter);
218         if (status)
219                 return status;
220         else if (! m->attr)
221                 return 0;
223         iter = sdb_llist_get_iter(SDB_STORE_OBJ(obj)->attributes);
224         while (sdb_llist_iter_has_next(iter)) {
225                 sdb_store_base_t *attr = STORE_BASE(sdb_llist_iter_get_next(iter));
227                 /* if any attribute matches, we found a matching host */
228                 if (! match_obj(M(m->attr), attr)) {
229                         sdb_llist_iter_destroy(iter);
230                         return 0;
231                 }
232         }
233         sdb_llist_iter_destroy(iter);
234         return -1;
235 } /* match_host */
237 /* generic matchers */
239 enum {
240         MATCHER_OR,
241         MATCHER_AND,
242         MATCHER_ATTR,
243         MATCHER_SERVICE,
244         MATCHER_HOST,
245 };
247 typedef int (*matcher_cb)(matcher_t *, sdb_store_base_t *);
249 /* this array needs to be indexable by the matcher types */
250 static matcher_cb matchers[] = {
251         match_logical,
252         match_logical,
253         match_obj,
254         match_obj,
255         match_obj,
256 };
258 static int
259 match(matcher_t *m, sdb_store_base_t *obj)
261         assert(m && obj);
262         assert((0 <= m->type)
263                         && ((size_t)m->type < SDB_STATIC_ARRAY_LEN(matchers)));
265         return matchers[m->type](m, obj);
266 } /* match */
268 static int
269 match_logical(matcher_t *m, sdb_store_base_t *obj)
271         int status;
273         assert(m && obj);
274         assert(OP_M(m)->left && OP_M(m)->right);
276         status = match(OP_M(m)->left, obj);
277         /* lazy evaluation */
278         if (status && (m->type == MATCHER_AND))
279                 return status;
280         else if ((! status) && (m->type == MATCHER_OR))
281                 return status;
283         return match(OP_M(m)->right, obj);
284 } /* match_logical */
286 static int
287 match_obj(matcher_t *m, sdb_store_base_t *obj)
289         int status;
291         assert(m && obj);
293         status = match_name(&OBJ_M(m)->name, obj->super.name);
294         if (status)
295                 return status;
297         switch (m->type) {
298                 case MATCHER_ATTR:
299                         return match_attr(ATTR_M(m), obj);
300                         break;
301                 case MATCHER_SERVICE:
302                         return match_service(SERVICE_M(m), obj);
303                         break;
304                 case MATCHER_HOST:
305                         return match_host(HOST_M(m), obj);
306                         break;
307         }
308         return -1;
309 } /* match_obj */
311 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */