Code

object system: Introduced a 'clone' callback for 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 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 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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */