Code

Use the sdb_error* function throughout the project.
[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"
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */