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;
177 return 0;
178 } /* sdb_metric_init */
180 static void
181 sdb_metric_destroy(sdb_object_t *obj)
182 {
183 sdb_metric_t *sobj = METRIC(obj);
184 assert(obj);
186 store_obj_destroy(obj);
188 if (sobj->attributes)
189 sdb_avltree_destroy(sobj->attributes);
190 } /* sdb_metric_destroy */
192 static int
193 sdb_attr_init(sdb_object_t *obj, va_list ap)
194 {
195 const sdb_data_t *value;
196 int ret;
198 /* this will consume the first two arguments
199 * (type and last_update) of ap */
200 ret = store_obj_init(obj, ap);
201 if (ret)
202 return ret;
203 value = va_arg(ap, const sdb_data_t *);
205 if (value)
206 if (sdb_data_copy(&ATTR(obj)->value, value))
207 return -1;
208 return 0;
209 } /* sdb_attr_init */
211 static void
212 sdb_attr_destroy(sdb_object_t *obj)
213 {
214 assert(obj);
216 store_obj_destroy(obj);
217 sdb_data_free_datum(&ATTR(obj)->value);
218 } /* sdb_attr_destroy */
220 static sdb_type_t sdb_host_type = {
221 sizeof(sdb_host_t),
222 sdb_host_init,
223 sdb_host_destroy
224 };
226 static sdb_type_t sdb_service_type = {
227 sizeof(sdb_service_t),
228 sdb_service_init,
229 sdb_service_destroy
230 };
232 static sdb_type_t sdb_metric_type = {
233 sizeof(sdb_metric_t),
234 sdb_metric_init,
235 sdb_metric_destroy
236 };
238 static sdb_type_t sdb_attribute_type = {
239 sizeof(sdb_attribute_t),
240 sdb_attr_init,
241 sdb_attr_destroy
242 };
244 /*
245 * private helper functions
246 */
248 static sdb_host_t *
249 lookup_host(const char *name)
250 {
251 return HOST(sdb_avltree_lookup(hosts, name));
252 } /* lookup_host */
254 static int
255 record_backend(sdb_store_obj_t *obj)
256 {
257 const sdb_plugin_info_t *info;
258 char **tmp;
259 size_t i;
261 info = sdb_plugin_current();
262 if (! info)
263 return 0;
265 for (i = 0; i < obj->backends_num; ++i)
266 if (!strcasecmp(obj->backends[i], info->plugin_name))
267 return 0;
269 tmp = realloc(obj->backends,
270 (obj->backends_num + 1) * sizeof(*obj->backends));
271 if (! tmp)
272 return -1;
274 obj->backends = tmp;
275 obj->backends[obj->backends_num] = strdup(info->plugin_name);
276 if (! obj->backends[obj->backends_num])
277 return -1;
279 ++obj->backends_num;
280 return 0;
281 } /* record_backend */
283 static int
284 store_obj(sdb_avltree_t *parent_tree, int type, const char *name,
285 sdb_time_t last_update, sdb_store_obj_t **updated_obj)
286 {
287 sdb_store_obj_t *old, *new;
288 int status = 0;
290 assert(parent_tree);
292 if (last_update <= 0)
293 last_update = sdb_gettime();
295 old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
296 if (old) {
297 if (old->last_update > last_update) {
298 sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
299 "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
300 SDB_STORE_TYPE_TO_NAME(type), name,
301 last_update, old->last_update);
302 /* don't report an error; the object may be updated by multiple
303 * backends */
304 status = 1;
305 }
306 else if (old->last_update == last_update) {
307 /* don't report an error and also don't even log this to avoid
308 * excessive noise on high sampling frequencies */
309 status = 1;
310 }
311 else {
312 sdb_time_t interval = last_update - old->last_update;
313 old->last_update = last_update;
314 if (interval) {
315 if (old->interval)
316 old->interval = (sdb_time_t)((0.9 * (double)old->interval)
317 + (0.1 * (double)interval));
318 else
319 old->interval = interval;
320 }
321 }
323 new = old;
324 sdb_object_deref(SDB_OBJ(old));
325 }
326 else {
327 if (type == SDB_ATTRIBUTE) {
328 /* the value will be updated by the caller */
329 new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
330 type, last_update, NULL));
331 }
332 else {
333 sdb_type_t t;
334 t = type == SDB_HOST
335 ? sdb_host_type
336 : type == SDB_SERVICE
337 ? sdb_service_type
338 : sdb_metric_type;
339 new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
340 }
342 if (new) {
343 status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
345 /* pass control to the tree or destroy in case of an error */
346 sdb_object_deref(SDB_OBJ(new));
347 }
348 else {
349 char errbuf[1024];
350 sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
351 SDB_STORE_TYPE_TO_NAME(type), name,
352 sdb_strerror(errno, errbuf, sizeof(errbuf)));
353 status = -1;
354 }
355 }
357 if (status < 0)
358 return status;
359 assert(new);
361 if (updated_obj)
362 *updated_obj = new;
364 if (record_backend(new))
365 return -1;
366 return status;
367 } /* store_obj */
369 static int
370 store_attr(sdb_avltree_t *attributes, const char *key, const sdb_data_t *value,
371 sdb_time_t last_update)
372 {
373 sdb_store_obj_t *attr = NULL;
374 int status;
376 status = store_obj(attributes, SDB_ATTRIBUTE, key, last_update, &attr);
377 if (status)
378 return status;
380 /* don't update unchanged values */
381 if (! sdb_data_cmp(&ATTR(attr)->value, value))
382 return status;
384 assert(attr);
385 if (sdb_data_copy(&ATTR(attr)->value, value))
386 return -1;
387 return status;
388 } /* store_attr */
390 /* The host_lock has to be acquired before calling this function. */
391 static sdb_avltree_t *
392 get_host_children(const char *hostname, int type)
393 {
394 char *cname = NULL;
395 sdb_host_t *host;
397 assert(hostname);
398 assert((type == SDB_SERVICE) || (type == SDB_METRIC)
399 || (type == SDB_ATTRIBUTE));
401 if (! hosts)
402 return NULL;
404 cname = sdb_plugin_cname(strdup(hostname));
405 if (! cname) {
406 sdb_log(SDB_LOG_ERR, "store: strdup failed");
407 return NULL;
408 }
410 host = lookup_host(cname);
411 free(cname);
412 if (! host)
413 return NULL;
415 sdb_object_deref(SDB_OBJ(host));
416 if (type == SDB_ATTRIBUTE)
417 return host->attributes;
418 else if (type == SDB_METRIC)
419 return host->metrics;
420 else
421 return host->services;
422 } /* get_host_children */
424 /*
425 * store_common_tojson serializes common object attributes to JSON.
426 *
427 * The function never returns an error. Rather, an error message will be part
428 * of the serialized data.
429 */
430 static void
431 store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
432 {
433 char time_str[64];
434 char interval_str[64];
435 size_t i;
437 if (! sdb_strftime(time_str, sizeof(time_str),
438 "%F %T %z", obj->last_update))
439 snprintf(time_str, sizeof(time_str), "<error>");
440 time_str[sizeof(time_str) - 1] = '\0';
442 if (! sdb_strfinterval(interval_str, sizeof(interval_str),
443 obj->interval))
444 snprintf(interval_str, sizeof(interval_str), "<error>");
445 interval_str[sizeof(interval_str) - 1] = '\0';
447 sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
448 "\"update_interval\": \"%s\", \"backends\": [",
449 time_str, interval_str);
451 for (i = 0; i < obj->backends_num; ++i) {
452 sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
453 if (i < obj->backends_num - 1)
454 sdb_strbuf_append(buf, ",");
455 }
456 sdb_strbuf_append(buf, "]");
457 } /* store_common_tojson */
459 /*
460 * store_obj_tojson serializes attribute / metric / service objects to JSON.
461 *
462 * The function never returns an error. Rather, an error message will be part
463 * of the serialized data.
464 */
465 static void
466 store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf,
467 sdb_store_matcher_t *filter, int flags)
468 {
469 sdb_avltree_iter_t *iter;
471 assert((type == SDB_ATTRIBUTE)
472 || (type == SDB_METRIC)
473 || (type == SDB_SERVICE));
475 sdb_strbuf_append(buf, "[");
476 iter = sdb_avltree_get_iter(tree);
477 if (! iter) {
478 char errbuf[1024];
479 sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
480 SDB_STORE_TYPE_TO_NAME(type),
481 sdb_strerror(errno, errbuf, sizeof(errbuf)));
482 sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
483 SDB_STORE_TYPE_TO_NAME(type), errbuf);
484 }
486 /* has_next returns false if the iterator is NULL */
487 while (sdb_avltree_iter_has_next(iter)) {
488 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
489 assert(sobj);
490 assert(sobj->type == type);
492 if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL)))
493 continue;
495 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name);
496 if (sobj->type == SDB_ATTRIBUTE) {
497 char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1];
498 sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
499 SDB_DOUBLE_QUOTED);
500 sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
501 }
502 store_common_tojson(sobj, buf);
504 if ((sobj->type == SDB_SERVICE)
505 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
506 sdb_strbuf_append(buf, ", \"attributes\": ");
507 store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
508 buf, filter, flags);
509 }
510 else if ((sobj->type == SDB_METRIC)
511 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
512 sdb_strbuf_append(buf, ", \"attributes\": ");
513 store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
514 buf, filter, flags);
515 }
516 sdb_strbuf_append(buf, "}");
518 if (sdb_avltree_iter_has_next(iter))
519 sdb_strbuf_append(buf, ",");
520 }
522 sdb_avltree_iter_destroy(iter);
523 sdb_strbuf_append(buf, "]");
524 } /* store_obj_tojson */
526 /*
527 * public API
528 */
530 void
531 sdb_store_clear(void)
532 {
533 sdb_avltree_destroy(hosts);
534 hosts = NULL;
535 } /* sdb_store_clear */
537 int
538 sdb_store_host(const char *name, sdb_time_t last_update)
539 {
540 char *cname = NULL;
541 int status = 0;
543 if (! name)
544 return -1;
546 cname = sdb_plugin_cname(strdup(name));
547 if (! cname) {
548 sdb_log(SDB_LOG_ERR, "store: strdup failed");
549 return -1;
550 }
552 pthread_rwlock_wrlock(&host_lock);
553 if (! hosts)
554 if (! (hosts = sdb_avltree_create()))
555 status = -1;
557 if (! status)
558 status = store_obj(hosts, SDB_HOST, cname, last_update, NULL);
559 pthread_rwlock_unlock(&host_lock);
561 free(cname);
562 return status;
563 } /* sdb_store_host */
565 _Bool
566 sdb_store_has_host(const char *name)
567 {
568 sdb_host_t *host;
570 if (! name)
571 return NULL;
573 host = lookup_host(name);
574 return host != NULL;
575 } /* sdb_store_has_host */
577 sdb_store_obj_t *
578 sdb_store_get_host(const char *name)
579 {
580 sdb_host_t *host;
582 if (! name)
583 return NULL;
585 host = lookup_host(name);
586 if (! host)
587 return NULL;
589 return STORE_OBJ(host);
590 } /* sdb_store_get_host */
592 int
593 sdb_store_attribute(const char *hostname,
594 const char *key, const sdb_data_t *value,
595 sdb_time_t last_update)
596 {
597 sdb_avltree_t *attrs;
598 int status = 0;
600 if ((! hostname) || (! key))
601 return -1;
603 pthread_rwlock_wrlock(&host_lock);
604 attrs = get_host_children(hostname, SDB_ATTRIBUTE);
605 if (! attrs) {
606 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
607 "host '%s' not found", key, hostname);
608 status = -1;
609 }
611 if (! status)
612 status = store_attr(attrs, key, value, last_update);
614 pthread_rwlock_unlock(&host_lock);
615 return status;
616 } /* sdb_store_attribute */
618 int
619 sdb_store_service(const char *hostname, const char *name,
620 sdb_time_t last_update)
621 {
622 sdb_avltree_t *services;
624 int status = 0;
626 if ((! hostname) || (! name))
627 return -1;
629 pthread_rwlock_wrlock(&host_lock);
630 services = get_host_children(hostname, SDB_SERVICE);
631 if (! services) {
632 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
633 "host '%s' not found", name, hostname);
634 status = -1;
635 }
637 if (! status)
638 status = store_obj(services, SDB_SERVICE, name, last_update, NULL);
639 pthread_rwlock_unlock(&host_lock);
640 return status;
641 } /* sdb_store_service */
643 int
644 sdb_store_service_attr(const char *hostname, const char *service,
645 const char *key, const sdb_data_t *value, sdb_time_t last_update)
646 {
647 sdb_avltree_t *services;
648 sdb_service_t *svc;
649 int status = 0;
651 if ((! hostname) || (! service) || (! key))
652 return -1;
654 pthread_rwlock_wrlock(&host_lock);
655 services = get_host_children(hostname, SDB_SERVICE);
656 if (! services) {
657 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
658 "for service '%s' - host '%ss' not found",
659 key, service, hostname);
660 pthread_rwlock_unlock(&host_lock);
661 return -1;
662 }
664 svc = SVC(sdb_avltree_lookup(services, service));
665 if (! svc) {
666 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
667 "service '%s/%s' not found", key, hostname, service);
668 status = -1;
669 }
671 if (! status)
672 status = store_attr(svc->attributes, key, value, last_update);
674 sdb_object_deref(SDB_OBJ(svc));
675 pthread_rwlock_unlock(&host_lock);
676 return status;
677 } /* sdb_store_service_attr */
679 int
680 sdb_store_metric(const char *hostname, const char *name,
681 sdb_time_t last_update)
682 {
683 sdb_avltree_t *metrics;
685 int status = 0;
687 if ((! hostname) || (! name))
688 return -1;
690 pthread_rwlock_wrlock(&host_lock);
691 metrics = get_host_children(hostname, SDB_METRIC);
692 if (! metrics) {
693 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
694 "host '%s' not found", name, hostname);
695 status = -1;
696 }
698 if (! status)
699 status = store_obj(metrics, SDB_METRIC, name, last_update, NULL);
700 pthread_rwlock_unlock(&host_lock);
701 return status;
702 } /* sdb_store_metric */
704 int
705 sdb_store_metric_attr(const char *hostname, const char *metric,
706 const char *key, const sdb_data_t *value, sdb_time_t last_update)
707 {
708 sdb_avltree_t *metrics;
709 sdb_metric_t *m;
710 int status = 0;
712 if ((! hostname) || (! metric) || (! key))
713 return -1;
715 pthread_rwlock_wrlock(&host_lock);
716 metrics = get_host_children(hostname, SDB_METRIC);
717 if (! metrics) {
718 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
719 "for metric '%s' - host '%ss' not found",
720 key, metric, hostname);
721 pthread_rwlock_unlock(&host_lock);
722 return -1;
723 }
725 m = METRIC(sdb_avltree_lookup(metrics, metric));
726 if (! m) {
727 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
728 "metric '%s/%s' not found", key, hostname, metric);
729 status = -1;
730 }
732 if (! status)
733 status = store_attr(m->attributes, key, value, last_update);
735 sdb_object_deref(SDB_OBJ(m));
736 pthread_rwlock_unlock(&host_lock);
737 return status;
738 } /* sdb_store_metric_attr */
740 int
741 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
742 {
743 if ((! obj) || (! res))
744 return -1;
746 switch (field) {
747 case SDB_FIELD_LAST_UPDATE:
748 res->type = SDB_TYPE_DATETIME;
749 res->data.datetime = obj->last_update;
750 break;
751 case SDB_FIELD_AGE:
752 res->type = SDB_TYPE_DATETIME;
753 res->data.datetime = sdb_gettime() - obj->last_update;
754 break;
755 case SDB_FIELD_INTERVAL:
756 res->type = SDB_TYPE_DATETIME;
757 res->data.datetime = obj->interval;
758 break;
759 case SDB_FIELD_BACKEND:
760 /* TODO: add support for storing array values in a data object
761 * for now, fall thru to the error case */
762 default:
763 return -1;
764 }
765 return 0;
766 } /* sdb_store_get_field */
768 int
769 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
770 sdb_store_matcher_t *filter, int flags)
771 {
772 sdb_host_t *host = HOST(h);
774 if ((! h) || (h->type != SDB_HOST) || (! buf))
775 return -1;
777 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
778 store_common_tojson(h, buf);
780 if (! (flags & SDB_SKIP_ATTRIBUTES)) {
781 sdb_strbuf_append(buf, ", \"attributes\": ");
782 store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
783 }
785 if (! (flags & SDB_SKIP_METRICS)) {
786 sdb_strbuf_append(buf, ", \"metrics\": ");
787 store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
788 }
790 if (! (flags & SDB_SKIP_SERVICES)) {
791 sdb_strbuf_append(buf, ", \"services\": ");
792 store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
793 }
795 sdb_strbuf_append(buf, "}");
796 return 0;
797 } /* sdb_store_host_tojson */
799 int
800 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
801 {
802 sdb_avltree_iter_t *host_iter;
803 size_t len;
805 if (! buf)
806 return -1;
808 pthread_rwlock_rdlock(&host_lock);
810 host_iter = sdb_avltree_get_iter(hosts);
811 if (! host_iter) {
812 pthread_rwlock_unlock(&host_lock);
813 return -1;
814 }
816 sdb_strbuf_append(buf, "{\"hosts\":[");
818 len = sdb_strbuf_len(buf);
819 while (sdb_avltree_iter_has_next(host_iter)) {
820 sdb_store_obj_t *host;
822 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
823 assert(host);
825 if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
826 continue;
828 if (sdb_strbuf_len(buf) > len)
829 sdb_strbuf_append(buf, ",");
830 len = sdb_strbuf_len(buf);
832 if (sdb_store_host_tojson(host, buf, filter, flags))
833 return -1;
834 }
836 sdb_strbuf_append(buf, "]}");
838 sdb_avltree_iter_destroy(host_iter);
839 pthread_rwlock_unlock(&host_lock);
840 return 0;
841 } /* sdb_store_tojson */
843 /* TODO: actually support hierarchical data */
844 int
845 sdb_store_iterate(sdb_store_iter_cb cb, void *user_data)
846 {
847 sdb_avltree_iter_t *host_iter;
848 int status = 0;
850 pthread_rwlock_rdlock(&host_lock);
852 host_iter = sdb_avltree_get_iter(hosts);
853 if (! host_iter)
854 status = -1;
856 /* has_next returns false if the iterator is NULL */
857 while (sdb_avltree_iter_has_next(host_iter)) {
858 sdb_store_obj_t *host;
860 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
861 assert(host);
863 if (cb(host, user_data)) {
864 status = -1;
865 break;
866 }
867 }
869 sdb_avltree_iter_destroy(host_iter);
870 pthread_rwlock_unlock(&host_lock);
871 return status;
872 } /* sdb_store_iterate */
874 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */