Code

2565eca4c27feadab488efecec8e2921c7bfd434
[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;
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)
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)
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)
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)
258         return HOST(sdb_avltree_lookup(hosts, name));
259 } /* lookup_host */
261 static int
262 record_backend(sdb_store_obj_t *obj)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */