1 /*
2 * SysDB - src/core/store.c
3 * Copyright (C) 2012 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 #include "sysdb.h"
29 #include "core/store.h"
30 #include "utils/error.h"
31 #include "utils/llist.h"
33 #include <assert.h>
35 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
41 #include <pthread.h>
43 /*
44 * private data types
45 */
47 /* type used for looking up named objects */
48 typedef struct {
49 sdb_object_t parent;
50 const char *obj_name;
51 } sdb_store_lookup_obj_t;
52 #define SDB_STORE_LOOKUP_OBJ_INIT { SDB_OBJECT_INIT, NULL }
54 /*
55 * private variables
56 */
58 static sdb_llist_t *host_list = NULL;
59 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
61 /*
62 * private helper functions
63 */
65 static int
66 sdb_store_obj_cmp_by_name(const sdb_object_t *a, const sdb_object_t *b)
67 {
68 const sdb_store_obj_t *h1 = (const sdb_store_obj_t *)a;
69 const sdb_store_obj_t *h2 = (const sdb_store_obj_t *)b;
71 assert(h1 && h2);
72 return strcasecmp(h1->name, h2->name);
73 } /* sdb_store_obj_cmp_by_name */
75 static int
76 sdb_cmp_store_obj_with_name(const sdb_object_t *a, const sdb_object_t *b)
77 {
78 const sdb_store_obj_t *obj = (const sdb_store_obj_t *)a;
79 const sdb_store_lookup_obj_t *lookup = (const sdb_store_lookup_obj_t *)b;
81 assert(obj && lookup);
82 return strcasecmp(obj->name, lookup->obj_name);
83 } /* sdb_cmp_store_obj_with_name */
85 /*
86 * private types
87 */
89 static int
90 sdb_host_init(sdb_object_t *obj, va_list ap)
91 {
92 const char *name = va_arg(ap, const char *);
94 SDB_HOST(obj)->_name = strdup(name);
95 if (! SDB_HOST(obj)->_name)
96 return -1;
98 SDB_HOST(obj)->_last_update = sdb_gettime();
99 /* ignore errors -> last_update will be updated later */
101 SDB_HOST(obj)->attributes = sdb_llist_create();
102 if (! SDB_HOST(obj)->attributes)
103 return -1;
104 SDB_HOST(obj)->services = sdb_llist_create();
105 if (! SDB_HOST(obj)->services)
106 return -1;
107 return 0;
108 } /* sdb_host_init */
110 static void
111 sdb_host_destroy(sdb_object_t *obj)
112 {
113 assert(obj);
115 if (SDB_HOST(obj)->_name)
116 free(SDB_HOST(obj)->_name);
118 if (SDB_HOST(obj)->attributes)
119 sdb_llist_destroy(SDB_HOST(obj)->attributes);
120 if (SDB_HOST(obj)->services)
121 sdb_llist_destroy(SDB_HOST(obj)->services);
122 } /* sdb_host_destroy */
124 static sdb_object_t *
125 sdb_host_do_clone(const sdb_object_t *obj)
126 {
127 const sdb_host_t *host = (const sdb_host_t *)obj;
128 sdb_host_t *new;
130 new = sdb_host_create(host->_name);
131 if (! new)
132 return NULL;
134 /* make sure these are initialized; else sdb_object_deref() might access
135 * arbitrary memory in case of an error */
136 new->services = new->attributes = NULL;
138 if (host->attributes) {
139 new->attributes = sdb_llist_clone(host->attributes);
140 if (! new->attributes) {
141 sdb_object_deref(SDB_OBJ(new));
142 return NULL;
143 }
144 }
146 new->_last_update = host->_last_update;
147 if (host->services) {
148 new->services = sdb_llist_clone(host->services);
149 if (! new->services) {
150 sdb_object_deref(SDB_OBJ(new));
151 return NULL;
152 }
153 }
154 return SDB_OBJ(new);
155 } /* sdb_host_do_clone */
157 static int
158 sdb_attr_init(sdb_object_t *obj, va_list ap)
159 {
160 const char *hostname = va_arg(ap, const char *);
161 const char *name = va_arg(ap, const char *);
162 const char *value = va_arg(ap, const char *);
164 SDB_ATTR(obj)->hostname = strdup(hostname);
165 SDB_ATTR(obj)->_name = strdup(name);
166 SDB_ATTR(obj)->attr_value = strdup(value);
167 if ((! SDB_ATTR(obj)->hostname)
168 || (! SDB_ATTR(obj)->_name) || (! SDB_ATTR(obj)->attr_value))
169 return -1;
171 SDB_ATTR(obj)->_last_update = sdb_gettime();
172 return 0;
173 } /* sdb_attr_init */
175 static void
176 sdb_attr_destroy(sdb_object_t *obj)
177 {
178 assert(obj);
180 if (SDB_ATTR(obj)->hostname)
181 free(SDB_ATTR(obj)->hostname);
182 if (SDB_ATTR(obj)->_name)
183 free(SDB_ATTR(obj)->_name);
184 if (SDB_ATTR(obj)->attr_value)
185 free(SDB_ATTR(obj)->attr_value);
186 } /* sdb_attr_destroy */
188 static sdb_object_t *
189 sdb_attr_clone(const sdb_object_t *obj)
190 {
191 const sdb_attribute_t *attr = (const sdb_attribute_t *)obj;
192 sdb_attribute_t *new;
194 new = sdb_attribute_create(attr->hostname,
195 attr->_name, attr->attr_value);
196 if (! new)
197 return NULL;
199 new->_last_update = attr->_last_update;
200 return SDB_OBJ(new);
201 } /* sdb_attr_clone */
203 static int
204 sdb_svc_init(sdb_object_t *obj, va_list ap)
205 {
206 const char *hostname = va_arg(ap, const char *);
207 const char *name = va_arg(ap, const char *);
209 SDB_SVC(obj)->hostname = strdup(hostname);
210 SDB_SVC(obj)->_name = strdup(name);
211 if ((! SDB_SVC(obj)->hostname) || (! SDB_SVC(obj)->_name))
212 return -1;
214 SDB_SVC(obj)->_last_update = sdb_gettime();
215 /* ignore errors -> last_update will be updated later */
216 return 0;
217 } /* sdb_svc_init */
219 static void
220 sdb_svc_destroy(sdb_object_t *obj)
221 {
222 assert(obj);
224 if (SDB_SVC(obj)->hostname)
225 free(SDB_SVC(obj)->hostname);
226 if (SDB_SVC(obj)->_name)
227 free(SDB_SVC(obj)->_name);
228 } /* sdb_svc_destroy */
230 static sdb_object_t *
231 sdb_svc_clone(const sdb_object_t *obj)
232 {
233 const sdb_service_t *svc = (const sdb_service_t *)obj;
234 sdb_service_t *new;
236 new = sdb_service_create(svc->hostname, svc->_name);
237 if (! new)
238 return NULL;
240 new->_last_update = svc->_last_update;
241 return SDB_OBJ(new);
242 } /* sdb_svc_clone */
244 static sdb_type_t sdb_host_type = {
245 sizeof(sdb_host_t),
247 sdb_host_init,
248 sdb_host_destroy,
249 sdb_host_do_clone
250 };
252 static sdb_type_t sdb_attr_type = {
253 sizeof(sdb_attribute_t),
255 sdb_attr_init,
256 sdb_attr_destroy,
257 sdb_attr_clone
258 };
260 static sdb_type_t sdb_svc_type = {
261 sizeof(sdb_service_t),
263 sdb_svc_init,
264 sdb_svc_destroy,
265 sdb_svc_clone
266 };
268 /*
269 * public API
270 */
272 sdb_host_t *
273 sdb_host_create(const char *name)
274 {
275 sdb_object_t *obj;
277 if (! name)
278 return NULL;
280 obj = sdb_object_create(sdb_host_type, name);
281 if (! obj)
282 return NULL;
283 return SDB_HOST(obj);
284 } /* sdb_host_create */
286 sdb_host_t *
287 sdb_host_clone(const sdb_host_t *host)
288 {
289 return SDB_HOST(sdb_host_do_clone((const sdb_object_t *)host));
290 } /* sdb_host_clone */
292 int
293 sdb_store_host(const sdb_host_t *host)
294 {
295 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
296 sdb_time_t last_update;
298 sdb_host_t *old;
299 int status = 0;
301 if ((! host) || (! host->_name))
302 return -1;
304 last_update = host->_last_update;
305 if (last_update <= 0)
306 last_update = sdb_gettime();
308 pthread_rwlock_wrlock(&host_lock);
310 if (! host_list) {
311 if (! (host_list = sdb_llist_create())) {
312 pthread_rwlock_unlock(&host_lock);
313 return -1;
314 }
315 }
317 lookup.obj_name = host->_name;
318 old = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
319 sdb_cmp_store_obj_with_name));
321 if (old) {
322 if (old->_last_update > last_update) {
323 sdb_log(SDB_LOG_DEBUG, "store: Cannot update host '%s' - "
324 "value too old (%"PRIscTIME" < %"PRIscTIME")",
325 host->_name, last_update, old->_last_update);
326 /* don't report an error; the host may be updated by multiple
327 * backends */
328 status = 1;
329 }
330 else {
331 old->_last_update = last_update;
332 }
333 }
334 else {
335 sdb_host_t *new = sdb_host_clone(host);
336 if (! new) {
337 char errbuf[1024];
338 sdb_log(SDB_LOG_ERR, "store: Failed to clone host object: %s",
339 sdb_strerror(errno, errbuf, sizeof(errbuf)));
340 pthread_rwlock_unlock(&host_lock);
341 return -1;
342 }
344 if (! new->attributes) {
345 if (! (new->attributes = sdb_llist_create())) {
346 char errbuf[1024];
347 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
348 "host object '%s': %s", host->_name,
349 sdb_strerror(errno, errbuf, sizeof(errbuf)));
350 sdb_object_deref(SDB_OBJ(new));
351 pthread_rwlock_unlock(&host_lock);
352 return -1;
353 }
354 }
356 if (! new->services) {
357 if (! (new->services = sdb_llist_create())) {
358 char errbuf[1024];
359 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
360 "host object '%s': %s", host->_name,
361 sdb_strerror(errno, errbuf, sizeof(errbuf)));
362 sdb_object_deref(SDB_OBJ(new));
363 pthread_rwlock_unlock(&host_lock);
364 return -1;
365 }
366 }
368 status = sdb_llist_insert_sorted(host_list, SDB_OBJ(new),
369 sdb_store_obj_cmp_by_name);
371 /* pass control to the list or destroy in case of an error */
372 sdb_object_deref(SDB_OBJ(new));
373 }
375 pthread_rwlock_unlock(&host_lock);
376 return status;
377 } /* sdb_store_host */
379 const sdb_host_t *
380 sdb_store_get_host(const char *name)
381 {
382 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
383 sdb_host_t *host;
385 if (! name)
386 return NULL;
388 lookup.obj_name = name;
389 host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
390 sdb_cmp_store_obj_with_name));
392 if (! host)
393 return NULL;
394 return host;
395 } /* sdb_store_get_host */
397 sdb_attribute_t *
398 sdb_attribute_create(const char *hostname,
399 const char *name, const char *value)
400 {
401 sdb_object_t *obj;
403 if ((! hostname) || (! name) || (! value))
404 return NULL;
406 obj = sdb_object_create(sdb_attr_type, hostname, name, value);
407 if (! obj)
408 return NULL;
409 return SDB_ATTR(obj);
410 } /* sdb_attribute_create */
412 sdb_attribute_t *
413 sdb_attribute_clone(const sdb_attribute_t *attr)
414 {
415 return SDB_ATTR(sdb_attr_clone((const sdb_object_t *)attr));
416 } /* sdb_attribute_clone */
418 int
419 sdb_store_attribute(const sdb_attribute_t *attr)
420 {
421 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
422 sdb_host_t *host;
424 sdb_attribute_t *old;
426 sdb_time_t last_update;
428 int status = 0;
430 if (! attr)
431 return -1;
433 last_update = attr->_last_update;
434 if (last_update <= 0)
435 last_update = sdb_gettime();
437 if (! host_list)
438 return -1;
440 pthread_rwlock_wrlock(&host_lock);
442 lookup.obj_name = attr->hostname;
443 host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
444 sdb_cmp_store_obj_with_name));
446 if (! host) {
447 pthread_rwlock_unlock(&host_lock);
448 return -1;
449 }
451 lookup.obj_name = attr->_name;
452 old = SDB_ATTR(sdb_llist_search(host->attributes,
453 (const sdb_object_t *)&lookup,
454 sdb_cmp_store_obj_with_name));
456 if (old) {
457 if (old->_last_update > last_update) {
458 sdb_log(SDB_LOG_DEBUG, "store: Cannot update attribute "
459 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
460 attr->hostname, attr->_name, last_update,
461 old->_last_update);
462 status = 1;
463 }
464 else {
465 old->_last_update = last_update;
466 }
467 }
468 else {
469 sdb_attribute_t *new = sdb_attribute_clone(attr);
470 if (! new) {
471 char errbuf[1024];
472 sdb_log(SDB_LOG_ERR, "store: Failed to clone attribute "
473 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
474 pthread_rwlock_unlock(&host_lock);
475 return -1;
476 }
478 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
479 sdb_store_obj_cmp_by_name);
481 /* pass control to the list or destroy in case of an error */
482 sdb_object_deref(SDB_OBJ(new));
483 }
485 pthread_rwlock_unlock(&host_lock);
486 return status;
487 } /* sdb_store_attribute */
489 sdb_service_t *
490 sdb_service_create(const char *hostname, const char *name)
491 {
492 sdb_object_t *obj;
494 if ((! hostname) || (! name))
495 return NULL;
497 obj = sdb_object_create(sdb_svc_type, hostname, name);
498 if (! obj)
499 return NULL;
500 return SDB_SVC(obj);
501 } /* sdb_service_create */
503 sdb_service_t *
504 sdb_service_clone(const sdb_service_t *svc)
505 {
506 return SDB_SVC(sdb_svc_clone((const sdb_object_t *)svc));
507 } /* sdb_service_clone */
509 int
510 sdb_store_service(const sdb_service_t *svc)
511 {
512 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
513 sdb_host_t *host;
515 sdb_service_t *old;
517 sdb_time_t last_update;
519 int status = 0;
521 if (! svc)
522 return -1;
524 last_update = svc->_last_update;
525 if (last_update <= 0)
526 last_update = sdb_gettime();
528 if (! host_list)
529 return -1;
531 pthread_rwlock_wrlock(&host_lock);
533 lookup.obj_name = svc->hostname;
534 host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
535 sdb_cmp_store_obj_with_name));
537 if (! host) {
538 pthread_rwlock_unlock(&host_lock);
539 return -1;
540 }
542 lookup.obj_name = svc->_name;
543 old = SDB_SVC(sdb_llist_search(host->services, (const sdb_object_t *)&lookup,
544 sdb_cmp_store_obj_with_name));
546 if (old) {
547 if (old->_last_update > last_update) {
548 sdb_log(SDB_LOG_DEBUG, "store: Cannot update service "
549 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
550 svc->hostname, svc->_name, last_update,
551 old->_last_update);
552 status = 1;
553 }
554 else {
555 old->_last_update = last_update;
556 }
557 }
558 else {
559 sdb_service_t *new = sdb_service_clone(svc);
560 if (! new) {
561 char errbuf[1024];
562 sdb_log(SDB_LOG_ERR, "store: Failed to clone service "
563 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
564 pthread_rwlock_unlock(&host_lock);
565 return -1;
566 }
568 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
569 sdb_store_obj_cmp_by_name);
571 /* pass control to the list or destroy in case of an error */
572 sdb_object_deref(SDB_OBJ(new));
573 }
575 pthread_rwlock_unlock(&host_lock);
576 return status;
577 } /* sdb_store_service */
579 const sdb_service_t *
580 sdb_store_get_service(const sdb_host_t *host, const char *name)
581 {
582 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
583 sdb_service_t *svc;
585 if ((! host) || (! name))
586 return NULL;
588 lookup.obj_name = name;
589 svc = SDB_SVC(sdb_llist_search(host->services,
590 (const sdb_object_t *)&lookup,
591 sdb_cmp_store_obj_with_name));
593 if (! svc)
594 return NULL;
595 return svc;
596 } /* sdb_store_get_service */
598 int
599 sdb_store_dump(FILE *fh)
600 {
601 sdb_llist_iter_t *host_iter;
603 if (! fh)
604 return -1;
606 pthread_rwlock_rdlock(&host_lock);
608 host_iter = sdb_llist_get_iter(host_list);
609 if (! host_iter) {
610 pthread_rwlock_unlock(&host_lock);
611 return -1;
612 }
614 while (sdb_llist_iter_has_next(host_iter)) {
615 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
616 sdb_llist_iter_t *svc_iter;
617 sdb_llist_iter_t *attr_iter;
619 char time_str[64];
621 assert(host);
623 if (! sdb_strftime(time_str, sizeof(time_str),
624 "%F %T %z", host->_last_update))
625 snprintf(time_str, sizeof(time_str), "<error>");
626 time_str[sizeof(time_str) - 1] = '\0';
628 fprintf(fh, "Host '%s' (last updated: %s):\n",
629 host->_name, time_str);
631 attr_iter = sdb_llist_get_iter(host->attributes);
632 if (! attr_iter) {
633 char errbuf[1024];
634 fprintf(fh, "Failed to retrieve attributes: %s\n",
635 sdb_strerror(errno, errbuf, sizeof(errbuf)));
636 continue;
637 }
639 while (sdb_llist_iter_has_next(attr_iter)) {
640 sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
641 assert(attr);
643 if (! sdb_strftime(time_str, sizeof(time_str),
644 "%F %T %z", attr->_last_update))
645 snprintf(time_str, sizeof(time_str), "<error>");
646 time_str[sizeof(time_str) - 1] = '\0';
648 fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
649 attr->_name, attr->attr_value, time_str);
650 }
652 sdb_llist_iter_destroy(attr_iter);
654 svc_iter = sdb_llist_get_iter(host->services);
655 if (! svc_iter) {
656 char errbuf[1024];
657 fprintf(fh, "Failed to retrieve services: %s\n",
658 sdb_strerror(errno, errbuf, sizeof(errbuf)));
659 continue;
660 }
662 while (sdb_llist_iter_has_next(svc_iter)) {
663 sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
664 assert(svc);
666 if (! sdb_strftime(time_str, sizeof(time_str),
667 "%F %T %z", svc->_last_update))
668 snprintf(time_str, sizeof(time_str), "<error>");
669 time_str[sizeof(time_str) - 1] = '\0';
671 fprintf(fh, "\tService '%s' (last updated: %s)\n",
672 svc->_name, time_str);
673 }
675 sdb_llist_iter_destroy(svc_iter);
676 }
678 sdb_llist_iter_destroy(host_iter);
679 pthread_rwlock_unlock(&host_lock);
680 return 0;
681 } /* sdb_store_dump */
683 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */