Code

Build a more generic/powerful query API which writes to a store-writer.
[sysdb.git] / src / core / store.c
1 /*
2  * SysDB - src/core/store.c
3  * Copyright (C) 2012-2013 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 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
33 #include "core/store-private.h"
34 #include "core/plugin.h"
35 #include "utils/avltree.h"
36 #include "utils/error.h"
38 #include <assert.h>
40 #include <errno.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
46 #include <pthread.h>
48 /*
49  * private types
50  */
52 struct sdb_store {
53         sdb_object_t super;
55         /* hosts are the top-level entries and
56          * reference everything else */
57         sdb_avltree_t *hosts;
58         pthread_rwlock_t host_lock;
59 };
61 /* internal representation of a to-be-stored object */
62 typedef struct {
63         sdb_store_obj_t *parent;
64         sdb_avltree_t *parent_tree;
65         int type;
66         const char *name;
67         sdb_time_t last_update;
68         const char * const *backends;
69         size_t backends_num;
70 } store_obj_t;
71 #define STORE_OBJ_INIT { NULL, NULL, 0, NULL, 0, NULL, 0 }
73 static sdb_type_t host_type;
74 static sdb_type_t service_type;
75 static sdb_type_t metric_type;
76 static sdb_type_t attribute_type;
78 static int
79 store_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
80 {
81         int err;
82         if (! (SDB_STORE(obj)->hosts = sdb_avltree_create()))
83                 return -1;
84         if ((err = pthread_rwlock_init(&SDB_STORE(obj)->host_lock,
85                                         /* attr = */ NULL))) {
86                 char errbuf[128];
87                 sdb_log(SDB_LOG_ERR, "store: Failed to initialize lock: %s",
88                                 sdb_strerror(err, errbuf, sizeof(errbuf)));
89                 return -1;
90         }
91         return 0;
92 } /* store_init */
94 static void
95 store_destroy(sdb_object_t *obj)
96 {
97         int err;
98         if ((err = pthread_rwlock_destroy(&SDB_STORE(obj)->host_lock))) {
99                 char errbuf[128];
100                 sdb_log(SDB_LOG_ERR, "store: Failed to destroy lock: %s",
101                                 sdb_strerror(err, errbuf, sizeof(errbuf)));
102                 return;
103         }
104         sdb_avltree_destroy(SDB_STORE(obj)->hosts);
105         SDB_STORE(obj)->hosts = NULL;
106 } /* store_destroy */
108 static int
109 store_obj_init(sdb_object_t *obj, va_list ap)
111         sdb_store_obj_t *sobj = STORE_OBJ(obj);
113         sobj->type = va_arg(ap, int);
115         sobj->last_update = va_arg(ap, sdb_time_t);
116         sobj->interval = 0;
117         sobj->backends = NULL;
118         sobj->backends_num = 0;
119         sobj->parent = NULL;
120         return 0;
121 } /* store_obj_init */
123 static void
124 store_obj_destroy(sdb_object_t *obj)
126         sdb_store_obj_t *sobj = STORE_OBJ(obj);
127         size_t i;
129         for (i = 0; i < sobj->backends_num; ++i)
130                 free(sobj->backends[i]);
131         free(sobj->backends);
132         sobj->backends = NULL;
133         sobj->backends_num = 0;
135         // We don't currently keep an extra reference for parent objects to
136         // avoid circular self-references which are not handled correctly by
137         // the ref-count base management layer.
138         //sdb_object_deref(SDB_OBJ(sobj->parent));
139 } /* store_obj_destroy */
141 static int
142 host_init(sdb_object_t *obj, va_list ap)
144         host_t *sobj = HOST(obj);
145         int ret;
147         /* this will consume the first argument (type) of ap */
148         ret = store_obj_init(obj, ap);
149         if (ret)
150                 return ret;
152         sobj->services = sdb_avltree_create();
153         if (! sobj->services)
154                 return -1;
155         sobj->metrics = sdb_avltree_create();
156         if (! sobj->metrics)
157                 return -1;
158         sobj->attributes = sdb_avltree_create();
159         if (! sobj->attributes)
160                 return -1;
161         return 0;
162 } /* host_init */
164 static void
165 host_destroy(sdb_object_t *obj)
167         host_t *sobj = HOST(obj);
168         assert(obj);
170         store_obj_destroy(obj);
172         if (sobj->services)
173                 sdb_avltree_destroy(sobj->services);
174         if (sobj->metrics)
175                 sdb_avltree_destroy(sobj->metrics);
176         if (sobj->attributes)
177                 sdb_avltree_destroy(sobj->attributes);
178 } /* host_destroy */
180 static int
181 service_init(sdb_object_t *obj, va_list ap)
183         service_t *sobj = SVC(obj);
184         int ret;
186         /* this will consume the first argument (type) of ap */
187         ret = store_obj_init(obj, ap);
188         if (ret)
189                 return ret;
191         sobj->attributes = sdb_avltree_create();
192         if (! sobj->attributes)
193                 return -1;
194         return 0;
195 } /* service_init */
197 static void
198 service_destroy(sdb_object_t *obj)
200         service_t *sobj = SVC(obj);
201         assert(obj);
203         store_obj_destroy(obj);
205         if (sobj->attributes)
206                 sdb_avltree_destroy(sobj->attributes);
207 } /* service_destroy */
209 static int
210 metric_init(sdb_object_t *obj, va_list ap)
212         sdb_metric_t *sobj = METRIC(obj);
213         int ret;
215         /* this will consume the first argument (type) of ap */
216         ret = store_obj_init(obj, ap);
217         if (ret)
218                 return ret;
220         sobj->attributes = sdb_avltree_create();
221         if (! sobj->attributes)
222                 return -1;
224         sobj->store.type = sobj->store.id = NULL;
225         return 0;
226 } /* metric_init */
228 static void
229 metric_destroy(sdb_object_t *obj)
231         sdb_metric_t *sobj = METRIC(obj);
232         assert(obj);
234         store_obj_destroy(obj);
236         if (sobj->attributes)
237                 sdb_avltree_destroy(sobj->attributes);
239         if (sobj->store.type)
240                 free(sobj->store.type);
241         if (sobj->store.id)
242                 free(sobj->store.id);
243 } /* metric_destroy */
245 static int
246 attr_init(sdb_object_t *obj, va_list ap)
248         const sdb_data_t *value;
249         int ret;
251         /* this will consume the first two arguments
252          * (type and last_update) of ap */
253         ret = store_obj_init(obj, ap);
254         if (ret)
255                 return ret;
256         value = va_arg(ap, const sdb_data_t *);
258         if (value)
259                 if (sdb_data_copy(&ATTR(obj)->value, value))
260                         return -1;
261         return 0;
262 } /* attr_init */
264 static void
265 attr_destroy(sdb_object_t *obj)
267         assert(obj);
269         store_obj_destroy(obj);
270         sdb_data_free_datum(&ATTR(obj)->value);
271 } /* attr_destroy */
273 static sdb_type_t store_type = {
274         /* size = */ sizeof(sdb_store_t),
275         /* init = */ store_init,
276         /* destroy = */ store_destroy,
277 };
279 static sdb_type_t host_type = {
280         /* size = */ sizeof(host_t),
281         /* init = */ host_init,
282         /* destroy = */ host_destroy
283 };
285 static sdb_type_t service_type = {
286         /* size = */ sizeof(service_t),
287         /* init = */ service_init,
288         /* destroy = */ service_destroy
289 };
291 static sdb_type_t metric_type = {
292         /* size = */ sizeof(sdb_metric_t),
293         /* init = */ metric_init,
294         /* destroy = */ metric_destroy
295 };
297 static sdb_type_t attribute_type = {
298         /* size = */ sizeof(attr_t),
299         /* init = */ attr_init,
300         /* destroy = */ attr_destroy
301 };
303 /*
304  * private helper functions
305  */
307 static int
308 record_backends(sdb_store_obj_t *obj,
309                 const char * const *backends, size_t backends_num)
311         char **tmp;
312         size_t i;
314         for (i = 0; i < backends_num; i++) {
315                 bool found = 0;
316                 size_t j;
318                 for (j = 0; j < obj->backends_num; ++j) {
319                         if (!strcasecmp(obj->backends[j], backends[i])) {
320                                 found = 1;
321                                 break;
322                         }
323                 }
324                 if (found)
325                         continue;
327                 tmp = realloc(obj->backends,
328                                 (obj->backends_num + 1) * sizeof(*obj->backends));
329                 if (! tmp)
330                         return -1;
332                 obj->backends = tmp;
333                 obj->backends[obj->backends_num] = strdup(backends[i]);
334                 if (! obj->backends[obj->backends_num])
335                         return -1;
337                 ++obj->backends_num;
338         }
339         return 0;
340 } /* record_backends */
342 static int
343 store_obj(store_obj_t *obj, sdb_store_obj_t **updated_obj)
345         sdb_store_obj_t *old, *new;
346         int status = 0;
348         assert(obj->parent_tree);
350         if (obj->last_update <= 0)
351                 obj->last_update = sdb_gettime();
353         old = STORE_OBJ(sdb_avltree_lookup(obj->parent_tree, obj->name));
354         if (old) {
355                 if (old->last_update > obj->last_update) {
356                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
357                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
358                                         SDB_STORE_TYPE_TO_NAME(obj->type), obj->name,
359                                         obj->last_update, old->last_update);
360                         /* don't report an error; the object may be updated by multiple
361                          * backends */
362                         status = 1;
363                 }
364                 else if (old->last_update == obj->last_update) {
365                         /* don't report an error and also don't even log this to avoid
366                          * excessive noise on high sampling frequencies */
367                         status = 1;
368                 }
369                 else {
370                         sdb_time_t interval = obj->last_update - old->last_update;
371                         old->last_update = obj->last_update;
372                         if (interval) {
373                                 if (old->interval)
374                                         old->interval = (sdb_time_t)((0.9 * (double)old->interval)
375                                                         + (0.1 * (double)interval));
376                                 else
377                                         old->interval = interval;
378                         }
379                 }
381                 new = old;
382                 sdb_object_deref(SDB_OBJ(old));
383         }
384         else {
385                 if (obj->type == SDB_ATTRIBUTE) {
386                         /* the value will be updated by the caller */
387                         new = STORE_OBJ(sdb_object_create(obj->name, attribute_type,
388                                                 obj->type, obj->last_update, NULL));
389                 }
390                 else {
391                         sdb_type_t t;
392                         t = obj->type == SDB_HOST
393                                 ? host_type
394                                 : obj->type == SDB_SERVICE
395                                         ? service_type
396                                         : metric_type;
397                         new = STORE_OBJ(sdb_object_create(obj->name, t,
398                                                 obj->type, obj->last_update));
399                 }
401                 if (new) {
402                         status = sdb_avltree_insert(obj->parent_tree, SDB_OBJ(new));
404                         /* pass control to the tree or destroy in case of an error */
405                         sdb_object_deref(SDB_OBJ(new));
406                 }
407                 else {
408                         char errbuf[1024];
409                         sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
410                                         SDB_STORE_TYPE_TO_NAME(obj->type), obj->name,
411                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
412                         status = -1;
413                 }
414         }
416         if (status < 0)
417                 return status;
418         assert(new);
420         if (new->parent != obj->parent) {
421                 // Avoid circular self-references which are not handled
422                 // correctly by the ref-count based management layer.
423                 //sdb_object_deref(SDB_OBJ(new->parent));
424                 //sdb_object_ref(SDB_OBJ(obj->parent));
425                 new->parent = obj->parent;
426         }
428         if (updated_obj)
429                 *updated_obj = new;
431         if (record_backends(new, obj->backends, obj->backends_num))
432                 return -1;
433         return status;
434 } /* store_obj */
436 static int
437 store_metric_store(sdb_metric_t *metric, sdb_store_metric_t *m)
439         char *type = metric->store.type;
440         char *id = metric->store.id;
442         if ((! metric->store.type) || strcasecmp(metric->store.type, m->store.type)) {
443                 if (! (type = strdup(m->store.type)))
444                         return -1;
445         }
446         if ((! metric->store.id) || strcasecmp(metric->store.id, m->store.id)) {
447                 if (! (id = strdup(m->store.id))) {
448                         if (type != metric->store.type)
449                                 free(type);
450                         return -1;
451                 }
452         }
454         if (type != metric->store.type) {
455                 if (metric->store.type)
456                         free(metric->store.type);
457                 metric->store.type = type;
458         }
459         if (id != metric->store.id) {
460                 if (metric->store.id)
461                         free(metric->store.id);
462                 metric->store.id = id;
463         }
464         return 0;
465 } /* store_metric_store */
467 /* The store's host_lock has to be acquired before calling this function. */
468 static sdb_avltree_t *
469 get_host_children(host_t *host, int type)
471         if ((type != SDB_SERVICE) && (type != SDB_METRIC)
472                         && (type != SDB_ATTRIBUTE))
473                 return NULL;
475         if (! host)
476                 return NULL;
478         if (type == SDB_ATTRIBUTE)
479                 return host->attributes;
480         else if (type == SDB_METRIC)
481                 return host->metrics;
482         else
483                 return host->services;
484 } /* get_host_children */
486 /*
487  * store writer API
488  */
490 static int
491 store_attribute(sdb_store_attribute_t *attr, sdb_object_t *user_data)
493         sdb_store_t *st = SDB_STORE(user_data);
494         store_obj_t obj = STORE_OBJ_INIT;
495         sdb_store_obj_t *new = NULL;
496         const char *hostname;
497         host_t *host;
499         sdb_avltree_t *children = NULL;
500         int status = 0;
502         if ((! attr) || (! attr->parent) || (! attr->key))
503                 return -1;
505         hostname = attr->hostname;
506         if (attr->parent_type == SDB_HOST)
507                 hostname = attr->parent;
508         if (! hostname)
509                 return -1;
511         pthread_rwlock_wrlock(&st->host_lock);
512         host = HOST(sdb_avltree_lookup(st->hosts, hostname));
513         if (! host) {
514                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
515                                 "host '%s' not found", attr->key, hostname);
516                 status = -1;
517         }
519         switch (attr->parent_type) {
520         case SDB_HOST:
521                 obj.parent = STORE_OBJ(host);
522                 obj.parent_tree = get_host_children(host, SDB_ATTRIBUTE);
523                 break;
524         case SDB_SERVICE:
525                 children = get_host_children(host, SDB_SERVICE);
526                 break;
527         case SDB_METRIC:
528                 children = get_host_children(host, SDB_METRIC);
529                 break;
530         default:
531                 status = -1;
532                 break;
533         }
535         if (children) {
536                 obj.parent = STORE_OBJ(sdb_avltree_lookup(children, attr->parent));
537                 if (! obj.parent) {
538                         sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
539                                         "%s '%s/%s' not found", attr->key,
540                                         SDB_STORE_TYPE_TO_NAME(attr->parent_type),
541                                         attr->hostname, attr->parent);
542                         status = -1;
543                 }
544                 else
545                         obj.parent_tree = attr->parent_type == SDB_SERVICE
546                                 ? SVC(obj.parent)->attributes
547                                 : METRIC(obj.parent)->attributes;
548         }
550         obj.type = SDB_ATTRIBUTE;
551         obj.name = attr->key;
552         obj.last_update = attr->last_update;
553         obj.backends = attr->backends;
554         obj.backends_num = attr->backends_num;
555         if (! status)
556                 status = store_obj(&obj, &new);
558         if (! status) {
559                 assert(new);
560                 /* update the value if it changed */
561                 if (sdb_data_cmp(&ATTR(new)->value, &attr->value))
562                         if (sdb_data_copy(&ATTR(new)->value, &attr->value))
563                                 status = -1;
564         }
566         if (obj.parent != STORE_OBJ(host))
567                 sdb_object_deref(SDB_OBJ(obj.parent));
568         sdb_object_deref(SDB_OBJ(host));
569         pthread_rwlock_unlock(&st->host_lock);
571         return status;
572 } /* store_attribute */
574 static int
575 store_host(sdb_store_host_t *host, sdb_object_t *user_data)
577         sdb_store_t *st = SDB_STORE(user_data);
578         store_obj_t obj = { NULL, st->hosts, SDB_HOST, NULL, 0, NULL, 0 };
579         int status = 0;
581         if ((! host) || (! host->name))
582                 return -1;
584         obj.name = host->name;
585         obj.last_update = host->last_update;
586         obj.backends = host->backends;
587         obj.backends_num = host->backends_num;
588         pthread_rwlock_wrlock(&st->host_lock);
589         status = store_obj(&obj, NULL);
590         pthread_rwlock_unlock(&st->host_lock);
592         return status;
593 } /* store_host */
595 static int
596 store_service(sdb_store_service_t *service, sdb_object_t *user_data)
598         sdb_store_t *st = SDB_STORE(user_data);
599         store_obj_t obj = STORE_OBJ_INIT;
600         host_t *host;
602         int status = 0;
604         if ((! service) || (! service->hostname) || (! service->name))
605                 return -1;
607         pthread_rwlock_wrlock(&st->host_lock);
608         host = HOST(sdb_avltree_lookup(st->hosts, service->hostname));
609         obj.parent = STORE_OBJ(host);
610         obj.parent_tree = get_host_children(host, SDB_SERVICE);
611         obj.type = SDB_SERVICE;
612         if (! obj.parent_tree) {
613                 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
614                                 "host '%s' not found", service->name, service->hostname);
615                 status = -1;
616         }
618         obj.name = service->name;
619         obj.last_update = service->last_update;
620         obj.backends = service->backends;
621         obj.backends_num = service->backends_num;
622         if (! status)
623                 status = store_obj(&obj, NULL);
625         sdb_object_deref(SDB_OBJ(host));
626         pthread_rwlock_unlock(&st->host_lock);
627         return status;
628 } /* store_service */
630 static int
631 store_metric(sdb_store_metric_t *metric, sdb_object_t *user_data)
633         sdb_store_t *st = SDB_STORE(user_data);
634         store_obj_t obj = STORE_OBJ_INIT;
635         sdb_store_obj_t *new = NULL;
636         host_t *host;
638         int status = 0;
640         if ((! metric) || (! metric->hostname) || (! metric->name))
641                 return -1;
643         if ((metric->store.type != NULL) != (metric->store.id != NULL))
644                 return -1;
646         pthread_rwlock_wrlock(&st->host_lock);
647         host = HOST(sdb_avltree_lookup(st->hosts, metric->hostname));
648         obj.parent = STORE_OBJ(host);
649         obj.parent_tree = get_host_children(host, SDB_METRIC);
650         obj.type = SDB_METRIC;
651         if (! obj.parent_tree) {
652                 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
653                                 "host '%s' not found", metric->name, metric->hostname);
654                 status = -1;
655         }
657         obj.name = metric->name;
658         obj.last_update = metric->last_update;
659         obj.backends = metric->backends;
660         obj.backends_num = metric->backends_num;
661         if (! status)
662                 status = store_obj(&obj, &new);
663         sdb_object_deref(SDB_OBJ(host));
665         if (status) {
666                 pthread_rwlock_unlock(&st->host_lock);
667                 return status;
668         }
670         assert(new);
671         if (metric->store.type && metric->store.id)
672                 if (store_metric_store(METRIC(new), metric))
673                         status = -1;
674         pthread_rwlock_unlock(&st->host_lock);
675         return status;
676 } /* store_metric */
678 sdb_store_writer_t sdb_store_writer = {
679         store_host, store_service, store_metric, store_attribute,
680 };
682 static sdb_object_t *
683 prepare_query(sdb_ast_node_t *ast,
684                 sdb_strbuf_t __attribute__((unused)) *errbuf,
685                 sdb_object_t __attribute__((unused)) *user_data)
687         return SDB_OBJ(sdb_store_query_prepare(ast));
688 } /* prepare_query */
690 static int
691 execute_query(sdb_object_t *q,
692                 sdb_store_writer_t *w, sdb_object_t *wd, sdb_strbuf_t *errbuf,
693                 sdb_object_t *user_data)
695         return sdb_store_query_execute(SDB_STORE(user_data),
696                         QUERY(q), w, wd, errbuf);
697 } /* execute_query */
699 sdb_store_reader_t sdb_store_reader = {
700         prepare_query, execute_query,
701 };
703 /*
704  * public API
705  */
707 sdb_store_t *
708 sdb_store_create(void)
710         return SDB_STORE(sdb_object_create("store", store_type));
711 } /* sdb_store_create */
713 int
714 sdb_store_host(sdb_store_t *store, const char *name, sdb_time_t last_update)
716         sdb_store_host_t host = {
717                 name, last_update, 0, NULL, 0,
718         };
719         return store_host(&host, SDB_OBJ(store));
720 } /* sdb_store_host */
722 int
723 sdb_store_service(sdb_store_t *store, const char *hostname, const char *name,
724                 sdb_time_t last_update)
726         sdb_store_service_t service = {
727                 hostname, name, last_update, 0, NULL, 0,
728         };
729         return store_service(&service, SDB_OBJ(store));
730 } /* sdb_store_service */
732 int
733 sdb_store_metric(sdb_store_t *store, const char *hostname, const char *name,
734                 sdb_metric_store_t *metric_store, sdb_time_t last_update)
736         sdb_store_metric_t metric = {
737                 hostname, name, { NULL, NULL }, last_update, 0, NULL, 0,
738         };
739         if (metric_store) {
740                 metric.store.type = metric_store->type;
741                 metric.store.id = metric_store->id;
742         }
743         return store_metric(&metric, SDB_OBJ(store));
744 } /* sdb_store_metric */
746 int
747 sdb_store_attribute(sdb_store_t *store, const char *hostname,
748                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
750         sdb_store_attribute_t attr = {
751                 NULL, SDB_HOST, hostname, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
752         };
753         if (value) {
754                 attr.value = *value;
755         }
756         return store_attribute(&attr, SDB_OBJ(store));
757 } /* sdb_store_attribute */
759 int
760 sdb_store_service_attr(sdb_store_t *store, const char *hostname,
761                 const char *service, const char *key, const sdb_data_t *value,
762                 sdb_time_t last_update)
764         sdb_store_attribute_t attr = {
765                 hostname, SDB_SERVICE, service, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
766         };
767         if (value) {
768                 attr.value = *value;
769         }
770         return store_attribute(&attr, SDB_OBJ(store));
771 } /* sdb_store_service_attr */
773 int
774 sdb_store_metric_attr(sdb_store_t *store, const char *hostname,
775                 const char *metric, const char *key, const sdb_data_t *value,
776                 sdb_time_t last_update)
778         sdb_store_attribute_t attr = {
779                 hostname, SDB_METRIC, metric, key, SDB_DATA_INIT, last_update, 0, NULL, 0,
780         };
781         if (value) {
782                 attr.value = *value;
783         }
784         return store_attribute(&attr, SDB_OBJ(store));
785 } /* sdb_store_metric_attr */
787 sdb_store_obj_t *
788 sdb_store_get_host(sdb_store_t *store, const char *name)
790         host_t *host;
792         if ((! store) || (! name))
793                 return NULL;
795         host = HOST(sdb_avltree_lookup(store->hosts, name));
796         if (! host)
797                 return NULL;
799         return STORE_OBJ(host);
800 } /* sdb_store_get_host */
802 sdb_store_obj_t *
803 sdb_store_get_child(sdb_store_obj_t *host, int type, const char *name)
805         sdb_avltree_t *children;
807         if ((! host) || (host->type != SDB_HOST) || (! name))
808                 return NULL;
810         children = get_host_children(HOST(host), type);
811         if (! children)
812                 return NULL;
813         return STORE_OBJ(sdb_avltree_lookup(children, name));
814 } /* sdb_store_get_child */
816 int
817 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
819         sdb_data_t tmp;
821         if (! obj)
822                 return -1;
824         switch (field) {
825                 case SDB_FIELD_NAME:
826                         tmp.type = SDB_TYPE_STRING;
827                         tmp.data.string = strdup(SDB_OBJ(obj)->name);
828                         if (! tmp.data.string)
829                                 return -1;
830                         break;
831                 case SDB_FIELD_LAST_UPDATE:
832                         tmp.type = SDB_TYPE_DATETIME;
833                         tmp.data.datetime = obj->last_update;
834                         break;
835                 case SDB_FIELD_AGE:
836                         tmp.type = SDB_TYPE_DATETIME;
837                         tmp.data.datetime = sdb_gettime() - obj->last_update;
838                         break;
839                 case SDB_FIELD_INTERVAL:
840                         tmp.type = SDB_TYPE_DATETIME;
841                         tmp.data.datetime = obj->interval;
842                         break;
843                 case SDB_FIELD_BACKEND:
844                         if (! res)
845                                 return 0;
846                         tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
847                         tmp.data.array.length = obj->backends_num;
848                         tmp.data.array.values = obj->backends;
849                         return sdb_data_copy(res, &tmp);
850                 case SDB_FIELD_VALUE:
851                         if (obj->type != SDB_ATTRIBUTE)
852                                 return -1;
853                         if (! res)
854                                 return 0;
855                         return sdb_data_copy(res, &ATTR(obj)->value);
856                 case SDB_FIELD_TIMESERIES:
857                         if (obj->type != SDB_METRIC)
858                                 return -1;
859                         tmp.type = SDB_TYPE_BOOLEAN;
860                         tmp.data.boolean = METRIC(obj)->store.type != NULL;
861                 default:
862                         return -1;
863         }
864         if (res)
865                 *res = tmp;
866         else
867                 sdb_data_free_datum(&tmp);
868         return 0;
869 } /* sdb_store_get_field */
871 int
872 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
873                 sdb_store_matcher_t *filter)
875         sdb_avltree_t *tree = NULL;
876         sdb_store_obj_t *attr;
878         if ((! obj) || (! name))
879                 return -1;
881         if (obj->type == SDB_HOST)
882                 tree = HOST(obj)->attributes;
883         else if (obj->type == SDB_SERVICE)
884                 tree = SVC(obj)->attributes;
885         else if (obj->type == SDB_METRIC)
886                 tree = METRIC(obj)->attributes;
888         if (! tree)
889                 return -1;
891         attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
892         if (! attr)
893                 return -1;
894         if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
895                 sdb_object_deref(SDB_OBJ(attr));
896                 return -1;
897         }
899         assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
900         if (res)
901                 sdb_data_copy(res, &ATTR(attr)->value);
902         sdb_object_deref(SDB_OBJ(attr));
903         return 0;
904 } /* sdb_store_get_attr */
906 int
907 sdb_store_scan(sdb_store_t *store, int type,
908                 sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
909                 sdb_store_lookup_cb cb, void *user_data)
911         sdb_avltree_iter_t *host_iter = NULL;
912         int status = 0;
914         if ((! store) || (! cb))
915                 return -1;
917         if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
918                 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
919                 return -1;
920         }
922         pthread_rwlock_rdlock(&store->host_lock);
923         host_iter = sdb_avltree_get_iter(store->hosts);
924         if (! host_iter)
925                 status = -1;
927         /* has_next returns false if the iterator is NULL */
928         while (sdb_avltree_iter_has_next(host_iter)) {
929                 sdb_store_obj_t *host;
930                 sdb_avltree_iter_t *iter = NULL;
932                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
933                 assert(host);
935                 if (! sdb_store_matcher_matches(filter, host, NULL))
936                         continue;
938                 if (type == SDB_SERVICE)
939                         iter = sdb_avltree_get_iter(HOST(host)->services);
940                 else if (type == SDB_METRIC)
941                         iter = sdb_avltree_get_iter(HOST(host)->metrics);
943                 if (iter) {
944                         while (sdb_avltree_iter_has_next(iter)) {
945                                 sdb_store_obj_t *obj;
946                                 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
947                                 assert(obj);
949                                 if (sdb_store_matcher_matches(m, obj, filter)) {
950                                         if (cb(obj, filter, user_data)) {
951                                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
952                                                                 "an error while scanning");
953                                                 status = -1;
954                                                 break;
955                                         }
956                                 }
957                         }
958                 }
959                 else if (sdb_store_matcher_matches(m, host, filter)) {
960                         if (cb(host, filter, user_data)) {
961                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
962                                                 "an error while scanning");
963                                 status = -1;
964                         }
965                 }
967                 sdb_avltree_iter_destroy(iter);
968                 if (status)
969                         break;
970         }
972         sdb_avltree_iter_destroy(host_iter);
973         pthread_rwlock_unlock(&store->host_lock);
974         return status;
975 } /* sdb_store_scan */
977 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */