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)
110 {
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)
125 {
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)
143 {
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)
166 {
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)
182 {
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)
199 {
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)
211 {
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)
230 {
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)
247 {
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)
266 {
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)
310 {
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)
344 {
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)
438 {
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)
470 {
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)
492 {
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)
576 {
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)
597 {
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)
632 {
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)
686 {
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)
694 {
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)
709 {
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)
715 {
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)
725 {
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)
735 {
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)
749 {
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)
763 {
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)
777 {
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)
789 {
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)
804 {
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)
818 {
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)
874 {
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)
910 {
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 : */