a0bbf55ce39173c536c4758fce4efefcfeca81cf
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"
32 #include "utils/string.h"
34 #include <assert.h>
36 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #include <pthread.h>
44 /*
45 * private data types
46 */
48 /* type used for looking up named objects */
49 typedef struct {
50 sdb_object_t parent;
51 const char *obj_name;
52 } sdb_store_lookup_obj_t;
53 #define SDB_STORE_LOOKUP_OBJ_INIT { SDB_OBJECT_INIT, NULL }
55 /*
56 * private variables
57 */
59 static sdb_llist_t *host_list = NULL;
60 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
62 /*
63 * private helper functions
64 */
66 static int
67 sdb_store_obj_cmp_by_name(const sdb_object_t *a, const sdb_object_t *b)
68 {
69 const sdb_store_obj_t *h1 = (const sdb_store_obj_t *)a;
70 const sdb_store_obj_t *h2 = (const sdb_store_obj_t *)b;
72 assert(h1 && h2);
73 return strcasecmp(h1->name, h2->name);
74 } /* sdb_store_obj_cmp_by_name */
76 static int
77 sdb_cmp_store_obj_with_name(const sdb_object_t *a, const sdb_object_t *b)
78 {
79 const sdb_store_obj_t *obj = (const sdb_store_obj_t *)a;
80 const sdb_store_lookup_obj_t *lookup = (const sdb_store_lookup_obj_t *)b;
82 assert(obj && lookup);
83 return strcasecmp(obj->name, lookup->obj_name);
84 } /* sdb_cmp_store_obj_with_name */
86 static int
87 sdb_host_init(sdb_object_t *obj, va_list ap)
88 {
89 const char *name = va_arg(ap, const char *);
91 SDB_HOST(obj)->host_name = strdup(name);
92 if (! SDB_HOST(obj)->host_name)
93 return -1;
95 SDB_HOST(obj)->host_last_update = sdb_gettime();
96 /* ignore errors -> last_update will be updated later */
98 SDB_HOST(obj)->attributes = sdb_llist_create();
99 if (! SDB_HOST(obj)->attributes)
100 return -1;
101 SDB_HOST(obj)->services = sdb_llist_create();
102 if (! SDB_HOST(obj)->services)
103 return -1;
104 return 0;
105 } /* sdb_host_init */
107 static void
108 sdb_host_destroy(sdb_object_t *obj)
109 {
110 assert(obj);
112 if (SDB_HOST(obj)->host_name)
113 free(SDB_HOST(obj)->host_name);
115 if (SDB_HOST(obj)->attributes)
116 sdb_llist_destroy(SDB_HOST(obj)->attributes);
117 if (SDB_HOST(obj)->services)
118 sdb_llist_destroy(SDB_HOST(obj)->services);
119 } /* sdb_host_destroy */
121 static int
122 sdb_attr_init(sdb_object_t *obj, va_list ap)
123 {
124 const char *hostname = va_arg(ap, const char *);
125 const char *name = va_arg(ap, const char *);
126 const char *value = va_arg(ap, const char *);
128 SDB_ATTR(obj)->hostname = strdup(hostname);
129 SDB_ATTR(obj)->attr_name = strdup(name);
130 SDB_ATTR(obj)->attr_value = strdup(value);
131 if ((! SDB_ATTR(obj)->hostname)
132 || (! SDB_ATTR(obj)->attr_name) || (! SDB_ATTR(obj)->attr_value))
133 return -1;
135 SDB_ATTR(obj)->attr_last_update = sdb_gettime();
136 return 0;
137 } /* sdb_attr_init */
139 static void
140 sdb_attr_destroy(sdb_object_t *obj)
141 {
142 assert(obj);
144 if (SDB_ATTR(obj)->hostname)
145 free(SDB_ATTR(obj)->hostname);
146 if (SDB_ATTR(obj)->attr_name)
147 free(SDB_ATTR(obj)->attr_name);
148 if (SDB_ATTR(obj)->attr_value)
149 free(SDB_ATTR(obj)->attr_value);
150 } /* sdb_attr_destroy */
152 static int
153 sdb_svc_init(sdb_object_t *obj, va_list ap)
154 {
155 const char *hostname = va_arg(ap, const char *);
156 const char *name = va_arg(ap, const char *);
158 SDB_SVC(obj)->hostname = strdup(hostname);
159 SDB_SVC(obj)->svc_name = strdup(name);
160 if ((! SDB_SVC(obj)->hostname) || (! SDB_SVC(obj)->svc_name))
161 return -1;
163 SDB_SVC(obj)->svc_last_update = sdb_gettime();
164 /* ignore errors -> last_update will be updated later */
165 return 0;
166 } /* sdb_svc_init */
168 static void
169 sdb_svc_destroy(sdb_object_t *obj)
170 {
171 assert(obj);
173 if (SDB_SVC(obj)->hostname)
174 free(SDB_SVC(obj)->hostname);
175 if (SDB_SVC(obj)->svc_name)
176 free(SDB_SVC(obj)->svc_name);
177 } /* sdb_svc_destroy */
179 /*
180 * public API
181 */
183 sdb_host_t *
184 sdb_host_create(const char *name)
185 {
186 sdb_object_t *obj;
188 if (! name)
189 return NULL;
191 obj = sdb_object_create(sizeof(sdb_host_t), sdb_host_init,
192 sdb_host_destroy, name);
193 if (! obj)
194 return NULL;
195 return SDB_HOST(obj);
196 } /* sdb_host_create */
198 sdb_host_t *
199 sdb_host_clone(const sdb_host_t *host)
200 {
201 sdb_host_t *new;
203 new = sdb_host_create(host->host_name);
204 if (! new)
205 return NULL;
207 /* make sure these are initialized; else sdb_object_deref() might access
208 * arbitrary memory in case of an error */
209 new->services = new->attributes = NULL;
211 if (host->attributes) {
212 new->attributes = sdb_llist_clone(host->attributes);
213 if (! new->attributes) {
214 sdb_object_deref(SDB_OBJ(new));
215 return NULL;
216 }
217 }
219 new->host_last_update = host->host_last_update;
220 if (host->services) {
221 new->services = sdb_llist_clone(host->services);
222 if (! new->services) {
223 sdb_object_deref(SDB_OBJ(new));
224 return NULL;
225 }
226 }
227 return new;
228 } /* sdb_host_clone */
230 int
231 sdb_store_host(const sdb_host_t *host)
232 {
233 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
234 sdb_time_t last_update;
236 sdb_host_t *old;
237 int status = 0;
239 if ((! host) || (! host->host_name))
240 return -1;
242 last_update = host->host_last_update;
243 if (last_update <= 0)
244 last_update = sdb_gettime();
246 pthread_rwlock_wrlock(&host_lock);
248 if (! host_list) {
249 if (! (host_list = sdb_llist_create())) {
250 pthread_rwlock_unlock(&host_lock);
251 return -1;
252 }
253 }
255 lookup.obj_name = host->host_name;
256 old = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
257 sdb_cmp_store_obj_with_name));
259 if (old) {
260 if (old->host_last_update > last_update) {
261 sdb_error_set(SDB_LOG_DEBUG, "store: Cannot update host '%s' - "
262 "value too old (%"PRIscTIME" < %"PRIscTIME")\n",
263 host->host_name, last_update, old->host_last_update);
264 /* don't report an error; the host may be updated by multiple
265 * backends */
266 status = 1;
267 }
268 else {
269 old->host_last_update = last_update;
270 }
271 }
272 else {
273 sdb_host_t *new = sdb_host_clone(host);
274 if (! new) {
275 char errbuf[1024];
276 sdb_error_set(SDB_LOG_ERR, "store: Failed to clone host object: "
277 "%s\n", sdb_strerror(errno, errbuf, sizeof(errbuf)));
278 pthread_rwlock_unlock(&host_lock);
279 return -1;
280 }
282 if (! new->attributes) {
283 if (! (new->attributes = sdb_llist_create())) {
284 char errbuf[1024];
285 sdb_error_set(SDB_LOG_ERR, "store: Failed to initialize "
286 "host object '%s': %s\n", host->host_name,
287 sdb_strerror(errno, errbuf, sizeof(errbuf)));
288 sdb_object_deref(SDB_OBJ(new));
289 pthread_rwlock_unlock(&host_lock);
290 return -1;
291 }
292 }
294 if (! new->services) {
295 if (! (new->services = sdb_llist_create())) {
296 char errbuf[1024];
297 sdb_error_set(SDB_LOG_ERR, "store: Failed to initialize "
298 "host object '%s': %s\n", host->host_name,
299 sdb_strerror(errno, errbuf, sizeof(errbuf)));
300 sdb_object_deref(SDB_OBJ(new));
301 pthread_rwlock_unlock(&host_lock);
302 return -1;
303 }
304 }
306 status = sdb_llist_insert_sorted(host_list, SDB_OBJ(new),
307 sdb_store_obj_cmp_by_name);
309 /* pass control to the list or destroy in case of an error */
310 sdb_object_deref(SDB_OBJ(new));
311 }
313 pthread_rwlock_unlock(&host_lock);
314 return status;
315 } /* sdb_store_host */
317 const sdb_host_t *
318 sdb_store_get_host(const char *name)
319 {
320 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
321 sdb_host_t *host;
323 if (! name)
324 return NULL;
326 lookup.obj_name = name;
327 host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
328 sdb_cmp_store_obj_with_name));
330 if (! host)
331 return NULL;
332 return host;
333 } /* sdb_store_get_host */
335 sdb_attribute_t *
336 sdb_attribute_create(const char *hostname,
337 const char *name, const char *value)
338 {
339 sdb_object_t *obj;
341 if ((! hostname) || (! name) || (! value))
342 return NULL;
344 obj = sdb_object_create(sizeof(sdb_attribute_t), sdb_attr_init,
345 sdb_attr_destroy, hostname, name, value);
346 if (! obj)
347 return NULL;
348 return SDB_ATTR(obj);
349 } /* sdb_attribute_create */
351 sdb_attribute_t *
352 sdb_attribute_clone(const sdb_attribute_t *attr)
353 {
354 sdb_attribute_t *new;
356 new = sdb_attribute_create(attr->hostname,
357 attr->attr_name, attr->attr_value);
358 if (! new)
359 return NULL;
361 new->attr_last_update = attr->attr_last_update;
362 return new;
363 } /* sdb_attribute_clone */
365 int
366 sdb_store_attribute(const sdb_attribute_t *attr)
367 {
368 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
369 sdb_host_t *host;
371 sdb_attribute_t *old;
373 sdb_time_t last_update;
375 int status = 0;
377 if (! attr)
378 return -1;
380 last_update = attr->attr_last_update;
381 if (last_update <= 0)
382 last_update = sdb_gettime();
384 if (! host_list)
385 return -1;
387 pthread_rwlock_wrlock(&host_lock);
389 lookup.obj_name = attr->hostname;
390 host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
391 sdb_cmp_store_obj_with_name));
393 if (! host) {
394 pthread_rwlock_unlock(&host_lock);
395 return -1;
396 }
398 lookup.obj_name = attr->attr_name;
399 old = SDB_ATTR(sdb_llist_search(host->attributes,
400 (const sdb_object_t *)&lookup,
401 sdb_cmp_store_obj_with_name));
403 if (old) {
404 if (old->host_last_update > last_update) {
405 sdb_error_set(SDB_LOG_DEBUG, "store: Cannot update attribute "
406 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")\n",
407 attr->hostname, attr->attr_name, last_update,
408 old->host_last_update);
409 status = 1;
410 }
411 else {
412 old->attr_last_update = last_update;
413 }
414 }
415 else {
416 sdb_attribute_t *new = sdb_attribute_clone(attr);
417 if (! new) {
418 char errbuf[1024];
419 sdb_error_set(SDB_LOG_ERR, "store: Failed to clone attribute "
420 "object: %s\n", sdb_strerror(errno, errbuf, sizeof(errbuf)));
421 pthread_rwlock_unlock(&host_lock);
422 return -1;
423 }
425 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
426 sdb_store_obj_cmp_by_name);
428 /* pass control to the list or destroy in case of an error */
429 sdb_object_deref(SDB_OBJ(new));
430 }
432 pthread_rwlock_unlock(&host_lock);
433 return status;
434 } /* sdb_store_attribute */
436 sdb_service_t *
437 sdb_service_create(const char *hostname, const char *name)
438 {
439 sdb_object_t *obj;
441 if ((! hostname) || (! name))
442 return NULL;
444 obj = sdb_object_create(sizeof(sdb_service_t), sdb_svc_init,
445 sdb_svc_destroy, hostname, name);
446 if (! obj)
447 return NULL;
448 return SDB_SVC(obj);
449 } /* sdb_service_create */
451 sdb_service_t *
452 sdb_service_clone(const sdb_service_t *svc)
453 {
454 sdb_service_t *new;
456 new = sdb_service_create(svc->hostname, svc->svc_name);
457 if (! new)
458 return NULL;
460 new->svc_last_update = svc->svc_last_update;
461 return new;
462 } /* sdb_service_clone */
464 int
465 sdb_store_service(const sdb_service_t *svc)
466 {
467 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
468 sdb_host_t *host;
470 sdb_service_t *old;
472 sdb_time_t last_update;
474 int status = 0;
476 if (! svc)
477 return -1;
479 last_update = svc->svc_last_update;
480 if (last_update <= 0)
481 last_update = sdb_gettime();
483 if (! host_list)
484 return -1;
486 pthread_rwlock_wrlock(&host_lock);
488 lookup.obj_name = svc->hostname;
489 host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
490 sdb_cmp_store_obj_with_name));
492 if (! host) {
493 pthread_rwlock_unlock(&host_lock);
494 return -1;
495 }
497 lookup.obj_name = svc->svc_name;
498 old = SDB_SVC(sdb_llist_search(host->services, (const sdb_object_t *)&lookup,
499 sdb_cmp_store_obj_with_name));
501 if (old) {
502 if (old->host_last_update > last_update) {
503 sdb_error_set(SDB_LOG_DEBUG, "store: Cannot update service "
504 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")\n",
505 svc->hostname, svc->svc_name, last_update,
506 old->host_last_update);
507 status = 1;
508 }
509 else {
510 old->svc_last_update = last_update;
511 }
512 }
513 else {
514 sdb_service_t *new = sdb_service_clone(svc);
515 if (! new) {
516 char errbuf[1024];
517 sdb_error_set(SDB_LOG_ERR, "store: Failed to clone service "
518 "object: %s\n", sdb_strerror(errno, errbuf, sizeof(errbuf)));
519 pthread_rwlock_unlock(&host_lock);
520 return -1;
521 }
523 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
524 sdb_store_obj_cmp_by_name);
526 /* pass control to the list or destroy in case of an error */
527 sdb_object_deref(SDB_OBJ(new));
528 }
530 pthread_rwlock_unlock(&host_lock);
531 return status;
532 } /* sdb_store_service */
534 const sdb_service_t *
535 sdb_store_get_service(const sdb_host_t *host, const char *name)
536 {
537 sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
538 sdb_service_t *svc;
540 if ((! host) || (! name))
541 return NULL;
543 lookup.obj_name = name;
544 svc = SDB_SVC(sdb_llist_search(host->services,
545 (const sdb_object_t *)&lookup,
546 sdb_cmp_store_obj_with_name));
548 if (! svc)
549 return NULL;
550 return svc;
551 } /* sdb_store_get_service */
553 int
554 sdb_store_dump(FILE *fh)
555 {
556 sdb_llist_iter_t *host_iter;
558 if (! fh)
559 return -1;
561 pthread_rwlock_rdlock(&host_lock);
563 host_iter = sdb_llist_get_iter(host_list);
564 if (! host_iter) {
565 pthread_rwlock_unlock(&host_lock);
566 return -1;
567 }
569 while (sdb_llist_iter_has_next(host_iter)) {
570 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
571 sdb_llist_iter_t *svc_iter;
572 sdb_llist_iter_t *attr_iter;
574 char time_str[64];
576 assert(host);
578 if (! sdb_strftime(time_str, sizeof(time_str),
579 "%F %T %z", host->host_last_update))
580 snprintf(time_str, sizeof(time_str), "<error>");
581 time_str[sizeof(time_str) - 1] = '\0';
583 fprintf(fh, "Host '%s' (last updated: %s):\n",
584 host->host_name, time_str);
586 attr_iter = sdb_llist_get_iter(host->attributes);
587 if (! attr_iter) {
588 char errbuf[1024];
589 fprintf(fh, "Failed to retrieve attributes: %s\n",
590 sdb_strerror(errno, errbuf, sizeof(errbuf)));
591 continue;
592 }
594 while (sdb_llist_iter_has_next(attr_iter)) {
595 sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
596 assert(attr);
598 if (! sdb_strftime(time_str, sizeof(time_str),
599 "%F %T %z", attr->attr_last_update))
600 snprintf(time_str, sizeof(time_str), "<error>");
601 time_str[sizeof(time_str) - 1] = '\0';
603 fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
604 attr->attr_name, attr->attr_value, time_str);
605 }
607 sdb_llist_iter_destroy(attr_iter);
609 svc_iter = sdb_llist_get_iter(host->services);
610 if (! svc_iter) {
611 char errbuf[1024];
612 fprintf(fh, "Failed to retrieve services: %s\n",
613 sdb_strerror(errno, errbuf, sizeof(errbuf)));
614 continue;
615 }
617 while (sdb_llist_iter_has_next(svc_iter)) {
618 sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
619 assert(svc);
621 if (! sdb_strftime(time_str, sizeof(time_str),
622 "%F %T %z", svc->svc_last_update))
623 snprintf(time_str, sizeof(time_str), "<error>");
624 time_str[sizeof(time_str) - 1] = '\0';
626 fprintf(fh, "\tService '%s' (last updated: %s)\n",
627 svc->svc_name, time_str);
628 }
630 sdb_llist_iter_destroy(svc_iter);
631 }
633 sdb_llist_iter_destroy(host_iter);
634 pthread_rwlock_unlock(&host_lock);
635 return 0;
636 } /* sdb_store_dump */
638 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */