Code

object system: Introduced a concept of types.
[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 "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)
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 int
125 sdb_attr_init(sdb_object_t *obj, va_list ap)
127         const char *hostname = va_arg(ap, const char *);
128         const char *name = va_arg(ap, const char *);
129         const char *value = va_arg(ap, const char *);
131         SDB_ATTR(obj)->hostname = strdup(hostname);
132         SDB_ATTR(obj)->_name = strdup(name);
133         SDB_ATTR(obj)->attr_value = strdup(value);
134         if ((! SDB_ATTR(obj)->hostname)
135                         || (! SDB_ATTR(obj)->_name) || (! SDB_ATTR(obj)->attr_value))
136                 return -1;
138         SDB_ATTR(obj)->_last_update = sdb_gettime();
139         return 0;
140 } /* sdb_attr_init */
142 static void
143 sdb_attr_destroy(sdb_object_t *obj)
145         assert(obj);
147         if (SDB_ATTR(obj)->hostname)
148                 free(SDB_ATTR(obj)->hostname);
149         if (SDB_ATTR(obj)->_name)
150                 free(SDB_ATTR(obj)->_name);
151         if (SDB_ATTR(obj)->attr_value)
152                 free(SDB_ATTR(obj)->attr_value);
153 } /* sdb_attr_destroy */
155 static int
156 sdb_svc_init(sdb_object_t *obj, va_list ap)
158         const char *hostname = va_arg(ap, const char *);
159         const char *name = va_arg(ap, const char *);
161         SDB_SVC(obj)->hostname = strdup(hostname);
162         SDB_SVC(obj)->_name = strdup(name);
163         if ((! SDB_SVC(obj)->hostname) || (! SDB_SVC(obj)->_name))
164                 return -1;
166         SDB_SVC(obj)->_last_update = sdb_gettime();
167         /* ignore errors -> last_update will be updated later */
168         return 0;
169 } /* sdb_svc_init */
171 static void
172 sdb_svc_destroy(sdb_object_t *obj)
174         assert(obj);
176         if (SDB_SVC(obj)->hostname)
177                 free(SDB_SVC(obj)->hostname);
178         if (SDB_SVC(obj)->_name)
179                 free(SDB_SVC(obj)->_name);
180 } /* sdb_svc_destroy */
182 static sdb_type_t sdb_host_type = {
183         sizeof(sdb_host_t),
185         sdb_host_init,
186         sdb_host_destroy
187 };
189 static sdb_type_t sdb_attr_type = {
190         sizeof(sdb_attribute_t),
192         sdb_attr_init,
193         sdb_attr_destroy
194 };
196 static sdb_type_t sdb_svc_type = {
197         sizeof(sdb_service_t),
199         sdb_svc_init,
200         sdb_svc_destroy
201 };
203 /*
204  * public API
205  */
207 sdb_host_t *
208 sdb_host_create(const char *name)
210         sdb_object_t *obj;
212         if (! name)
213                 return NULL;
215         obj = sdb_object_create(sdb_host_type, name);
216         if (! obj)
217                 return NULL;
218         return SDB_HOST(obj);
219 } /* sdb_host_create */
221 sdb_host_t *
222 sdb_host_clone(const sdb_host_t *host)
224         sdb_host_t *new;
226         new = sdb_host_create(host->_name);
227         if (! new)
228                 return NULL;
230         /* make sure these are initialized; else sdb_object_deref() might access
231          * arbitrary memory in case of an error */
232         new->services = new->attributes = NULL;
234         if (host->attributes) {
235                 new->attributes = sdb_llist_clone(host->attributes);
236                 if (! new->attributes) {
237                         sdb_object_deref(SDB_OBJ(new));
238                         return NULL;
239                 }
240         }
242         new->_last_update = host->_last_update;
243         if (host->services) {
244                 new->services = sdb_llist_clone(host->services);
245                 if (! new->services) {
246                         sdb_object_deref(SDB_OBJ(new));
247                         return NULL;
248                 }
249         }
250         return new;
251 } /* sdb_host_clone */
253 int
254 sdb_store_host(const sdb_host_t *host)
256         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
257         sdb_time_t last_update;
259         sdb_host_t *old;
260         int status = 0;
262         if ((! host) || (! host->_name))
263                 return -1;
265         last_update = host->_last_update;
266         if (last_update <= 0)
267                 last_update = sdb_gettime();
269         pthread_rwlock_wrlock(&host_lock);
271         if (! host_list) {
272                 if (! (host_list = sdb_llist_create())) {
273                         pthread_rwlock_unlock(&host_lock);
274                         return -1;
275                 }
276         }
278         lookup.obj_name = host->_name;
279         old = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
280                                 sdb_cmp_store_obj_with_name));
282         if (old) {
283                 if (old->_last_update > last_update) {
284                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update host '%s' - "
285                                         "value too old (%"PRIscTIME" < %"PRIscTIME")",
286                                         host->_name, last_update, old->_last_update);
287                         /* don't report an error; the host may be updated by multiple
288                          * backends */
289                         status = 1;
290                 }
291                 else {
292                         old->_last_update = last_update;
293                 }
294         }
295         else {
296                 sdb_host_t *new = sdb_host_clone(host);
297                 if (! new) {
298                         char errbuf[1024];
299                         sdb_log(SDB_LOG_ERR, "store: Failed to clone host object: %s",
300                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
301                         pthread_rwlock_unlock(&host_lock);
302                         return -1;
303                 }
305                 if (! new->attributes) {
306                         if (! (new->attributes = sdb_llist_create())) {
307                                 char errbuf[1024];
308                                 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
309                                                 "host object '%s': %s", host->_name,
310                                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
311                                 sdb_object_deref(SDB_OBJ(new));
312                                 pthread_rwlock_unlock(&host_lock);
313                                 return -1;
314                         }
315                 }
317                 if (! new->services) {
318                         if (! (new->services = sdb_llist_create())) {
319                                 char errbuf[1024];
320                                 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
321                                                 "host object '%s': %s", host->_name,
322                                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
323                                 sdb_object_deref(SDB_OBJ(new));
324                                 pthread_rwlock_unlock(&host_lock);
325                                 return -1;
326                         }
327                 }
329                 status = sdb_llist_insert_sorted(host_list, SDB_OBJ(new),
330                                 sdb_store_obj_cmp_by_name);
332                 /* pass control to the list or destroy in case of an error */
333                 sdb_object_deref(SDB_OBJ(new));
334         }
336         pthread_rwlock_unlock(&host_lock);
337         return status;
338 } /* sdb_store_host */
340 const sdb_host_t *
341 sdb_store_get_host(const char *name)
343         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
344         sdb_host_t *host;
346         if (! name)
347                 return NULL;
349         lookup.obj_name = name;
350         host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
351                                 sdb_cmp_store_obj_with_name));
353         if (! host)
354                 return NULL;
355         return host;
356 } /* sdb_store_get_host */
358 sdb_attribute_t *
359 sdb_attribute_create(const char *hostname,
360                 const char *name, const char *value)
362         sdb_object_t *obj;
364         if ((! hostname) || (! name) || (! value))
365                 return NULL;
367         obj = sdb_object_create(sdb_attr_type, hostname, name, value);
368         if (! obj)
369                 return NULL;
370         return SDB_ATTR(obj);
371 } /* sdb_attribute_create */
373 sdb_attribute_t *
374 sdb_attribute_clone(const sdb_attribute_t *attr)
376         sdb_attribute_t *new;
378         new = sdb_attribute_create(attr->hostname,
379                         attr->_name, attr->attr_value);
380         if (! new)
381                 return NULL;
383         new->_last_update = attr->_last_update;
384         return new;
385 } /* sdb_attribute_clone */
387 int
388 sdb_store_attribute(const sdb_attribute_t *attr)
390         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
391         sdb_host_t *host;
393         sdb_attribute_t *old;
395         sdb_time_t last_update;
397         int status = 0;
399         if (! attr)
400                 return -1;
402         last_update = attr->_last_update;
403         if (last_update <= 0)
404                 last_update = sdb_gettime();
406         if (! host_list)
407                 return -1;
409         pthread_rwlock_wrlock(&host_lock);
411         lookup.obj_name = attr->hostname;
412         host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
413                                 sdb_cmp_store_obj_with_name));
415         if (! host) {
416                 pthread_rwlock_unlock(&host_lock);
417                 return -1;
418         }
420         lookup.obj_name = attr->_name;
421         old = SDB_ATTR(sdb_llist_search(host->attributes,
422                                 (const sdb_object_t *)&lookup,
423                                 sdb_cmp_store_obj_with_name));
425         if (old) {
426                 if (old->_last_update > last_update) {
427                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update attribute "
428                                         "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
429                                         attr->hostname, attr->_name, last_update,
430                                         old->_last_update);
431                         status = 1;
432                 }
433                 else {
434                         old->_last_update = last_update;
435                 }
436         }
437         else {
438                 sdb_attribute_t *new = sdb_attribute_clone(attr);
439                 if (! new) {
440                         char errbuf[1024];
441                         sdb_log(SDB_LOG_ERR, "store: Failed to clone attribute "
442                                         "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
443                         pthread_rwlock_unlock(&host_lock);
444                         return -1;
445                 }
447                 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
448                                 sdb_store_obj_cmp_by_name);
450                 /* pass control to the list or destroy in case of an error */
451                 sdb_object_deref(SDB_OBJ(new));
452         }
454         pthread_rwlock_unlock(&host_lock);
455         return status;
456 } /* sdb_store_attribute */
458 sdb_service_t *
459 sdb_service_create(const char *hostname, const char *name)
461         sdb_object_t *obj;
463         if ((! hostname) || (! name))
464                 return NULL;
466         obj = sdb_object_create(sdb_svc_type, hostname, name);
467         if (! obj)
468                 return NULL;
469         return SDB_SVC(obj);
470 } /* sdb_service_create */
472 sdb_service_t *
473 sdb_service_clone(const sdb_service_t *svc)
475         sdb_service_t *new;
477         new = sdb_service_create(svc->hostname, svc->_name);
478         if (! new)
479                 return NULL;
481         new->_last_update = svc->_last_update;
482         return new;
483 } /* sdb_service_clone */
485 int
486 sdb_store_service(const sdb_service_t *svc)
488         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
489         sdb_host_t *host;
491         sdb_service_t *old;
493         sdb_time_t last_update;
495         int status = 0;
497         if (! svc)
498                 return -1;
500         last_update = svc->_last_update;
501         if (last_update <= 0)
502                 last_update = sdb_gettime();
504         if (! host_list)
505                 return -1;
507         pthread_rwlock_wrlock(&host_lock);
509         lookup.obj_name = svc->hostname;
510         host = SDB_HOST(sdb_llist_search(host_list, (const sdb_object_t *)&lookup,
511                                 sdb_cmp_store_obj_with_name));
513         if (! host) {
514                 pthread_rwlock_unlock(&host_lock);
515                 return -1;
516         }
518         lookup.obj_name = svc->_name;
519         old = SDB_SVC(sdb_llist_search(host->services, (const sdb_object_t *)&lookup,
520                                 sdb_cmp_store_obj_with_name));
522         if (old) {
523                 if (old->_last_update > last_update) {
524                         sdb_log(SDB_LOG_DEBUG, "store: Cannot update service "
525                                         "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
526                                         svc->hostname, svc->_name, last_update,
527                                         old->_last_update);
528                         status = 1;
529                 }
530                 else {
531                         old->_last_update = last_update;
532                 }
533         }
534         else {
535                 sdb_service_t *new = sdb_service_clone(svc);
536                 if (! new) {
537                         char errbuf[1024];
538                         sdb_log(SDB_LOG_ERR, "store: Failed to clone service "
539                                         "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
540                         pthread_rwlock_unlock(&host_lock);
541                         return -1;
542                 }
544                 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
545                                 sdb_store_obj_cmp_by_name);
547                 /* pass control to the list or destroy in case of an error */
548                 sdb_object_deref(SDB_OBJ(new));
549         }
551         pthread_rwlock_unlock(&host_lock);
552         return status;
553 } /* sdb_store_service */
555 const sdb_service_t *
556 sdb_store_get_service(const sdb_host_t *host, const char *name)
558         sdb_store_lookup_obj_t lookup = SDB_STORE_LOOKUP_OBJ_INIT;
559         sdb_service_t *svc;
561         if ((! host) || (! name))
562                 return NULL;
564         lookup.obj_name = name;
565         svc = SDB_SVC(sdb_llist_search(host->services,
566                                 (const sdb_object_t *)&lookup,
567                                 sdb_cmp_store_obj_with_name));
569         if (! svc)
570                 return NULL;
571         return svc;
572 } /* sdb_store_get_service */
574 int
575 sdb_store_dump(FILE *fh)
577         sdb_llist_iter_t *host_iter;
579         if (! fh)
580                 return -1;
582         pthread_rwlock_rdlock(&host_lock);
584         host_iter = sdb_llist_get_iter(host_list);
585         if (! host_iter) {
586                 pthread_rwlock_unlock(&host_lock);
587                 return -1;
588         }
590         while (sdb_llist_iter_has_next(host_iter)) {
591                 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
592                 sdb_llist_iter_t *svc_iter;
593                 sdb_llist_iter_t *attr_iter;
595                 char time_str[64];
597                 assert(host);
599                 if (! sdb_strftime(time_str, sizeof(time_str),
600                                         "%F %T %z", host->_last_update))
601                         snprintf(time_str, sizeof(time_str), "<error>");
602                 time_str[sizeof(time_str) - 1] = '\0';
604                 fprintf(fh, "Host '%s' (last updated: %s):\n",
605                                 host->_name, time_str);
607                 attr_iter = sdb_llist_get_iter(host->attributes);
608                 if (! attr_iter) {
609                         char errbuf[1024];
610                         fprintf(fh, "Failed to retrieve attributes: %s\n",
611                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
612                         continue;
613                 }
615                 while (sdb_llist_iter_has_next(attr_iter)) {
616                         sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
617                         assert(attr);
619                         if (! sdb_strftime(time_str, sizeof(time_str),
620                                                 "%F %T %z", attr->_last_update))
621                                 snprintf(time_str, sizeof(time_str), "<error>");
622                         time_str[sizeof(time_str) - 1] = '\0';
624                         fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
625                                         attr->_name, attr->attr_value, time_str);
626                 }
628                 sdb_llist_iter_destroy(attr_iter);
630                 svc_iter = sdb_llist_get_iter(host->services);
631                 if (! svc_iter) {
632                         char errbuf[1024];
633                         fprintf(fh, "Failed to retrieve services: %s\n",
634                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
635                         continue;
636                 }
638                 while (sdb_llist_iter_has_next(svc_iter)) {
639                         sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
640                         assert(svc);
642                         if (! sdb_strftime(time_str, sizeof(time_str),
643                                                 "%F %T %z", svc->_last_update))
644                                 snprintf(time_str, sizeof(time_str), "<error>");
645                         time_str[sizeof(time_str) - 1] = '\0';
647                         fprintf(fh, "\tService '%s' (last updated: %s)\n",
648                                         svc->_name, time_str);
649                 }
651                 sdb_llist_iter_destroy(svc_iter);
652         }
654         sdb_llist_iter_destroy(host_iter);
655         pthread_rwlock_unlock(&host_lock);
656         return 0;
657 } /* sdb_store_dump */
659 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */