50991fcea78c244b17eef8e4b0dbbd98acb6ea5a
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 "core/plugin.h"
32 #include "utils/llist.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 variables
46 */
48 static sdb_llist_t *host_list = NULL;
49 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
51 /*
52 * private types
53 */
55 static sdb_type_t sdb_host_type;
56 static sdb_type_t sdb_attribute_type;
57 static sdb_type_t sdb_service_type;
59 typedef struct {
60 sdb_object_t super;
61 sdb_time_t last_update;
62 } sdb_store_obj_t;
63 #define SDB_STORE_OBJ(obj) ((sdb_store_obj_t *)(obj))
64 #define SDB_CONST_STORE_OBJ(obj) ((const sdb_store_obj_t *)(obj))
66 typedef struct {
67 sdb_store_obj_t super;
69 char *hostname;
70 } sdb_service_t;
71 #define SDB_SVC(obj) ((sdb_service_t *)(obj))
72 #define SDB_CONST_SVC(obj) ((const sdb_service_t *)(obj))
74 typedef struct {
75 sdb_store_obj_t super;
77 char *attr_value;
78 char *hostname;
79 } sdb_attribute_t;
80 #define SDB_ATTR(obj) ((sdb_attribute_t *)(obj))
81 #define SDB_CONST_ATTR(obj) ((const sdb_attribute_t *)(obj))
83 typedef struct {
84 sdb_store_obj_t super;
86 sdb_llist_t *attributes;
87 sdb_llist_t *services;
88 } sdb_host_t;
89 #define SDB_HOST(obj) ((sdb_host_t *)(obj))
90 #define SDB_CONST_HOST(obj) ((const sdb_host_t *)(obj))
92 /* shortcuts for accessing the sdb_store_obj_t attributes of inheriting
93 * objects */
94 #define _last_update super.last_update
96 static int
97 sdb_host_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
98 {
99 SDB_HOST(obj)->_last_update = sdb_gettime();
100 /* ignore errors -> last_update will be updated later */
102 SDB_HOST(obj)->attributes = sdb_llist_create();
103 if (! SDB_HOST(obj)->attributes)
104 return -1;
105 SDB_HOST(obj)->services = sdb_llist_create();
106 if (! SDB_HOST(obj)->services)
107 return -1;
108 return 0;
109 } /* sdb_host_init */
111 static void
112 sdb_host_destroy(sdb_object_t *obj)
113 {
114 assert(obj);
116 if (SDB_HOST(obj)->attributes)
117 sdb_llist_destroy(SDB_HOST(obj)->attributes);
118 if (SDB_HOST(obj)->services)
119 sdb_llist_destroy(SDB_HOST(obj)->services);
120 } /* sdb_host_destroy */
122 static sdb_object_t *
123 sdb_host_clone(const sdb_object_t *obj)
124 {
125 const sdb_host_t *host = (const sdb_host_t *)obj;
126 sdb_host_t *new;
128 new = SDB_HOST(sdb_object_create(obj->name, sdb_host_type));
129 if (! new)
130 return NULL;
132 /* make sure these are initialized; else sdb_object_deref() might access
133 * arbitrary memory in case of an error */
134 new->services = new->attributes = NULL;
136 if (host->attributes) {
137 new->attributes = sdb_llist_clone(host->attributes);
138 if (! new->attributes) {
139 sdb_object_deref(SDB_OBJ(new));
140 return NULL;
141 }
142 }
144 new->_last_update = host->_last_update;
145 if (host->services) {
146 new->services = sdb_llist_clone(host->services);
147 if (! new->services) {
148 sdb_object_deref(SDB_OBJ(new));
149 return NULL;
150 }
151 }
152 return SDB_OBJ(new);
153 } /* sdb_host_clone */
155 static int
156 sdb_attr_init(sdb_object_t *obj, va_list ap)
157 {
158 const char *hostname = va_arg(ap, const char *);
159 const char *value = va_arg(ap, const char *);
161 SDB_ATTR(obj)->hostname = strdup(hostname);
162 SDB_ATTR(obj)->attr_value = strdup(value);
163 if ((! SDB_ATTR(obj)->hostname) || (! SDB_ATTR(obj)->attr_value))
164 return -1;
166 SDB_ATTR(obj)->_last_update = sdb_gettime();
167 return 0;
168 } /* sdb_attr_init */
170 static void
171 sdb_attr_destroy(sdb_object_t *obj)
172 {
173 assert(obj);
175 if (SDB_ATTR(obj)->hostname)
176 free(SDB_ATTR(obj)->hostname);
177 if (SDB_ATTR(obj)->attr_value)
178 free(SDB_ATTR(obj)->attr_value);
179 } /* sdb_attr_destroy */
181 static sdb_object_t *
182 sdb_attr_clone(const sdb_object_t *obj)
183 {
184 const sdb_attribute_t *attr = (const sdb_attribute_t *)obj;
185 sdb_attribute_t *new;
187 new = SDB_ATTR(sdb_object_create(obj->name, sdb_attribute_type,
188 attr->hostname, attr->attr_value));
189 if (! new)
190 return NULL;
192 new->_last_update = attr->_last_update;
193 return SDB_OBJ(new);
194 } /* sdb_attr_clone */
196 static int
197 sdb_svc_init(sdb_object_t *obj, va_list ap)
198 {
199 const char *hostname = va_arg(ap, const char *);
201 SDB_SVC(obj)->hostname = strdup(hostname);
202 if (! SDB_SVC(obj)->hostname)
203 return -1;
205 SDB_SVC(obj)->_last_update = sdb_gettime();
206 /* ignore errors -> last_update will be updated later */
207 return 0;
208 } /* sdb_svc_init */
210 static void
211 sdb_svc_destroy(sdb_object_t *obj)
212 {
213 assert(obj);
215 if (SDB_SVC(obj)->hostname)
216 free(SDB_SVC(obj)->hostname);
217 } /* sdb_svc_destroy */
219 static sdb_object_t *
220 sdb_svc_clone(const sdb_object_t *obj)
221 {
222 const sdb_service_t *svc = (const sdb_service_t *)obj;
223 sdb_service_t *new;
225 new = SDB_SVC(sdb_object_create(obj->name, sdb_service_type,
226 svc->hostname));
227 if (! new)
228 return NULL;
230 new->_last_update = svc->_last_update;
231 return SDB_OBJ(new);
232 } /* sdb_svc_clone */
234 static sdb_type_t sdb_host_type = {
235 sizeof(sdb_host_t),
237 sdb_host_init,
238 sdb_host_destroy,
239 sdb_host_clone
240 };
242 static sdb_type_t sdb_attribute_type = {
243 sizeof(sdb_attribute_t),
245 sdb_attr_init,
246 sdb_attr_destroy,
247 sdb_attr_clone
248 };
250 static sdb_type_t sdb_service_type = {
251 sizeof(sdb_service_t),
253 sdb_svc_init,
254 sdb_svc_destroy,
255 sdb_svc_clone
256 };
258 /*
259 * public API
260 */
262 int
263 sdb_store_host(const char *name, sdb_time_t last_update)
264 {
265 sdb_host_t *old;
267 char *cname;
268 int status = 0;
270 if (! name)
271 return -1;
273 cname = sdb_plugin_cname(strdup(name));
274 if (! cname) {
275 sdb_log(SDB_LOG_ERR, "store: strdup failed");
276 return -1;
277 }
279 if (last_update <= 0)
280 last_update = sdb_gettime();
282 pthread_rwlock_wrlock(&host_lock);
284 if (! host_list) {
285 if (! (host_list = sdb_llist_create())) {
286 pthread_rwlock_unlock(&host_lock);
287 return -1;
288 }
289 }
291 old = SDB_HOST(sdb_llist_search_by_name(host_list, cname));
293 if (old) {
294 if (old->_last_update > last_update) {
295 sdb_log(SDB_LOG_DEBUG, "store: Cannot update host '%s' - "
296 "value too old (%"PRIscTIME" < %"PRIscTIME")",
297 cname, last_update, old->_last_update);
298 /* don't report an error; the host may be updated by multiple
299 * backends */
300 status = 1;
301 }
302 else {
303 old->_last_update = last_update;
304 }
305 }
306 else {
307 sdb_host_t *new = SDB_HOST(sdb_object_create(name, sdb_host_type));
308 if (! new) {
309 char errbuf[1024];
310 sdb_log(SDB_LOG_ERR, "store: Failed to clone host object: %s",
311 sdb_strerror(errno, errbuf, sizeof(errbuf)));
312 pthread_rwlock_unlock(&host_lock);
313 return -1;
314 }
316 free(SDB_OBJ(new)->name);
317 SDB_OBJ(new)->name = cname;
319 if (! new->attributes) {
320 if (! (new->attributes = sdb_llist_create())) {
321 char errbuf[1024];
322 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
323 "host object '%s': %s", SDB_OBJ(new)->name,
324 sdb_strerror(errno, errbuf, sizeof(errbuf)));
325 sdb_object_deref(SDB_OBJ(new));
326 pthread_rwlock_unlock(&host_lock);
327 return -1;
328 }
329 }
331 if (! new->services) {
332 if (! (new->services = sdb_llist_create())) {
333 char errbuf[1024];
334 sdb_log(SDB_LOG_ERR, "store: Failed to initialize "
335 "host object '%s': %s", SDB_OBJ(new)->name,
336 sdb_strerror(errno, errbuf, sizeof(errbuf)));
337 sdb_object_deref(SDB_OBJ(new));
338 pthread_rwlock_unlock(&host_lock);
339 return -1;
340 }
341 }
343 status = sdb_llist_insert_sorted(host_list, SDB_OBJ(new),
344 sdb_object_cmp_by_name);
346 /* pass control to the list or destroy in case of an error */
347 sdb_object_deref(SDB_OBJ(new));
348 }
350 pthread_rwlock_unlock(&host_lock);
351 return status;
352 } /* sdb_store_host */
354 _Bool
355 sdb_store_has_host(const char *name)
356 {
357 sdb_host_t *host;
359 if (! name)
360 return NULL;
362 host = SDB_HOST(sdb_llist_search_by_name(host_list, name));
363 return host != NULL;
364 } /* sdb_store_has_host */
366 int
367 sdb_store_attribute(const char *hostname, const char *key, const char *value,
368 sdb_time_t last_update)
369 {
370 sdb_host_t *host;
371 sdb_attribute_t *old;
373 int status = 0;
375 if ((! hostname) || (! key))
376 return -1;
378 if (last_update <= 0)
379 last_update = sdb_gettime();
381 if (! host_list)
382 return -1;
384 pthread_rwlock_wrlock(&host_lock);
386 host = SDB_HOST(sdb_llist_search_by_name(host_list, hostname));
387 if (! host) {
388 pthread_rwlock_unlock(&host_lock);
389 return -1;
390 }
392 old = SDB_ATTR(sdb_llist_search_by_name(host->attributes, key));
393 if (old) {
394 if (old->_last_update > last_update) {
395 sdb_log(SDB_LOG_DEBUG, "store: Cannot update attribute "
396 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
397 hostname, key, last_update, old->_last_update);
398 status = 1;
399 }
400 else {
401 old->_last_update = last_update;
402 }
403 }
404 else {
405 sdb_attribute_t *new = SDB_ATTR(sdb_object_create(key,
406 sdb_attribute_type, hostname, value));
407 if (! new) {
408 char errbuf[1024];
409 sdb_log(SDB_LOG_ERR, "store: Failed to clone attribute "
410 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
411 pthread_rwlock_unlock(&host_lock);
412 return -1;
413 }
415 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
416 sdb_object_cmp_by_name);
418 /* pass control to the list or destroy in case of an error */
419 sdb_object_deref(SDB_OBJ(new));
420 }
422 pthread_rwlock_unlock(&host_lock);
423 return status;
424 } /* sdb_store_attribute */
426 int
427 sdb_store_service(const char *hostname, const char *name,
428 sdb_time_t last_update)
429 {
430 sdb_host_t *host;
431 sdb_service_t *old;
433 int status = 0;
435 if ((! hostname) || (! name))
436 return -1;
438 if (last_update <= 0)
439 last_update = sdb_gettime();
441 if (! host_list)
442 return -1;
444 pthread_rwlock_wrlock(&host_lock);
446 host = SDB_HOST(sdb_llist_search_by_name(host_list, hostname));
447 if (! host) {
448 pthread_rwlock_unlock(&host_lock);
449 return -1;
450 }
452 old = SDB_SVC(sdb_llist_search_by_name(host->services, name));
453 if (old) {
454 if (old->_last_update > last_update) {
455 sdb_log(SDB_LOG_DEBUG, "store: Cannot update service "
456 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
457 hostname, name, last_update, old->_last_update);
458 status = 1;
459 }
460 else {
461 old->_last_update = last_update;
462 }
463 }
464 else {
465 sdb_service_t *new = SDB_SVC(sdb_object_create(name, sdb_service_type,
466 hostname));
467 if (! new) {
468 char errbuf[1024];
469 sdb_log(SDB_LOG_ERR, "store: Failed to clone service "
470 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
471 pthread_rwlock_unlock(&host_lock);
472 return -1;
473 }
475 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
476 sdb_object_cmp_by_name);
478 /* pass control to the list or destroy in case of an error */
479 sdb_object_deref(SDB_OBJ(new));
480 }
482 pthread_rwlock_unlock(&host_lock);
483 return status;
484 } /* sdb_store_service */
486 int
487 sdb_store_dump(FILE *fh)
488 {
489 sdb_llist_iter_t *host_iter;
491 if (! fh)
492 return -1;
494 pthread_rwlock_rdlock(&host_lock);
496 host_iter = sdb_llist_get_iter(host_list);
497 if (! host_iter) {
498 pthread_rwlock_unlock(&host_lock);
499 return -1;
500 }
502 while (sdb_llist_iter_has_next(host_iter)) {
503 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
504 sdb_llist_iter_t *svc_iter;
505 sdb_llist_iter_t *attr_iter;
507 char time_str[64];
509 assert(host);
511 if (! sdb_strftime(time_str, sizeof(time_str),
512 "%F %T %z", host->_last_update))
513 snprintf(time_str, sizeof(time_str), "<error>");
514 time_str[sizeof(time_str) - 1] = '\0';
516 fprintf(fh, "Host '%s' (last updated: %s):\n",
517 SDB_OBJ(host)->name, time_str);
519 attr_iter = sdb_llist_get_iter(host->attributes);
520 if (! attr_iter) {
521 char errbuf[1024];
522 fprintf(fh, "Failed to retrieve attributes: %s\n",
523 sdb_strerror(errno, errbuf, sizeof(errbuf)));
524 continue;
525 }
527 while (sdb_llist_iter_has_next(attr_iter)) {
528 sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
529 assert(attr);
531 if (! sdb_strftime(time_str, sizeof(time_str),
532 "%F %T %z", attr->_last_update))
533 snprintf(time_str, sizeof(time_str), "<error>");
534 time_str[sizeof(time_str) - 1] = '\0';
536 fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
537 SDB_OBJ(attr)->name, attr->attr_value, time_str);
538 }
540 sdb_llist_iter_destroy(attr_iter);
542 svc_iter = sdb_llist_get_iter(host->services);
543 if (! svc_iter) {
544 char errbuf[1024];
545 fprintf(fh, "Failed to retrieve services: %s\n",
546 sdb_strerror(errno, errbuf, sizeof(errbuf)));
547 continue;
548 }
550 while (sdb_llist_iter_has_next(svc_iter)) {
551 sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
552 assert(svc);
554 if (! sdb_strftime(time_str, sizeof(time_str),
555 "%F %T %z", svc->_last_update))
556 snprintf(time_str, sizeof(time_str), "<error>");
557 time_str[sizeof(time_str) - 1] = '\0';
559 fprintf(fh, "\tService '%s' (last updated: %s)\n",
560 SDB_OBJ(svc)->name, time_str);
561 }
563 sdb_llist_iter_destroy(svc_iter);
564 }
566 sdb_llist_iter_destroy(host_iter);
567 pthread_rwlock_unlock(&host_lock);
568 return 0;
569 } /* sdb_store_dump */
571 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */