Code

Merged branch 'master' of git://git.tokkee.org/sysdb.git.
[sysdb.git] / src / core / store.c
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 "core/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  * public 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)
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)
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)
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)
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)
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)
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)
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)
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 const 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 const sdb_type_t sdb_attribute_type = {
253         sizeof(sdb_attribute_t),
255         sdb_attr_init,
256         sdb_attr_destroy,
257         sdb_attr_clone
258 };
260 const sdb_type_t sdb_service_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)
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 int
287 sdb_store_host(const sdb_host_t *host)
289         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
290         sdb_time_t last_update;
292         sdb_host_t *old;
293         int status = 0;
295         if ((! host) || (! host->_name))
296                 return -1;
298         last_update = host->_last_update;
299         if (last_update <= 0)
300                 last_update = sdb_gettime();
302         pthread_rwlock_wrlock(&host_lock);
304         if (! host_list) {
305                 if (! (host_list = sdb_llist_create())) {
306                         pthread_rwlock_unlock(&host_lock);
307                         return -1;
308                 }
309         }
311         lookup.obj_name = host->_name;
312         old = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
313                                 sdb_cmp_store_obj_with_name));
315         if (old) {
316                 if (old->_last_update > last_update) {
317                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update host '%s' - "
318                                         "value too old (%"PRIscTIME" < %"PRIscTIME")",
319                                         host->_name, last_update, old->_last_update);
320                         /* don't report an error; the host may be updated by multiple
321                          * backends */
322                         status = 1;
323                 }
324                 else {
325                         old->_last_update = last_update;
326                 }
327         }
328         else {
329                 sdb_host_t *new = SDB_HOST(sdb_object_clone(SDB_CONST_OBJ(host)));
330                 if (! new) {
331                         char errbuf[1024];
332                         sdb_log(SDB_LOG_ERR, "store: Failed to clone host object: %s",
333                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
334                         pthread_rwlock_unlock(&host_lock);
335                         return -1;
336                 }
338                 if (! new->attributes) {
339                         if (! (new->attributes = sdb_llist_create())) {
340                                 char errbuf[1024];
341                                 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
342                                                 "host object '%s': %s", host->_name,
343                                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
344                                 sdb_object_deref(SDB_OBJ(new));
345                                 pthread_rwlock_unlock(&host_lock);
346                                 return -1;
347                         }
348                 }
350                 if (! new->services) {
351                         if (! (new->services = sdb_llist_create())) {
352                                 char errbuf[1024];
353                                 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
354                                                 "host object '%s': %s", host->_name,
355                                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
356                                 sdb_object_deref(SDB_OBJ(new));
357                                 pthread_rwlock_unlock(&host_lock);
358                                 return -1;
359                         }
360                 }
362                 status = sdb_llist_insert_sorted(host_list, SDB_OBJ(new),
363                                 sdb_store_obj_cmp_by_name);
365                 /* pass control to the list or destroy in case of an error */
366                 sdb_object_deref(SDB_OBJ(new));
367         }
369         pthread_rwlock_unlock(&host_lock);
370         return status;
371 } /* sdb_store_host */
373 const sdb_host_t *
374 sdb_store_get_host(const char *name)
376         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
377         sdb_host_t *host;
379         if (! name)
380                 return NULL;
382         lookup.obj_name = name;
383         host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
384                                 sdb_cmp_store_obj_with_name));
386         if (! host)
387                 return NULL;
388         return host;
389 } /* sdb_store_get_host */
391 sdb_attribute_t *
392 sdb_attribute_create(const char *hostname,
393                 const char *name, const char *value)
395         sdb_object_t *obj;
397         if ((! hostname) || (! name) || (! value))
398                 return NULL;
400         obj = sdb_object_create(sdb_attribute_type, hostname, name, value);
401         if (! obj)
402                 return NULL;
403         return SDB_ATTR(obj);
404 } /* sdb_attribute_create */
406 int
407 sdb_store_attribute(const sdb_attribute_t *attr)
409         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
410         sdb_host_t *host;
412         sdb_attribute_t *old;
414         sdb_time_t last_update;
416         int status = 0;
418         if (! attr)
419                 return -1;
421         last_update = attr->_last_update;
422         if (last_update <= 0)
423                 last_update = sdb_gettime();
425         if (! host_list)
426                 return -1;
428         pthread_rwlock_wrlock(&host_lock);
430         lookup.obj_name = attr->hostname;
431         host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
432                                 sdb_cmp_store_obj_with_name));
434         if (! host) {
435                 pthread_rwlock_unlock(&host_lock);
436                 return -1;
437         }
439         lookup.obj_name = attr->_name;
440         old = SDB_ATTR(sdb_llist_search(host->attributes,
441                                 (const sdb_object_t *)&lookup,
442                                 sdb_cmp_store_obj_with_name));
444         if (old) {
445                 if (old->_last_update > last_update) {
446                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update attribute "
447                                         "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
448                                         attr->hostname, attr->_name, last_update,
449                                         old->_last_update);
450                         status = 1;
451                 }
452                 else {
453                         old->_last_update = last_update;
454                 }
455         }
456         else {
457                 sdb_attribute_t *new = SDB_ATTR(sdb_object_clone(SDB_CONST_OBJ(attr)));
458                 if (! new) {
459                         char errbuf[1024];
460                         sdb_log(SDB_LOG_ERR, "store: Failed to clone attribute "
461                                         "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
462                         pthread_rwlock_unlock(&host_lock);
463                         return -1;
464                 }
466                 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
467                                 sdb_store_obj_cmp_by_name);
469                 /* pass control to the list or destroy in case of an error */
470                 sdb_object_deref(SDB_OBJ(new));
471         }
473         pthread_rwlock_unlock(&host_lock);
474         return status;
475 } /* sdb_store_attribute */
477 sdb_service_t *
478 sdb_service_create(const char *hostname, const char *name)
480         sdb_object_t *obj;
482         if ((! hostname) || (! name))
483                 return NULL;
485         obj = sdb_object_create(sdb_service_type, hostname, name);
486         if (! obj)
487                 return NULL;
488         return SDB_SVC(obj);
489 } /* sdb_service_create */
491 int
492 sdb_store_service(const sdb_service_t *svc)
494         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
495         sdb_host_t *host;
497         sdb_service_t *old;
499         sdb_time_t last_update;
501         int status = 0;
503         if (! svc)
504                 return -1;
506         last_update = svc->_last_update;
507         if (last_update <= 0)
508                 last_update = sdb_gettime();
510         if (! host_list)
511                 return -1;
513         pthread_rwlock_wrlock(&host_lock);
515         lookup.obj_name = svc->hostname;
516         host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
517                                 sdb_cmp_store_obj_with_name));
519         if (! host) {
520                 pthread_rwlock_unlock(&host_lock);
521                 return -1;
522         }
524         lookup.obj_name = svc->_name;
525         old = SDB_SVC(sdb_llist_search(host->services, (const sdb_object_t *)&lookup,
526                                 sdb_cmp_store_obj_with_name));
528         if (old) {
529                 if (old->_last_update > last_update) {
530                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update service "
531                                         "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
532                                         svc->hostname, svc->_name, last_update,
533                                         old->_last_update);
534                         status = 1;
535                 }
536                 else {
537                         old->_last_update = last_update;
538                 }
539         }
540         else {
541                 sdb_service_t *new = SDB_SVC(sdb_object_clone(SDB_CONST_OBJ(svc)));
542                 if (! new) {
543                         char errbuf[1024];
544                         sdb_log(SDB_LOG_ERR, "store: Failed to clone service "
545                                         "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
546                         pthread_rwlock_unlock(&host_lock);
547                         return -1;
548                 }
550                 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
551                                 sdb_store_obj_cmp_by_name);
553                 /* pass control to the list or destroy in case of an error */
554                 sdb_object_deref(SDB_OBJ(new));
555         }
557         pthread_rwlock_unlock(&host_lock);
558         return status;
559 } /* sdb_store_service */
561 const sdb_service_t *
562 sdb_store_get_service(const sdb_host_t *host, const char *name)
564         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
565         sdb_service_t *svc;
567         if ((! host) || (! name))
568                 return NULL;
570         lookup.obj_name = name;
571         svc = SDB_SVC(sdb_llist_search(host->services,
572                                 (const sdb_object_t *)&lookup,
573                                 sdb_cmp_store_obj_with_name));
575         if (! svc)
576                 return NULL;
577         return svc;
578 } /* sdb_store_get_service */
580 int
581 sdb_store_dump(FILE *fh)
583         sdb_llist_iter_t *host_iter;
585         if (! fh)
586                 return -1;
588         pthread_rwlock_rdlock(&host_lock);
590         host_iter = sdb_llist_get_iter(host_list);
591         if (! host_iter) {
592                 pthread_rwlock_unlock(&host_lock);
593                 return -1;
594         }
596         while (sdb_llist_iter_has_next(host_iter)) {
597                 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
598                 sdb_llist_iter_t *svc_iter;
599                 sdb_llist_iter_t *attr_iter;
601                 char time_str[64];
603                 assert(host);
605                 if (! sdb_strftime(time_str, sizeof(time_str),
606                                         "%F %T %z", host->_last_update))
607                         snprintf(time_str, sizeof(time_str), "<error>");
608                 time_str[sizeof(time_str) - 1] = '\0';
610                 fprintf(fh, "Host '%s' (last updated: %s):\n",
611                                 host->_name, time_str);
613                 attr_iter = sdb_llist_get_iter(host->attributes);
614                 if (! attr_iter) {
615                         char errbuf[1024];
616                         fprintf(fh, "Failed to retrieve attributes: %s\n",
617                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
618                         continue;
619                 }
621                 while (sdb_llist_iter_has_next(attr_iter)) {
622                         sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
623                         assert(attr);
625                         if (! sdb_strftime(time_str, sizeof(time_str),
626                                                 "%F %T %z", attr->_last_update))
627                                 snprintf(time_str, sizeof(time_str), "<error>");
628                         time_str[sizeof(time_str) - 1] = '\0';
630                         fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
631                                         attr->_name, attr->attr_value, time_str);
632                 }
634                 sdb_llist_iter_destroy(attr_iter);
636                 svc_iter = sdb_llist_get_iter(host->services);
637                 if (! svc_iter) {
638                         char errbuf[1024];
639                         fprintf(fh, "Failed to retrieve services: %s\n",
640                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
641                         continue;
642                 }
644                 while (sdb_llist_iter_has_next(svc_iter)) {
645                         sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
646                         assert(svc);
648                         if (! sdb_strftime(time_str, sizeof(time_str),
649                                                 "%F %T %z", svc->_last_update))
650                                 snprintf(time_str, sizeof(time_str), "<error>");
651                         time_str[sizeof(time_str) - 1] = '\0';
653                         fprintf(fh, "\tService '%s' (last updated: %s)\n",
654                                         svc->_name, time_str);
655                 }
657                 sdb_llist_iter_destroy(svc_iter);
658         }
660         sdb_llist_iter_destroy(host_iter);
661         pthread_rwlock_unlock(&host_lock);
662         return 0;
663 } /* sdb_store_dump */
665 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */