Code

store: Added core support for “metrics” objects.
[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 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)
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)
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)
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)
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)
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)
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)
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)
251         return HOST(sdb_avltree_lookup(hosts, name));
252 } /* lookup_host */
254 static int
255 record_backend(sdb_store_obj_t *obj)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */