Code

store: Record each object's parent.
[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         // We don't currently keep an extra reference for parent objects to
92         // avoid circular self-references which are not handled correctly by
93         // the ref-count base management layer.
94         //sdb_object_deref(SDB_OBJ(sobj->parent));
95 } /* store_obj_destroy */
97 static int
98 sdb_host_init(sdb_object_t *obj, va_list ap)
99 {
100         sdb_host_t *sobj = HOST(obj);
101         int ret;
103         /* this will consume the first argument (type) of ap */
104         ret = store_obj_init(obj, ap);
105         if (ret)
106                 return ret;
108         sobj->services = sdb_avltree_create();
109         if (! sobj->services)
110                 return -1;
111         sobj->metrics = sdb_avltree_create();
112         if (! sobj->metrics)
113                 return -1;
114         sobj->attributes = sdb_avltree_create();
115         if (! sobj->attributes)
116                 return -1;
117         return 0;
118 } /* sdb_host_init */
120 static void
121 sdb_host_destroy(sdb_object_t *obj)
123         sdb_host_t *sobj = HOST(obj);
124         assert(obj);
126         store_obj_destroy(obj);
128         if (sobj->services)
129                 sdb_avltree_destroy(sobj->services);
130         if (sobj->metrics)
131                 sdb_avltree_destroy(sobj->metrics);
132         if (sobj->attributes)
133                 sdb_avltree_destroy(sobj->attributes);
134 } /* sdb_host_destroy */
136 static int
137 sdb_service_init(sdb_object_t *obj, va_list ap)
139         sdb_service_t *sobj = SVC(obj);
140         int ret;
142         /* this will consume the first argument (type) of ap */
143         ret = store_obj_init(obj, ap);
144         if (ret)
145                 return ret;
147         sobj->attributes = sdb_avltree_create();
148         if (! sobj->attributes)
149                 return -1;
150         return 0;
151 } /* sdb_service_init */
153 static void
154 sdb_service_destroy(sdb_object_t *obj)
156         sdb_service_t *sobj = SVC(obj);
157         assert(obj);
159         store_obj_destroy(obj);
161         if (sobj->attributes)
162                 sdb_avltree_destroy(sobj->attributes);
163 } /* sdb_service_destroy */
165 static int
166 sdb_metric_init(sdb_object_t *obj, va_list ap)
168         sdb_metric_t *sobj = METRIC(obj);
169         int ret;
171         /* this will consume the first argument (type) of ap */
172         ret = store_obj_init(obj, ap);
173         if (ret)
174                 return ret;
176         sobj->attributes = sdb_avltree_create();
177         if (! sobj->attributes)
178                 return -1;
180         sobj->store.type = sobj->store.id = NULL;
181         return 0;
182 } /* sdb_metric_init */
184 static void
185 sdb_metric_destroy(sdb_object_t *obj)
187         sdb_metric_t *sobj = METRIC(obj);
188         assert(obj);
190         store_obj_destroy(obj);
192         if (sobj->attributes)
193                 sdb_avltree_destroy(sobj->attributes);
195         if (sobj->store.type)
196                 free(sobj->store.type);
197         if (sobj->store.id)
198                 free(sobj->store.id);
199 } /* sdb_metric_destroy */
201 static int
202 sdb_attr_init(sdb_object_t *obj, va_list ap)
204         const sdb_data_t *value;
205         int ret;
207         /* this will consume the first two arguments
208          * (type and last_update) of ap */
209         ret = store_obj_init(obj, ap);
210         if (ret)
211                 return ret;
212         value = va_arg(ap, const sdb_data_t *);
214         if (value)
215                 if (sdb_data_copy(&ATTR(obj)->value, value))
216                         return -1;
217         return 0;
218 } /* sdb_attr_init */
220 static void
221 sdb_attr_destroy(sdb_object_t *obj)
223         assert(obj);
225         store_obj_destroy(obj);
226         sdb_data_free_datum(&ATTR(obj)->value);
227 } /* sdb_attr_destroy */
229 static sdb_type_t sdb_host_type = {
230         sizeof(sdb_host_t),
231         sdb_host_init,
232         sdb_host_destroy
233 };
235 static sdb_type_t sdb_service_type = {
236         sizeof(sdb_service_t),
237         sdb_service_init,
238         sdb_service_destroy
239 };
241 static sdb_type_t sdb_metric_type = {
242         sizeof(sdb_metric_t),
243         sdb_metric_init,
244         sdb_metric_destroy
245 };
247 static sdb_type_t sdb_attribute_type = {
248         sizeof(sdb_attribute_t),
249         sdb_attr_init,
250         sdb_attr_destroy
251 };
253 /*
254  * private helper functions
255  */
257 static sdb_host_t *
258 lookup_host(const char *name, _Bool canonicalize)
260         sdb_host_t *host;
261         char *cname;
263         assert(name);
264         if (! canonicalize)
265                 return HOST(sdb_avltree_lookup(hosts, name));
267         cname = strdup(name);
268         cname = sdb_plugin_cname(cname);
269         if (! cname) {
270                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
271                 return NULL;
272         }
274         host = HOST(sdb_avltree_lookup(hosts, cname));
275         free(cname);
276         return host;
277 } /* lookup_host */
279 static int
280 record_backend(sdb_store_obj_t *obj)
282         const sdb_plugin_info_t *info;
283         char **tmp;
284         size_t i;
286         info = sdb_plugin_current();
287         if (! info)
288                 return 0;
290         for (i = 0; i < obj->backends_num; ++i)
291                 if (!strcasecmp(obj->backends[i], info->plugin_name))
292                         return 0;
294         tmp = realloc(obj->backends,
295                         (obj->backends_num + 1) * sizeof(*obj->backends));
296         if (! tmp)
297                 return -1;
299         obj->backends = tmp;
300         obj->backends[obj->backends_num] = strdup(info->plugin_name);
301         if (! obj->backends[obj->backends_num])
302                 return -1;
304         ++obj->backends_num;
305         return 0;
306 } /* record_backend */
308 static int
309 store_obj(sdb_store_obj_t *parent, sdb_avltree_t *parent_tree,
310                 int type, const char *name, sdb_time_t last_update,
311                 sdb_store_obj_t **updated_obj)
313         sdb_store_obj_t *old, *new;
314         int status = 0;
316         assert(parent_tree);
318         if (last_update <= 0)
319                 last_update = sdb_gettime();
321         old = STORE_OBJ(sdb_avltree_lookup(parent_tree, name));
322         if (old) {
323                 if (old->last_update > last_update) {
324                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update %s '%s' - "
325                                         "value too old (%"PRIsdbTIME" < %"PRIsdbTIME")",
326                                         SDB_STORE_TYPE_TO_NAME(type), name,
327                                         last_update, old->last_update);
328                         /* don't report an error; the object may be updated by multiple
329                          * backends */
330                         status = 1;
331                 }
332                 else if (old->last_update == last_update) {
333                         /* don't report an error and also don't even log this to avoid
334                          * excessive noise on high sampling frequencies */
335                         status = 1;
336                 }
337                 else {
338                         sdb_time_t interval = last_update - old->last_update;
339                         old->last_update = last_update;
340                         if (interval) {
341                                 if (old->interval)
342                                         old->interval = (sdb_time_t)((0.9 * (double)old->interval)
343                                                         + (0.1 * (double)interval));
344                                 else
345                                         old->interval = interval;
346                         }
347                 }
349                 new = old;
350                 sdb_object_deref(SDB_OBJ(old));
351         }
352         else {
353                 if (type == SDB_ATTRIBUTE) {
354                         /* the value will be updated by the caller */
355                         new = STORE_OBJ(sdb_object_create(name, sdb_attribute_type,
356                                                 type, last_update, NULL));
357                 }
358                 else {
359                         sdb_type_t t;
360                         t = type == SDB_HOST
361                                 ? sdb_host_type
362                                 : type == SDB_SERVICE
363                                         ? sdb_service_type
364                                         : sdb_metric_type;
365                         new = STORE_OBJ(sdb_object_create(name, t, type, last_update));
366                 }
368                 if (new) {
369                         status = sdb_avltree_insert(parent_tree, SDB_OBJ(new));
371                         /* pass control to the tree or destroy in case of an error */
372                         sdb_object_deref(SDB_OBJ(new));
373                 }
374                 else {
375                         char errbuf[1024];
376                         sdb_log(SDB_LOG_ERR, "store: Failed to create %s '%s': %s",
377                                         SDB_STORE_TYPE_TO_NAME(type), name,
378                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
379                         status = -1;
380                 }
381         }
383         if (status < 0)
384                 return status;
385         assert(new);
387         if (new->parent != parent) {
388                 // Avoid circular self-references which are not handled
389                 // correctly by the ref-count based management layer.
390                 //sdb_object_deref(SDB_OBJ(new->parent));
391                 //sdb_object_ref(SDB_OBJ(parent));
392                 new->parent = parent;
393         }
395         if (updated_obj)
396                 *updated_obj = new;
398         if (record_backend(new))
399                 return -1;
400         return status;
401 } /* store_obj */
403 static int
404 store_attr(sdb_store_obj_t *parent, sdb_avltree_t *attributes,
405                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
407         sdb_store_obj_t *attr = NULL;
408         int status;
410         status = store_obj(parent, attributes, SDB_ATTRIBUTE,
411                         key, last_update, &attr);
412         if (status)
413                 return status;
415         /* don't update unchanged values */
416         if (! sdb_data_cmp(&ATTR(attr)->value, value))
417                 return status;
419         assert(attr);
420         if (sdb_data_copy(&ATTR(attr)->value, value))
421                 return -1;
422         return status;
423 } /* store_attr */
425 /* The host_lock has to be acquired before calling this function. */
426 static sdb_avltree_t *
427 get_host_children(sdb_host_t *host, int type)
429         assert((type == SDB_SERVICE) || (type == SDB_METRIC)
430                         || (type == SDB_ATTRIBUTE));
432         if (! host)
433                 return NULL;
435         if (type == SDB_ATTRIBUTE)
436                 return host->attributes;
437         else if (type == SDB_METRIC)
438                 return host->metrics;
439         else
440                 return host->services;
441 } /* get_host_children */
443 /*
444  * store_common_tojson serializes common object attributes to JSON.
445  *
446  * The function never returns an error. Rather, an error message will be part
447  * of the serialized data.
448  */
449 static void
450 store_common_tojson(sdb_store_obj_t *obj, sdb_strbuf_t *buf)
452         char time_str[64];
453         char interval_str[64];
454         size_t i;
456         /* TODO: make time and interval formats configurable */
457         if (! sdb_strftime(time_str, sizeof(time_str),
458                                 "%F %T %z", obj->last_update))
459                 snprintf(time_str, sizeof(time_str), "<error>");
460         time_str[sizeof(time_str) - 1] = '\0';
462         if (! sdb_strfinterval(interval_str, sizeof(interval_str),
463                                 obj->interval))
464                 snprintf(interval_str, sizeof(interval_str), "<error>");
465         interval_str[sizeof(interval_str) - 1] = '\0';
467         sdb_strbuf_append(buf, "\"last_update\": \"%s\", "
468                         "\"update_interval\": \"%s\", \"backends\": [",
469                         time_str, interval_str);
471         for (i = 0; i < obj->backends_num; ++i) {
472                 sdb_strbuf_append(buf, "\"%s\"", obj->backends[i]);
473                 if (i < obj->backends_num - 1)
474                         sdb_strbuf_append(buf, ",");
475         }
476         sdb_strbuf_append(buf, "]");
477 } /* store_common_tojson */
479 /*
480  * store_obj_tojson serializes attribute / metric / service objects to JSON.
481  *
482  * The function never returns an error. Rather, an error message will be part
483  * of the serialized data.
484  */
485 static void
486 store_obj_tojson(sdb_avltree_t *tree, int type, sdb_strbuf_t *buf,
487                 sdb_store_matcher_t *filter, int flags)
489         sdb_avltree_iter_t *iter;
491         assert((type == SDB_ATTRIBUTE)
492                         || (type == SDB_METRIC)
493                         || (type == SDB_SERVICE));
495         sdb_strbuf_append(buf, "[");
496         iter = sdb_avltree_get_iter(tree);
497         if (! iter) {
498                 char errbuf[1024];
499                 sdb_log(SDB_LOG_ERR, "store: Failed to retrieve %ss: %s\n",
500                                 SDB_STORE_TYPE_TO_NAME(type),
501                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
502                 sdb_strbuf_append(buf, "{\"error\": \"failed to retrieve %ss: %s\"}",
503                                 SDB_STORE_TYPE_TO_NAME(type), errbuf);
504         }
506         /* has_next returns false if the iterator is NULL */
507         while (sdb_avltree_iter_has_next(iter)) {
508                 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
509                 assert(sobj);
510                 assert(sobj->type == type);
512                 if (filter && (! sdb_store_matcher_matches(filter, sobj, NULL)))
513                         continue;
515                 sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(sobj)->name);
516                 if (sobj->type == SDB_ATTRIBUTE) {
517                         char tmp[sdb_data_strlen(&ATTR(sobj)->value) + 1];
518                         sdb_data_format(&ATTR(sobj)->value, tmp, sizeof(tmp),
519                                         SDB_DOUBLE_QUOTED);
520                         sdb_strbuf_append(buf, "\"value\": %s, ", tmp);
521                 }
522                 store_common_tojson(sobj, buf);
524                 if ((sobj->type == SDB_SERVICE)
525                                 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
526                         sdb_strbuf_append(buf, ", \"attributes\": ");
527                         store_obj_tojson(SVC(sobj)->attributes, SDB_ATTRIBUTE,
528                                         buf, filter, flags);
529                 }
530                 else if ((sobj->type == SDB_METRIC)
531                                 && (! (flags & SDB_SKIP_ATTRIBUTES))) {
532                         sdb_strbuf_append(buf, ", \"attributes\": ");
533                         store_obj_tojson(METRIC(sobj)->attributes, SDB_ATTRIBUTE,
534                                         buf, filter, flags);
535                 }
536                 sdb_strbuf_append(buf, "}");
538                 if (sdb_avltree_iter_has_next(iter))
539                         sdb_strbuf_append(buf, ",");
540         }
542         sdb_avltree_iter_destroy(iter);
543         sdb_strbuf_append(buf, "]");
544 } /* store_obj_tojson */
546 /*
547  * ts_tojson serializes a time-series to JSON.
548  *
549  * The function never returns an error. Rather, an error message will be part
550  * of the serialized data.
551  */
552 static void
553 ts_tojson(sdb_timeseries_t *ts, sdb_strbuf_t *buf)
555         char start_str[64];
556         char end_str[64];
558         size_t i;
560         /* TODO: make time format configurable */
561         if (! sdb_strftime(start_str, sizeof(start_str),
562                                 "%F %T %z", ts->start))
563                 snprintf(start_str, sizeof(start_str), "<error>");
564         start_str[sizeof(start_str) - 1] = '\0';
565         if (! sdb_strftime(end_str, sizeof(end_str),
566                                 "%F %T %z", ts->end))
567                 snprintf(end_str, sizeof(end_str), "<error>");
568         end_str[sizeof(end_str) - 1] = '\0';
570         sdb_strbuf_append(buf, "{\"start\": \"%s\", \"end\": \"%s\", \"data\": {",
571                         start_str, end_str);
573         for (i = 0; i < ts->data_names_len; ++i) {
574                 size_t j;
575                 sdb_strbuf_append(buf, "\"%s\": [", ts->data_names[i]);
577                 for (j = 0; j < ts->data_len; ++j) {
578                         char time_str[64];
580                         if (! sdb_strftime(time_str, sizeof(time_str),
581                                                 "%F %T %z", ts->data[i][j].timestamp))
582                                 snprintf(time_str, sizeof(time_str), "<error>");
583                         time_str[sizeof(time_str) - 1] = '\0';
585                         sdb_strbuf_append(buf, "{\"timestamp\": \"%s\", "
586                                         "\"value\": \"%f\"}", time_str, ts->data[i][j].value);
588                         if (j < ts->data_len - 1)
589                                 sdb_strbuf_append(buf, ",");
590                 }
592                 if (i < ts->data_names_len - 1)
593                         sdb_strbuf_append(buf, "],");
594                 else
595                         sdb_strbuf_append(buf, "]");
596         }
597         sdb_strbuf_append(buf, "}}");
598 } /* ts_tojson */
600 /*
601  * public API
602  */
604 void
605 sdb_store_clear(void)
607         sdb_avltree_destroy(hosts);
608         hosts = NULL;
609 } /* sdb_store_clear */
611 int
612 sdb_store_host(const char *name, sdb_time_t last_update)
614         char *cname = NULL;
615         int status = 0;
617         if (! name)
618                 return -1;
620         cname = sdb_plugin_cname(strdup(name));
621         if (! cname) {
622                 sdb_log(SDB_LOG_ERR, "store: strdup failed");
623                 return -1;
624         }
626         pthread_rwlock_wrlock(&host_lock);
627         if (! hosts)
628                 if (! (hosts = sdb_avltree_create()))
629                         status = -1;
631         if (! status)
632                 status = store_obj(NULL, hosts, SDB_HOST, cname, last_update, NULL);
633         pthread_rwlock_unlock(&host_lock);
635         free(cname);
636         return status;
637 } /* sdb_store_host */
639 _Bool
640 sdb_store_has_host(const char *name)
642         sdb_host_t *host;
644         if (! name)
645                 return NULL;
647         host = lookup_host(name, /* canonicalize = */ 0);
648         sdb_object_deref(SDB_OBJ(host));
649         return host != NULL;
650 } /* sdb_store_has_host */
652 sdb_store_obj_t *
653 sdb_store_get_host(const char *name)
655         sdb_host_t *host;
657         if (! name)
658                 return NULL;
660         host = lookup_host(name, /* canonicalize = */ 0);
661         if (! host)
662                 return NULL;
664         return STORE_OBJ(host);
665 } /* sdb_store_get_host */
667 int
668 sdb_store_attribute(const char *hostname,
669                 const char *key, const sdb_data_t *value,
670                 sdb_time_t last_update)
672         sdb_host_t *host;
673         sdb_avltree_t *attrs;
674         int status = 0;
676         if ((! hostname) || (! key))
677                 return -1;
679         pthread_rwlock_wrlock(&host_lock);
680         host = lookup_host(hostname, /* canonicalize = */ 1);
681         attrs = get_host_children(host, SDB_ATTRIBUTE);
682         if (! attrs) {
683                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
684                                 "host '%s' not found", key, hostname);
685                 status = -1;
686         }
688         if (! status)
689                 status = store_attr(STORE_OBJ(host), attrs, key, value, last_update);
691         sdb_object_deref(SDB_OBJ(host));
692         pthread_rwlock_unlock(&host_lock);
693         return status;
694 } /* sdb_store_attribute */
696 int
697 sdb_store_service(const char *hostname, const char *name,
698                 sdb_time_t last_update)
700         sdb_host_t *host;
701         sdb_avltree_t *services;
703         int status = 0;
705         if ((! hostname) || (! name))
706                 return -1;
708         pthread_rwlock_wrlock(&host_lock);
709         host = lookup_host(hostname, /* canonicalize = */ 1);
710         services = get_host_children(host, SDB_SERVICE);
711         if (! services) {
712                 sdb_log(SDB_LOG_ERR, "store: Failed to store service '%s' - "
713                                 "host '%s' not found", name, hostname);
714                 status = -1;
715         }
717         if (! status)
718                 status = store_obj(STORE_OBJ(host), services, SDB_SERVICE,
719                                 name, last_update, NULL);
721         sdb_object_deref(SDB_OBJ(host));
722         pthread_rwlock_unlock(&host_lock);
723         return status;
724 } /* sdb_store_service */
726 int
727 sdb_store_service_attr(const char *hostname, const char *service,
728                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
730         sdb_host_t *host;
731         sdb_service_t *svc;
732         sdb_avltree_t *services;
733         int status = 0;
735         if ((! hostname) || (! service) || (! key))
736                 return -1;
738         pthread_rwlock_wrlock(&host_lock);
739         host = lookup_host(hostname, /* canonicalize = */ 1);
740         services = get_host_children(host, SDB_SERVICE);
741         sdb_object_deref(SDB_OBJ(host));
742         if (! services) {
743                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
744                                 "for service '%s' - host '%ss' not found",
745                                 key, service, hostname);
746                 pthread_rwlock_unlock(&host_lock);
747                 return -1;
748         }
750         svc = SVC(sdb_avltree_lookup(services, service));
751         if (! svc) {
752                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
753                                 "service '%s/%s' not found", key, hostname, service);
754                 status = -1;
755         }
757         if (! status)
758                 status = store_attr(STORE_OBJ(svc), svc->attributes,
759                                 key, value, last_update);
761         sdb_object_deref(SDB_OBJ(svc));
762         pthread_rwlock_unlock(&host_lock);
763         return status;
764 } /* sdb_store_service_attr */
766 int
767 sdb_store_metric(const char *hostname, const char *name,
768                 sdb_metric_store_t *store, sdb_time_t last_update)
770         sdb_store_obj_t *obj = NULL;
771         sdb_host_t *host;
772         sdb_metric_t *metric;
774         sdb_avltree_t *metrics;
776         int status = 0;
778         if ((! hostname) || (! name))
779                 return -1;
780         if (store && ((! store->type) || (! store->id)))
781                 return -1;
783         pthread_rwlock_wrlock(&host_lock);
784         host = lookup_host(hostname, /* canonicalize = */ 1);
785         metrics = get_host_children(host, SDB_METRIC);
786         if (! metrics) {
787                 sdb_log(SDB_LOG_ERR, "store: Failed to store metric '%s' - "
788                                 "host '%s' not found", name, hostname);
789                 status = -1;
790         }
792         if (! status)
793                 status = store_obj(STORE_OBJ(host), metrics, SDB_METRIC,
794                                 name, last_update, &obj);
795         sdb_object_deref(SDB_OBJ(host));
797         if (status || (! store)) {
798                 pthread_rwlock_unlock(&host_lock);
799                 return status;
800         }
802         assert(obj);
803         metric = METRIC(obj);
805         if ((! metric->store.type) || strcasecmp(metric->store.type, store->type)) {
806                 if (metric->store.type)
807                         free(metric->store.type);
808                 metric->store.type = strdup(store->type);
809         }
810         if ((! metric->store.id) || strcasecmp(metric->store.id, store->id)) {
811                 if (metric->store.id)
812                         free(metric->store.id);
813                 metric->store.id = strdup(store->id);
814         }
816         if ((! metric->store.type) || (! metric->store.id)) {
817                 if (metric->store.type)
818                         free(metric->store.type);
819                 if (metric->store.id)
820                         free(metric->store.id);
821                 metric->store.type = metric->store.id = NULL;
822                 status = -1;
823         }
824         pthread_rwlock_unlock(&host_lock);
825         return status;
826 } /* sdb_store_metric */
828 int
829 sdb_store_metric_attr(const char *hostname, const char *metric,
830                 const char *key, const sdb_data_t *value, sdb_time_t last_update)
832         sdb_avltree_t *metrics;
833         sdb_host_t *host;
834         sdb_metric_t *m;
835         int status = 0;
837         if ((! hostname) || (! metric) || (! key))
838                 return -1;
840         pthread_rwlock_wrlock(&host_lock);
841         host = lookup_host(hostname, /* canonicalize = */ 1);
842         metrics = get_host_children(host, SDB_METRIC);
843         sdb_object_deref(SDB_OBJ(host));
844         if (! metrics) {
845                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' "
846                                 "for metric '%s' - host '%s' not found",
847                                 key, metric, hostname);
848                 pthread_rwlock_unlock(&host_lock);
849                 return -1;
850         }
852         m = METRIC(sdb_avltree_lookup(metrics, metric));
853         if (! m) {
854                 sdb_log(SDB_LOG_ERR, "store: Failed to store attribute '%s' - "
855                                 "metric '%s/%s' not found", key, hostname, metric);
856                 status = -1;
857         }
859         if (! status)
860                 status = store_attr(STORE_OBJ(m), m->attributes,
861                                 key, value, last_update);
863         sdb_object_deref(SDB_OBJ(m));
864         pthread_rwlock_unlock(&host_lock);
865         return status;
866 } /* sdb_store_metric_attr */
868 int
869 sdb_store_fetch_timeseries(const char *hostname, const char *metric,
870                 sdb_timeseries_opts_t *opts, sdb_strbuf_t *buf)
872         sdb_avltree_t *metrics;
873         sdb_host_t *host;
874         sdb_metric_t *m;
876         sdb_timeseries_t *ts;
878         if ((! hostname) || (! metric) || (! opts) || (! buf))
879                 return -1;
881         pthread_rwlock_rdlock(&host_lock);
882         host = lookup_host(hostname, /* canonicalize = */ 1);
883         metrics = get_host_children(host, SDB_METRIC);
884         sdb_object_deref(SDB_OBJ(host));
885         if (! metrics) {
886                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
887                                 "- host '%s' not found", hostname, metric, hostname);
888                 pthread_rwlock_unlock(&host_lock);
889                 return -1;
890         }
892         m = METRIC(sdb_avltree_lookup(metrics, metric));
893         if (! m) {
894                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
895                                 "- metric '%s' not found", hostname, metric, metric);
896                 pthread_rwlock_unlock(&host_lock);
897                 return -1;
898         }
900         if ((! m->store.type) || (! m->store.id)) {
901                 sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
902                                 "- no data-store configured for the stored metric",
903                                 hostname, metric);
904                 pthread_rwlock_unlock(&host_lock);
905                 return -1;
906         }
908         {
909                 char type[strlen(m->store.type) + 1];
910                 char id[strlen(m->store.id) + 1];
912                 strncpy(type, m->store.type, sizeof(type));
913                 strncpy(id, m->store.id, sizeof(id));
914                 pthread_rwlock_unlock(&host_lock);
916                 ts = sdb_plugin_fetch_timeseries(type, id, opts);
917                 if (! ts) {
918                         sdb_log(SDB_LOG_ERR, "store: Failed to fetch time-series '%s/%s' "
919                                         "- %s fetcher callback returned no data for '%s'",
920                                         hostname, metric, type, id);
921                         return -1;
922                 }
923         }
925         ts_tojson(ts, buf);
926         sdb_timeseries_destroy(ts);
927         return 0;
928 } /* sdb_store_fetch_timeseries */
930 int
931 sdb_store_get_field(sdb_store_obj_t *obj, int field, sdb_data_t *res)
933         sdb_data_t tmp;
935         if (! obj)
936                 return -1;
938         switch (field) {
939                 case SDB_FIELD_NAME:
940                         tmp.type = SDB_TYPE_STRING;
941                         tmp.data.string = strdup(SDB_OBJ(obj)->name);
942                         if (! tmp.data.string)
943                                 return -1;
944                         break;
945                 case SDB_FIELD_LAST_UPDATE:
946                         tmp.type = SDB_TYPE_DATETIME;
947                         tmp.data.datetime = obj->last_update;
948                         break;
949                 case SDB_FIELD_AGE:
950                         tmp.type = SDB_TYPE_DATETIME;
951                         tmp.data.datetime = sdb_gettime() - obj->last_update;
952                         break;
953                 case SDB_FIELD_INTERVAL:
954                         tmp.type = SDB_TYPE_DATETIME;
955                         tmp.data.datetime = obj->interval;
956                         break;
957                 case SDB_FIELD_BACKEND:
958                 {
959                         tmp.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
960                         tmp.data.array.length = obj->backends_num;
961                         tmp.data.array.values = obj->backends;
962                         return sdb_data_copy(res, &tmp);
963                         break;
964                 }
965                 default:
966                         return -1;
967         }
968         if (res)
969                 *res = tmp;
970         else
971                 sdb_data_free_datum(&tmp);
972         return 0;
973 } /* sdb_store_get_field */
975 int
976 sdb_store_get_attr(sdb_store_obj_t *obj, const char *name, sdb_data_t *res,
977                 sdb_store_matcher_t *filter)
979         sdb_avltree_t *tree = NULL;
980         sdb_store_obj_t *attr;
982         if ((! obj) || (! name))
983                 return -1;
985         if (obj->type == SDB_HOST)
986                 tree = HOST(obj)->attributes;
987         else if (obj->type == SDB_SERVICE)
988                 tree = SVC(obj)->attributes;
989         else if (obj->type == SDB_METRIC)
990                 tree = METRIC(obj)->attributes;
992         if (! tree)
993                 return -1;
995         attr = STORE_OBJ(sdb_avltree_lookup(tree, name));
996         if (! attr)
997                 return -1;
998         if (filter && (! sdb_store_matcher_matches(filter, attr, NULL))) {
999                 sdb_object_deref(SDB_OBJ(attr));
1000                 return -1;
1001         }
1003         assert(STORE_OBJ(attr)->type == SDB_ATTRIBUTE);
1004         if (res)
1005                 sdb_data_copy(res, &ATTR(attr)->value);
1006         sdb_object_deref(SDB_OBJ(attr));
1007         return 0;
1008 } /* sdb_store_get_attr */
1010 int
1011 sdb_store_host_tojson(sdb_store_obj_t *h, sdb_strbuf_t *buf,
1012                 sdb_store_matcher_t *filter, int flags)
1014         sdb_host_t *host = HOST(h);
1016         if ((! h) || (h->type != SDB_HOST) || (! buf))
1017                 return -1;
1019         /* This function ignores SKIP_EMPTY flags given that the current
1020          * implementation sucks and it's nut currently used when calling this
1021          * function directly. */
1023         sdb_strbuf_append(buf, "{\"name\": \"%s\", ", SDB_OBJ(host)->name);
1024         store_common_tojson(h, buf);
1026         if (! (flags & SDB_SKIP_ATTRIBUTES)) {
1027                 sdb_strbuf_append(buf, ", \"attributes\": ");
1028                 store_obj_tojson(host->attributes, SDB_ATTRIBUTE, buf, filter, flags);
1029         }
1031         if (! (flags & SDB_SKIP_METRICS)) {
1032                 sdb_strbuf_append(buf, ", \"metrics\": ");
1033                 store_obj_tojson(host->metrics, SDB_METRIC, buf, filter, flags);
1034         }
1036         if (! (flags & SDB_SKIP_SERVICES)) {
1037                 sdb_strbuf_append(buf, ", \"services\": ");
1038                 store_obj_tojson(host->services, SDB_SERVICE, buf, filter, flags);
1039         }
1041         sdb_strbuf_append(buf, "}");
1042         return 0;
1043 } /* sdb_store_host_tojson */
1045 static _Bool
1046 has_children(sdb_avltree_t *tree, sdb_store_matcher_t *filter)
1048         sdb_avltree_iter_t *iter;
1050         if (! filter)
1051                 return sdb_avltree_size(tree) > 0;
1053         iter = sdb_avltree_get_iter(tree);
1054         while (sdb_avltree_iter_has_next(iter)) {
1055                 sdb_store_obj_t *sobj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1056                 if (sdb_store_matcher_matches(filter, sobj, NULL)) {
1057                         sdb_avltree_iter_destroy(iter);
1058                         return 1;
1059                 }
1060         }
1061         sdb_avltree_iter_destroy(iter);
1062         return 0;
1063 } /* has_children */
1065 int
1066 sdb_store_tojson(sdb_strbuf_t *buf, sdb_store_matcher_t *filter, int flags)
1068         sdb_avltree_iter_t *host_iter;
1069         size_t len;
1071         if (! buf)
1072                 return -1;
1074         pthread_rwlock_rdlock(&host_lock);
1076         host_iter = sdb_avltree_get_iter(hosts);
1077         if (! host_iter) {
1078                 pthread_rwlock_unlock(&host_lock);
1079                 return -1;
1080         }
1082         sdb_strbuf_append(buf, "[");
1084         len = sdb_strbuf_len(buf);
1085         while (sdb_avltree_iter_has_next(host_iter)) {
1086                 sdb_store_obj_t *host;
1088                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1089                 assert(host);
1091                 if (filter && (! sdb_store_matcher_matches(filter, host, NULL)))
1092                         continue;
1094                 /*
1095                  * XXX: This approach sucks but it's the best we can do at the moment.
1096                  * In the future, all store lookups should be split into multiple
1097                  * steps instead: first, retrieve all relevant objects and apply all
1098                  * pre-processing operations and then format it for the wire.
1099                  */
1100                 if ((flags & SDB_SKIP_EMPTY_SERVICES)
1101                                 && (! has_children(HOST(host)->services, filter)))
1102                         continue;
1103                 if ((flags & SDB_SKIP_EMPTY_METRICS)
1104                                 && (! has_children(HOST(host)->metrics, filter)))
1105                         continue;
1107                 if (sdb_strbuf_len(buf) > len)
1108                         sdb_strbuf_append(buf, ",");
1109                 len = sdb_strbuf_len(buf);
1111                 if (sdb_store_host_tojson(host, buf, filter, flags))
1112                         return -1;
1113         }
1115         sdb_strbuf_append(buf, "]");
1117         sdb_avltree_iter_destroy(host_iter);
1118         pthread_rwlock_unlock(&host_lock);
1119         return 0;
1120 } /* sdb_store_tojson */
1122 int
1123 sdb_store_scan(int type, sdb_store_matcher_t *m, sdb_store_matcher_t *filter,
1124                 sdb_store_lookup_cb cb, void *user_data)
1126         sdb_avltree_iter_t *host_iter;
1127         int status = 0;
1129         if (! cb)
1130                 return -1;
1132         if ((type != SDB_HOST) && (type != SDB_SERVICE) && (type != SDB_METRIC)) {
1133                 sdb_log(SDB_LOG_ERR, "store: Cannot scan objects of type %d", type);
1134                 return -1;
1135         }
1137         pthread_rwlock_rdlock(&host_lock);
1139         host_iter = sdb_avltree_get_iter(hosts);
1140         if (! host_iter)
1141                 status = -1;
1143         /* has_next returns false if the iterator is NULL */
1144         while (sdb_avltree_iter_has_next(host_iter)) {
1145                 sdb_store_obj_t *host;
1146                 sdb_avltree_iter_t *iter = NULL;
1148                 host = STORE_OBJ(sdb_avltree_iter_get_next(host_iter));
1149                 assert(host);
1151                 if (! sdb_store_matcher_matches(filter, host, NULL))
1152                         continue;
1154                 if (type == SDB_SERVICE)
1155                         iter = sdb_avltree_get_iter(HOST(host)->services);
1156                 else if (type == SDB_METRIC)
1157                         iter = sdb_avltree_get_iter(HOST(host)->metrics);
1159                 if (iter) {
1160                         while (sdb_avltree_iter_has_next(iter)) {
1161                                 sdb_store_obj_t *obj;
1162                                 obj = STORE_OBJ(sdb_avltree_iter_get_next(iter));
1163                                 assert(obj);
1165                                 if (sdb_store_matcher_matches(m, obj, filter)) {
1166                                         if (cb(obj, filter, user_data)) {
1167                                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1168                                                                 "an error while scanning");
1169                                                 status = -1;
1170                                                 break;
1171                                         }
1172                                 }
1173                         }
1174                 }
1175                 else if (sdb_store_matcher_matches(m, host, filter)) {
1176                         if (cb(host, filter, user_data)) {
1177                                 sdb_log(SDB_LOG_ERR, "store: Callback returned "
1178                                                 "an error while scanning");
1179                                 status = -1;
1180                         }
1181                 }
1183                 sdb_avltree_iter_destroy(iter);
1184                 if (status)
1185                         break;
1186         }
1188         sdb_avltree_iter_destroy(host_iter);
1189         pthread_rwlock_unlock(&host_lock);
1190         return status;
1191 } /* sdb_store_scan */
1193 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */