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_ATTRIBUTE));
400 if (! hosts)
401 return NULL;
403 cname = sdb_plugin_cname(strdup(hostname));
404 if (! cname) {
405 sdb_log(SDB_LOG_ERR, "store: strdup failed");
406 return NULL;
407 }
409 host = lookup_host(cname);
410 free(cname);
411 if (! host)
412 return NULL;
414 sdb_object_deref(SDB_OBJ(host));
415 if (type == SDB_ATTRIBUTE)
416 return host->attributes;
417 else if (type == SDB_METRIC)
418 return host->metrics;
419 else
420 return host->services;
421 } /* get_host_children */
423 /*
424 * store_common_tojson serializes common object attributes to JSON.
425 *
426 * The function never returns an error. Rather, an error message will be part
427 * of the serialized data.
428 */
429 static void
430 store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
431 {
432 char time_str[64];
433 char interval_str[64];
434 size_t i;
436 if (! sdb_strftime(time_str, sizeof(time_str),
437 "%F %T %z", obj->last_update))
438 snprintf(time_str, sizeof(time_str), "<error>");
439 time_str[sizeof(time_str) - 1] = '\0';
441 if (! sdb_strfinterval(interval_str, sizeof(interval_str),
442 obj->interval))
443 snprintf(interval_str, sizeof(interval_str), "<error>");
444 interval_str[sizeof(interval_str) - 1] = '\0';
446 sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
447 "\"update_interval\": \"%s\", \"backends\": [",
448 time_str, interval_str);
450 for (i = 0; i < obj->backends_num; ++i) {
451 sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
452 if (i < obj->backends_num - 1)
453 sdb_strbuf_append(buf, ",");
454 }
455 sdb_strbuf_append(buf, "]");
456 } /* store_common_tojson */
458 /*
459 * store_obj_tojson serializes attribute / service objects to JSON.
460 *
461 * The function never returns an error. Rather, an error message will be part
462 * of the serialized data.
463 */
464 static void
465 store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf,
466 sdb_store_matcher_t *filter, int flags)
467 {
468 sdb_avltree_iter_t *iter;
470 assert((type == SDB_ATTRIBUTE)
471 || (type == SDB_METRIC)
472 || (type == SDB_SERVICE));
474 sdb_strbuf_append(buf, "[");
475 iter = sdb_avltree_get_iter(tree);
476 if (! iter) {
477 char errbuf[1024];
478 sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
479 SDB_STORE_TYPE_TO_NAME(type),
480 sdb_strerror(errno, errbuf, sizeof(errbuf)));
481 sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
482 SDB_STORE_TYPE_TO_NAME(type), errbuf);
483 }
485 /* has_next returns false if the iterator is NULL */
486 while (sdb_avltree_iter_has_next(iter)) {
487 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
488 assert(sobj);
489 assert(sobj->type == type);
491 if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL)))
492 continue;
494 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name);
495 if (sobj->type == SDB_ATTRIBUTE) {
496 char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1];
497 sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
498 SDB_DOUBLE_QUOTED);
499 sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
500 }
501 store_common_tojson(sobj, buf);
503 if ((sobj->type == SDB_SERVICE)
504 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
505 sdb_strbuf_append(buf, ", \"attributes\": ");
506 store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
507 buf, filter, flags);
508 }
509 else if ((sobj->type == SDB_METRIC)
510 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
511 sdb_strbuf_append(buf, ", \"attributes\": ");
512 store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
513 buf, filter, flags);
514 }
515 sdb_strbuf_append(buf, "}");
517 if (sdb_avltree_iter_has_next(iter))
518 sdb_strbuf_append(buf, ",");
519 }
521 sdb_avltree_iter_destroy(iter);
522 sdb_strbuf_append(buf, "]");
523 } /* store_obj_tojson */
525 /*
526 * public API
527 */
529 void
530 sdb_store_clear(void)
531 {
532 sdb_avltree_destroy(hosts);
533 hosts = NULL;
534 } /* sdb_store_clear */
536 int
537 sdb_store_host(const char *name, sdb_time_t last_update)
538 {
539 char *cname = NULL;
540 int status = 0;
542 if (! name)
543 return -1;
545 cname = sdb_plugin_cname(strdup(name));
546 if (! cname) {
547 sdb_log(SDB_LOG_ERR, "store: strdup failed");
548 return -1;
549 }
551 pthread_rwlock_wrlock(&host_lock);
552 if (! hosts)
553 if (! (hosts = sdb_avltree_create()))
554 status = -1;
556 if (! status)
557 status = store_obj(hosts, SDB_HOST, cname, last_update, NULL);
558 pthread_rwlock_unlock(&host_lock);
560 free(cname);
561 return status;
562 } /* sdb_store_host */
564 _Bool
565 sdb_store_has_host(const char *name)
566 {
567 sdb_host_t *host;
569 if (! name)
570 return NULL;
572 host = lookup_host(name);
573 return host != NULL;
574 } /* sdb_store_has_host */
576 sdb_store_obj_t *
577 sdb_store_get_host(const char *name)
578 {
579 sdb_host_t *host;
581 if (! name)
582 return NULL;
584 host = lookup_host(name);
585 if (! host)
586 return NULL;
588 return STORE_OBJ(host);
589 } /* sdb_store_get_host */
591 int
592 sdb_store_attribute(const char *hostname,
593 const char *key, const sdb_data_t *value,
594 sdb_time_t last_update)
595 {
596 sdb_avltree_t *attrs;
597 int status = 0;
599 if ((! hostname) || (! key))
600 return -1;
602 pthread_rwlock_wrlock(&host_lock);
603 attrs = get_host_children(hostname, SDB_ATTRIBUTE);
604 if (! attrs) {
605 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
606 "host '%s' not found", key, hostname);
607 status = -1;
608 }
610 if (! status)
611 status = store_attr(attrs, key, value, last_update);
613 pthread_rwlock_unlock(&host_lock);
614 return status;
615 } /* sdb_store_attribute */
617 int
618 sdb_store_service(const char *hostname, const char *name,
619 sdb_time_t last_update)
620 {
621 sdb_avltree_t *services;
623 int status = 0;
625 if ((! hostname) || (! name))
626 return -1;
628 pthread_rwlock_wrlock(&host_lock);
629 services = get_host_children(hostname, SDB_SERVICE);
630 if (! services) {
631 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
632 "host '%s' not found", name, hostname);
633 status = -1;
634 }
636 if (! status)
637 status = store_obj(services, SDB_SERVICE, name, last_update, NULL);
638 pthread_rwlock_unlock(&host_lock);
639 return status;
640 } /* sdb_store_service */
642 int
643 sdb_store_service_attr(const char *hostname, const char *service,
644 const char *key, const sdb_data_t *value, sdb_time_t last_update)
645 {
646 sdb_avltree_t *services;
647 sdb_service_t *svc;
648 int status = 0;
650 if ((! hostname) || (! service) || (! key))
651 return -1;
653 pthread_rwlock_wrlock(&host_lock);
654 services = get_host_children(hostname, SDB_SERVICE);
655 if (! services) {
656 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
657 "for service '%s' - host '%ss' not found",
658 key, service, hostname);
659 pthread_rwlock_unlock(&host_lock);
660 return -1;
661 }
663 svc = SVC(sdb_avltree_lookup(services, service));
664 if (! svc) {
665 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
666 "service '%s/%s' not found", key, hostname, service);
667 status = -1;
668 }
670 if (! status)
671 status = store_attr(svc->attributes, key, value, last_update);
673 sdb_object_deref(SDB_OBJ(svc));
674 pthread_rwlock_unlock(&host_lock);
675 return status;
676 } /* sdb_store_service_attr */
678 int
679 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
680 {
681 if ((! obj) || (! res))
682 return -1;
684 switch (field) {
685 case SDB_FIELD_LAST_UPDATE:
686 res->type = SDB_TYPE_DATETIME;
687 res->data.datetime = obj->last_update;
688 break;
689 case SDB_FIELD_AGE:
690 res->type = SDB_TYPE_DATETIME;
691 res->data.datetime = sdb_gettime() - obj->last_update;
692 break;
693 case SDB_FIELD_INTERVAL:
694 res->type = SDB_TYPE_DATETIME;
695 res->data.datetime = obj->interval;
696 break;
697 case SDB_FIELD_BACKEND:
698 /* TODO: add support for storing array values in a data object
699 * for now, fall thru to the error case */
700 default:
701 return -1;
702 }
703 return 0;
704 } /* sdb_store_get_field */
706 int
707 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
708 sdb_store_matcher_t *filter, int flags)
709 {
710 sdb_host_t *host = HOST(h);
712 if ((! h) || (h->type != SDB_HOST) || (! buf))
713 return -1;
715 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
716 store_common_tojson(h, buf);
718 if (! (flags & SDB_SKIP_ATTRIBUTES)) {
719 sdb_strbuf_append(buf, ", \"attributes\": ");
720 store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
721 }
723 if (! (flags & SDB_SKIP_METRICS)) {
724 sdb_strbuf_append(buf, ", \"metrics\": ");
725 store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
726 }
728 if (! (flags & SDB_SKIP_SERVICES)) {
729 sdb_strbuf_append(buf, ", \"services\": ");
730 store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
731 }
733 sdb_strbuf_append(buf, "}");
734 return 0;
735 } /* sdb_store_host_tojson */
737 int
738 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
739 {
740 sdb_avltree_iter_t *host_iter;
741 size_t len;
743 if (! buf)
744 return -1;
746 pthread_rwlock_rdlock(&host_lock);
748 host_iter = sdb_avltree_get_iter(hosts);
749 if (! host_iter) {
750 pthread_rwlock_unlock(&host_lock);
751 return -1;
752 }
754 sdb_strbuf_append(buf, "{\"hosts\":[");
756 len = sdb_strbuf_len(buf);
757 while (sdb_avltree_iter_has_next(host_iter)) {
758 sdb_store_obj_t *host;
760 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
761 assert(host);
763 if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
764 continue;
766 if (sdb_strbuf_len(buf) > len)
767 sdb_strbuf_append(buf, ",");
768 len = sdb_strbuf_len(buf);
770 if (sdb_store_host_tojson(host, buf, filter, flags))
771 return -1;
772 }
774 sdb_strbuf_append(buf, "]}");
776 sdb_avltree_iter_destroy(host_iter);
777 pthread_rwlock_unlock(&host_lock);
778 return 0;
779 } /* sdb_store_tojson */
781 /* TODO: actually support hierarchical data */
782 int
783 sdb_store_iterate(sdb_store_iter_cb cb, void *user_data)
784 {
785 sdb_avltree_iter_t *host_iter;
786 int status = 0;
788 pthread_rwlock_rdlock(&host_lock);
790 host_iter = sdb_avltree_get_iter(hosts);
791 if (! host_iter)
792 status = -1;
794 /* has_next returns false if the iterator is NULL */
795 while (sdb_avltree_iter_has_next(host_iter)) {
796 sdb_store_obj_t *host;
798 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
799 assert(host);
801 if (cb(host, user_data)) {
802 status = -1;
803 break;
804 }
805 }
807 sdb_avltree_iter_destroy(host_iter);
808 pthread_rwlock_unlock(&host_lock);
809 return status;
810 } /* sdb_store_iterate */
812 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */