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 variables
50 */
52 static sdb_avltree_t *hosts = NULL;
53 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
55 /*
56 * private types
57 */
59 static sdb_type_t sdb_host_type;
60 static sdb_type_t sdb_service_type;
61 static sdb_type_t sdb_metric_type;
62 static sdb_type_t sdb_attribute_type;
64 static int
65 store_obj_init(sdb_object_t *obj, va_list ap)
66 {
67 sdb_store_obj_t *sobj = STORE_OBJ(obj);
69 sobj->type = va_arg(ap, int);
71 sobj->last_update = va_arg(ap, sdb_time_t);
72 sobj->interval = 0;
73 sobj->backends = NULL;
74 sobj->backends_num = 0;
75 sobj->parent = NULL;
76 return 0;
77 } /* store_obj_init */
79 static void
80 store_obj_destroy(sdb_object_t *obj)
81 {
82 sdb_store_obj_t *sobj = STORE_OBJ(obj);
83 size_t i;
85 for (i = 0; i < sobj->backends_num; ++i)
86 free(sobj->backends[i]);
87 free(sobj->backends);
88 sobj->backends = NULL;
89 sobj->backends_num = 0;
91 if (sobj->parent)
92 sdb_object_deref(SDB_OBJ(sobj->parent));
93 } /* store_obj_destroy */
95 static int
96 sdb_host_init(sdb_object_t *obj, va_list ap)
97 {
98 sdb_host_t *sobj = HOST(obj);
99 int ret;
101 /* this will consume the first argument (type) of ap */
102 ret = store_obj_init(obj, ap);
103 if (ret)
104 return ret;
106 sobj->services = sdb_avltree_create();
107 if (! sobj->services)
108 return -1;
109 sobj->metrics = sdb_avltree_create();
110 if (! sobj->metrics)
111 return -1;
112 sobj->attributes = sdb_avltree_create();
113 if (! sobj->attributes)
114 return -1;
115 return 0;
116 } /* sdb_host_init */
118 static void
119 sdb_host_destroy(sdb_object_t *obj)
120 {
121 sdb_host_t *sobj = HOST(obj);
122 assert(obj);
124 store_obj_destroy(obj);
126 if (sobj->services)
127 sdb_avltree_destroy(sobj->services);
128 if (sobj->metrics)
129 sdb_avltree_destroy(sobj->metrics);
130 if (sobj->attributes)
131 sdb_avltree_destroy(sobj->attributes);
132 } /* sdb_host_destroy */
134 static int
135 sdb_service_init(sdb_object_t *obj, va_list ap)
136 {
137 sdb_service_t *sobj = SVC(obj);
138 int ret;
140 /* this will consume the first argument (type) of ap */
141 ret = store_obj_init(obj, ap);
142 if (ret)
143 return ret;
145 sobj->attributes = sdb_avltree_create();
146 if (! sobj->attributes)
147 return -1;
148 return 0;
149 } /* sdb_service_init */
151 static void
152 sdb_service_destroy(sdb_object_t *obj)
153 {
154 sdb_service_t *sobj = SVC(obj);
155 assert(obj);
157 store_obj_destroy(obj);
159 if (sobj->attributes)
160 sdb_avltree_destroy(sobj->attributes);
161 } /* sdb_service_destroy */
163 static int
164 sdb_metric_init(sdb_object_t *obj, va_list ap)
165 {
166 sdb_metric_t *sobj = METRIC(obj);
167 int ret;
169 /* this will consume the first argument (type) of ap */
170 ret = store_obj_init(obj, ap);
171 if (ret)
172 return ret;
174 sobj->attributes = sdb_avltree_create();
175 if (! sobj->attributes)
176 return -1;
178 sobj->store.type = sobj->store.id = NULL;
179 return 0;
180 } /* sdb_metric_init */
182 static void
183 sdb_metric_destroy(sdb_object_t *obj)
184 {
185 sdb_metric_t *sobj = METRIC(obj);
186 assert(obj);
188 store_obj_destroy(obj);
190 if (sobj->attributes)
191 sdb_avltree_destroy(sobj->attributes);
193 if (sobj->store.type)
194 free(sobj->store.type);
195 if (sobj->store.id)
196 free(sobj->store.id);
197 } /* sdb_metric_destroy */
199 static int
200 sdb_attr_init(sdb_object_t *obj, va_list ap)
201 {
202 const sdb_data_t *value;
203 int ret;
205 /* this will consume the first two arguments
206 * (type and last_update) of ap */
207 ret = store_obj_init(obj, ap);
208 if (ret)
209 return ret;
210 value = va_arg(ap, const sdb_data_t *);
212 if (value)
213 if (sdb_data_copy(&ATTR(obj)->value, value))
214 return -1;
215 return 0;
216 } /* sdb_attr_init */
218 static void
219 sdb_attr_destroy(sdb_object_t *obj)
220 {
221 assert(obj);
223 store_obj_destroy(obj);
224 sdb_data_free_datum(&ATTR(obj)->value);
225 } /* sdb_attr_destroy */
227 static sdb_type_t sdb_host_type = {
228 sizeof(sdb_host_t),
229 sdb_host_init,
230 sdb_host_destroy
231 };
233 static sdb_type_t sdb_service_type = {
234 sizeof(sdb_service_t),
235 sdb_service_init,
236 sdb_service_destroy
237 };
239 static sdb_type_t sdb_metric_type = {
240 sizeof(sdb_metric_t),
241 sdb_metric_init,
242 sdb_metric_destroy
243 };
245 static sdb_type_t sdb_attribute_type = {
246 sizeof(sdb_attribute_t),
247 sdb_attr_init,
248 sdb_attr_destroy
249 };
251 /*
252 * private helper functions
253 */
255 static sdb_host_t *
256 lookup_host(const char *name)
257 {
258 return HOST(sdb_avltree_lookup(hosts, name));
259 } /* lookup_host */
261 static int
262 record_backend(sdb_store_obj_t *obj)
263 {
264 const sdb_plugin_info_t *info;
265 char **tmp;
266 size_t i;
268 info = sdb_plugin_current();
269 if (! info)
270 return 0;
272 for (i = 0; i < obj->backends_num; ++i)
273 if (!strcasecmp(obj->backends[i], info->plugin_name))
274 return 0;
276 tmp = realloc(obj->backends,
277 (obj->backends_num + 1) * sizeof(*obj->backends));
278 if (! tmp)
279 return -1;
281 obj->backends = tmp;
282 obj->backends[obj->backends_num] = strdup(info->plugin_name);
283 if (! obj->backends[obj->backends_num])
284 return -1;
286 ++obj->backends_num;
287 return 0;
288 } /* record_backend */
290 static int
291 store_obj(sdb_avltree_t *parent_tree, int type, const char *name,
292 sdb_time_t last_update, sdb_store_obj_t **updated_obj)
293 {
294 sdb_store_obj_t *old, *new;
295 int status = 0;
297 assert(parent_tree);
299 if (last_update <= 0)
300 last_update = sdb_gettime();
302 old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
303 if (old) {
304 if (old->last_update > last_update) {
305 sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
306 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
307 SDB_STORE_TYPE_TO_NAME(type), name,
308 last_update, old->last_update);
309 /* don't report an error; the object may be updated by multiple
310 * backends */
311 status = 1;
312 }
313 else if (old->last_update == last_update) {
314 /* don't report an error and also don't even log this to avoid
315 * excessive noise on high sampling frequencies */
316 status = 1;
317 }
318 else {
319 sdb_time_t interval = last_update - old->last_update;
320 old->last_update = last_update;
321 if (interval) {
322 if (old->interval)
323 old->interval = (sdb_time_t)((0.9 * (double)old->interval)
324 + (0.1 * (double)interval));
325 else
326 old->interval = interval;
327 }
328 }
330 new = old;
331 sdb_object_deref(SDB_OBJ(old));
332 }
333 else {
334 if (type == SDB_ATTRIBUTE) {
335 /* the value will be updated by the caller */
336 new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
337 type, last_update, NULL));
338 }
339 else {
340 sdb_type_t t;
341 t = type == SDB_HOST
342 ? sdb_host_type
343 : type == SDB_SERVICE
344 ? sdb_service_type
345 : sdb_metric_type;
346 new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
347 }
349 if (new) {
350 status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
352 /* pass control to the tree or destroy in case of an error */
353 sdb_object_deref(SDB_OBJ(new));
354 }
355 else {
356 char errbuf[1024];
357 sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
358 SDB_STORE_TYPE_TO_NAME(type), name,
359 sdb_strerror(errno, errbuf, sizeof(errbuf)));
360 status = -1;
361 }
362 }
364 if (status < 0)
365 return status;
366 assert(new);
368 if (updated_obj)
369 *updated_obj = new;
371 if (record_backend(new))
372 return -1;
373 return status;
374 } /* store_obj */
376 static int
377 store_attr(sdb_avltree_t *attributes, const char *key, const sdb_data_t *value,
378 sdb_time_t last_update)
379 {
380 sdb_store_obj_t *attr = NULL;
381 int status;
383 status = store_obj(attributes, SDB_ATTRIBUTE, key, last_update, &attr);
384 if (status)
385 return status;
387 /* don't update unchanged values */
388 if (! sdb_data_cmp(&ATTR(attr)->value, value))
389 return status;
391 assert(attr);
392 if (sdb_data_copy(&ATTR(attr)->value, value))
393 return -1;
394 return status;
395 } /* store_attr */
397 /* The host_lock has to be acquired before calling this function. */
398 static sdb_avltree_t *
399 get_host_children(const char *hostname, int type)
400 {
401 char *cname = NULL;
402 sdb_host_t *host;
404 assert(hostname);
405 assert((type == SDB_SERVICE) || (type == SDB_METRIC)
406 || (type == SDB_ATTRIBUTE));
408 if (! hosts)
409 return NULL;
411 cname = sdb_plugin_cname(strdup(hostname));
412 if (! cname) {
413 sdb_log(SDB_LOG_ERR, "store: strdup failed");
414 return NULL;
415 }
417 host = lookup_host(cname);
418 free(cname);
419 if (! host)
420 return NULL;
422 sdb_object_deref(SDB_OBJ(host));
423 if (type == SDB_ATTRIBUTE)
424 return host->attributes;
425 else if (type == SDB_METRIC)
426 return host->metrics;
427 else
428 return host->services;
429 } /* get_host_children */
431 /*
432 * store_common_tojson serializes common object attributes to JSON.
433 *
434 * The function never returns an error. Rather, an error message will be part
435 * of the serialized data.
436 */
437 static void
438 store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
439 {
440 char time_str[64];
441 char interval_str[64];
442 size_t i;
444 if (! sdb_strftime(time_str, sizeof(time_str),
445 "%F %T %z", obj->last_update))
446 snprintf(time_str, sizeof(time_str), "<error>");
447 time_str[sizeof(time_str) - 1] = '\0';
449 if (! sdb_strfinterval(interval_str, sizeof(interval_str),
450 obj->interval))
451 snprintf(interval_str, sizeof(interval_str), "<error>");
452 interval_str[sizeof(interval_str) - 1] = '\0';
454 sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
455 "\"update_interval\": \"%s\", \"backends\": [",
456 time_str, interval_str);
458 for (i = 0; i < obj->backends_num; ++i) {
459 sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
460 if (i < obj->backends_num - 1)
461 sdb_strbuf_append(buf, ",");
462 }
463 sdb_strbuf_append(buf, "]");
464 } /* store_common_tojson */
466 /*
467 * store_obj_tojson serializes attribute / metric / service objects to JSON.
468 *
469 * The function never returns an error. Rather, an error message will be part
470 * of the serialized data.
471 */
472 static void
473 store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf,
474 sdb_store_matcher_t *filter, int flags)
475 {
476 sdb_avltree_iter_t *iter;
478 assert((type == SDB_ATTRIBUTE)
479 || (type == SDB_METRIC)
480 || (type == SDB_SERVICE));
482 sdb_strbuf_append(buf, "[");
483 iter = sdb_avltree_get_iter(tree);
484 if (! iter) {
485 char errbuf[1024];
486 sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
487 SDB_STORE_TYPE_TO_NAME(type),
488 sdb_strerror(errno, errbuf, sizeof(errbuf)));
489 sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
490 SDB_STORE_TYPE_TO_NAME(type), errbuf);
491 }
493 /* has_next returns false if the iterator is NULL */
494 while (sdb_avltree_iter_has_next(iter)) {
495 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
496 assert(sobj);
497 assert(sobj->type == type);
499 if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL)))
500 continue;
502 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name);
503 if (sobj->type == SDB_ATTRIBUTE) {
504 char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1];
505 sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
506 SDB_DOUBLE_QUOTED);
507 sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
508 }
509 store_common_tojson(sobj, buf);
511 if ((sobj->type == SDB_SERVICE)
512 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
513 sdb_strbuf_append(buf, ", \"attributes\": ");
514 store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
515 buf, filter, flags);
516 }
517 else if ((sobj->type == SDB_METRIC)
518 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
519 sdb_strbuf_append(buf, ", \"attributes\": ");
520 store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
521 buf, filter, flags);
522 }
523 sdb_strbuf_append(buf, "}");
525 if (sdb_avltree_iter_has_next(iter))
526 sdb_strbuf_append(buf, ",");
527 }
529 sdb_avltree_iter_destroy(iter);
530 sdb_strbuf_append(buf, "]");
531 } /* store_obj_tojson */
533 /*
534 * public API
535 */
537 void
538 sdb_store_clear(void)
539 {
540 sdb_avltree_destroy(hosts);
541 hosts = NULL;
542 } /* sdb_store_clear */
544 int
545 sdb_store_host(const char *name, sdb_time_t last_update)
546 {
547 char *cname = NULL;
548 int status = 0;
550 if (! name)
551 return -1;
553 cname = sdb_plugin_cname(strdup(name));
554 if (! cname) {
555 sdb_log(SDB_LOG_ERR, "store: strdup failed");
556 return -1;
557 }
559 pthread_rwlock_wrlock(&host_lock);
560 if (! hosts)
561 if (! (hosts = sdb_avltree_create()))
562 status = -1;
564 if (! status)
565 status = store_obj(hosts, SDB_HOST, cname, last_update, NULL);
566 pthread_rwlock_unlock(&host_lock);
568 free(cname);
569 return status;
570 } /* sdb_store_host */
572 _Bool
573 sdb_store_has_host(const char *name)
574 {
575 sdb_host_t *host;
577 if (! name)
578 return NULL;
580 host = lookup_host(name);
581 return host != NULL;
582 } /* sdb_store_has_host */
584 sdb_store_obj_t *
585 sdb_store_get_host(const char *name)
586 {
587 sdb_host_t *host;
589 if (! name)
590 return NULL;
592 host = lookup_host(name);
593 if (! host)
594 return NULL;
596 return STORE_OBJ(host);
597 } /* sdb_store_get_host */
599 int
600 sdb_store_attribute(const char *hostname,
601 const char *key, const sdb_data_t *value,
602 sdb_time_t last_update)
603 {
604 sdb_avltree_t *attrs;
605 int status = 0;
607 if ((! hostname) || (! key))
608 return -1;
610 pthread_rwlock_wrlock(&host_lock);
611 attrs = get_host_children(hostname, SDB_ATTRIBUTE);
612 if (! attrs) {
613 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
614 "host '%s' not found", key, hostname);
615 status = -1;
616 }
618 if (! status)
619 status = store_attr(attrs, key, value, last_update);
621 pthread_rwlock_unlock(&host_lock);
622 return status;
623 } /* sdb_store_attribute */
625 int
626 sdb_store_service(const char *hostname, const char *name,
627 sdb_time_t last_update)
628 {
629 sdb_avltree_t *services;
631 int status = 0;
633 if ((! hostname) || (! name))
634 return -1;
636 pthread_rwlock_wrlock(&host_lock);
637 services = get_host_children(hostname, SDB_SERVICE);
638 if (! services) {
639 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
640 "host '%s' not found", name, hostname);
641 status = -1;
642 }
644 if (! status)
645 status = store_obj(services, SDB_SERVICE, name, last_update, NULL);
646 pthread_rwlock_unlock(&host_lock);
647 return status;
648 } /* sdb_store_service */
650 int
651 sdb_store_service_attr(const char *hostname, const char *service,
652 const char *key, const sdb_data_t *value, sdb_time_t last_update)
653 {
654 sdb_avltree_t *services;
655 sdb_service_t *svc;
656 int status = 0;
658 if ((! hostname) || (! service) || (! key))
659 return -1;
661 pthread_rwlock_wrlock(&host_lock);
662 services = get_host_children(hostname, SDB_SERVICE);
663 if (! services) {
664 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
665 "for service '%s' - host '%ss' not found",
666 key, service, hostname);
667 pthread_rwlock_unlock(&host_lock);
668 return -1;
669 }
671 svc = SVC(sdb_avltree_lookup(services, service));
672 if (! svc) {
673 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
674 "service '%s/%s' not found", key, hostname, service);
675 status = -1;
676 }
678 if (! status)
679 status = store_attr(svc->attributes, key, value, last_update);
681 sdb_object_deref(SDB_OBJ(svc));
682 pthread_rwlock_unlock(&host_lock);
683 return status;
684 } /* sdb_store_service_attr */
686 int
687 sdb_store_metric(const char *hostname, const char *name,
688 sdb_metric_store_t *store, sdb_time_t last_update)
689 {
690 sdb_store_obj_t *obj = NULL;
691 sdb_metric_t *metric;
693 sdb_avltree_t *metrics;
695 int status = 0;
697 if ((! hostname) || (! name))
698 return -1;
699 if (store && ((! store->type) || (! store->id)))
700 return -1;
702 pthread_rwlock_wrlock(&host_lock);
703 metrics = get_host_children(hostname, SDB_METRIC);
704 if (! metrics) {
705 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
706 "host '%s' not found", name, hostname);
707 status = -1;
708 }
710 if (! status)
711 status = store_obj(metrics, SDB_METRIC, name, last_update, &obj);
713 if (status || (! store)) {
714 pthread_rwlock_unlock(&host_lock);
715 return status;
716 }
718 assert(obj);
719 metric = METRIC(obj);
721 if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
722 if (metric->store.type)
723 free(metric->store.type);
724 metric->store.type = strdup(store->type);
725 }
726 if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
727 if (metric->store.id)
728 free(metric->store.id);
729 metric->store.id = strdup(store->id);
730 }
732 if ((! metric->store.type) || (! metric->store.id)) {
733 if (metric->store.type)
734 free(metric->store.type);
735 if (metric->store.id)
736 free(metric->store.id);
737 metric->store.type = metric->store.id = NULL;
738 status = -1;
739 }
740 pthread_rwlock_unlock(&host_lock);
741 return status;
742 } /* sdb_store_metric */
744 int
745 sdb_store_metric_attr(const char *hostname, const char *metric,
746 const char *key, const sdb_data_t *value, sdb_time_t last_update)
747 {
748 sdb_avltree_t *metrics;
749 sdb_metric_t *m;
750 int status = 0;
752 if ((! hostname) || (! metric) || (! key))
753 return -1;
755 pthread_rwlock_wrlock(&host_lock);
756 metrics = get_host_children(hostname, SDB_METRIC);
757 if (! metrics) {
758 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
759 "for metric '%s' - host '%ss' not found",
760 key, metric, hostname);
761 pthread_rwlock_unlock(&host_lock);
762 return -1;
763 }
765 m = METRIC(sdb_avltree_lookup(metrics, metric));
766 if (! m) {
767 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
768 "metric '%s/%s' not found", key, hostname, metric);
769 status = -1;
770 }
772 if (! status)
773 status = store_attr(m->attributes, key, value, last_update);
775 sdb_object_deref(SDB_OBJ(m));
776 pthread_rwlock_unlock(&host_lock);
777 return status;
778 } /* sdb_store_metric_attr */
780 int
781 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
782 {
783 if ((! obj) || (! res))
784 return -1;
786 switch (field) {
787 case SDB_FIELD_LAST_UPDATE:
788 res->type = SDB_TYPE_DATETIME;
789 res->data.datetime = obj->last_update;
790 break;
791 case SDB_FIELD_AGE:
792 res->type = SDB_TYPE_DATETIME;
793 res->data.datetime = sdb_gettime() - obj->last_update;
794 break;
795 case SDB_FIELD_INTERVAL:
796 res->type = SDB_TYPE_DATETIME;
797 res->data.datetime = obj->interval;
798 break;
799 case SDB_FIELD_BACKEND:
800 /* TODO: add support for storing array values in a data object
801 * for now, fall thru to the error case */
802 default:
803 return -1;
804 }
805 return 0;
806 } /* sdb_store_get_field */
808 int
809 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
810 sdb_store_matcher_t *filter, int flags)
811 {
812 sdb_host_t *host = HOST(h);
814 if ((! h) || (h->type != SDB_HOST) || (! buf))
815 return -1;
817 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
818 store_common_tojson(h, buf);
820 if (! (flags & SDB_SKIP_ATTRIBUTES)) {
821 sdb_strbuf_append(buf, ", \"attributes\": ");
822 store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
823 }
825 if (! (flags & SDB_SKIP_METRICS)) {
826 sdb_strbuf_append(buf, ", \"metrics\": ");
827 store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
828 }
830 if (! (flags & SDB_SKIP_SERVICES)) {
831 sdb_strbuf_append(buf, ", \"services\": ");
832 store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
833 }
835 sdb_strbuf_append(buf, "}");
836 return 0;
837 } /* sdb_store_host_tojson */
839 int
840 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
841 {
842 sdb_avltree_iter_t *host_iter;
843 size_t len;
845 if (! buf)
846 return -1;
848 pthread_rwlock_rdlock(&host_lock);
850 host_iter = sdb_avltree_get_iter(hosts);
851 if (! host_iter) {
852 pthread_rwlock_unlock(&host_lock);
853 return -1;
854 }
856 sdb_strbuf_append(buf, "{\"hosts\":[");
858 len = sdb_strbuf_len(buf);
859 while (sdb_avltree_iter_has_next(host_iter)) {
860 sdb_store_obj_t *host;
862 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
863 assert(host);
865 if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
866 continue;
868 if (sdb_strbuf_len(buf) > len)
869 sdb_strbuf_append(buf, ",");
870 len = sdb_strbuf_len(buf);
872 if (sdb_store_host_tojson(host, buf, filter, flags))
873 return -1;
874 }
876 sdb_strbuf_append(buf, "]}");
878 sdb_avltree_iter_destroy(host_iter);
879 pthread_rwlock_unlock(&host_lock);
880 return 0;
881 } /* sdb_store_tojson */
883 /* TODO: actually support hierarchical data */
884 int
885 sdb_store_iterate(sdb_store_iter_cb cb, void *user_data)
886 {
887 sdb_avltree_iter_t *host_iter;
888 int status = 0;
890 pthread_rwlock_rdlock(&host_lock);
892 host_iter = sdb_avltree_get_iter(hosts);
893 if (! host_iter)
894 status = -1;
896 /* has_next returns false if the iterator is NULL */
897 while (sdb_avltree_iter_has_next(host_iter)) {
898 sdb_store_obj_t *host;
900 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
901 assert(host);
903 if (cb(host, user_data)) {
904 status = -1;
905 break;
906 }
907 }
909 sdb_avltree_iter_destroy(host_iter);
910 pthread_rwlock_unlock(&host_lock);
911 return status;
912 } /* sdb_store_iterate */
914 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */