52328f8cf4ad6aa89b8039e0e7b2372babaa2a40
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)
122 {
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)
138 {
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)
155 {
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)
167 {
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)
186 {
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)
203 {
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)
222 {
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)
259 {
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)
281 {
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)
312 {
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)
406 {
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)
428 {
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)
451 {
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)
488 {
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)
554 {
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)
606 {
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)
613 {
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)
641 {
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)
654 {
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)
671 {
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)
699 {
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)
729 {
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)
769 {
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)
831 {
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)
871 {
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)
932 {
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)
978 {
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)
1013 {
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)
1047 {
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)
1067 {
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)
1125 {
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 : */