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 variables
45 */
47 static sdb_llist_t *host_list = NULL;
48 static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
50 /*
51 * public types
52 */
54 static int
55 sdb_host_init(sdb_object_t *obj, va_list __attribute__((unused)) ap)
56 {
57 SDB_HOST(obj)->_last_update = sdb_gettime();
58 /* ignore errors -> last_update will be updated later */
60 SDB_HOST(obj)->attributes = sdb_llist_create();
61 if (! SDB_HOST(obj)->attributes)
62 return -1;
63 SDB_HOST(obj)->services = sdb_llist_create();
64 if (! SDB_HOST(obj)->services)
65 return -1;
66 return 0;
67 } /* sdb_host_init */
69 static void
70 sdb_host_destroy(sdb_object_t *obj)
71 {
72 assert(obj);
74 if (SDB_HOST(obj)->attributes)
75 sdb_llist_destroy(SDB_HOST(obj)->attributes);
76 if (SDB_HOST(obj)->services)
77 sdb_llist_destroy(SDB_HOST(obj)->services);
78 } /* sdb_host_destroy */
80 static sdb_object_t *
81 sdb_host_do_clone(const sdb_object_t *obj)
82 {
83 const sdb_host_t *host = (const sdb_host_t *)obj;
84 sdb_host_t *new;
86 new = sdb_host_create(obj->name);
87 if (! new)
88 return NULL;
90 /* make sure these are initialized; else sdb_object_deref() might access
91 * arbitrary memory in case of an error */
92 new->services = new->attributes = NULL;
94 if (host->attributes) {
95 new->attributes = sdb_llist_clone(host->attributes);
96 if (! new->attributes) {
97 sdb_object_deref(SDB_OBJ(new));
98 return NULL;
99 }
100 }
102 new->_last_update = host->_last_update;
103 if (host->services) {
104 new->services = sdb_llist_clone(host->services);
105 if (! new->services) {
106 sdb_object_deref(SDB_OBJ(new));
107 return NULL;
108 }
109 }
110 return SDB_OBJ(new);
111 } /* sdb_host_do_clone */
113 static int
114 sdb_attr_init(sdb_object_t *obj, va_list ap)
115 {
116 const char *hostname = va_arg(ap, const char *);
117 const char *value = va_arg(ap, const char *);
119 SDB_ATTR(obj)->hostname = strdup(hostname);
120 SDB_ATTR(obj)->attr_value = strdup(value);
121 if ((! SDB_ATTR(obj)->hostname) || (! SDB_ATTR(obj)->attr_value))
122 return -1;
124 SDB_ATTR(obj)->_last_update = sdb_gettime();
125 return 0;
126 } /* sdb_attr_init */
128 static void
129 sdb_attr_destroy(sdb_object_t *obj)
130 {
131 assert(obj);
133 if (SDB_ATTR(obj)->hostname)
134 free(SDB_ATTR(obj)->hostname);
135 if (SDB_ATTR(obj)->attr_value)
136 free(SDB_ATTR(obj)->attr_value);
137 } /* sdb_attr_destroy */
139 static sdb_object_t *
140 sdb_attr_clone(const sdb_object_t *obj)
141 {
142 const sdb_attribute_t *attr = (const sdb_attribute_t *)obj;
143 sdb_attribute_t *new;
145 new = sdb_attribute_create(attr->hostname,
146 obj->name, attr->attr_value);
147 if (! new)
148 return NULL;
150 new->_last_update = attr->_last_update;
151 return SDB_OBJ(new);
152 } /* sdb_attr_clone */
154 static int
155 sdb_svc_init(sdb_object_t *obj, va_list ap)
156 {
157 const char *hostname = va_arg(ap, const char *);
159 SDB_SVC(obj)->hostname = strdup(hostname);
160 if (! SDB_SVC(obj)->hostname)
161 return -1;
163 SDB_SVC(obj)->_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)
170 {
171 assert(obj);
173 if (SDB_SVC(obj)->hostname)
174 free(SDB_SVC(obj)->hostname);
175 } /* sdb_svc_destroy */
177 static sdb_object_t *
178 sdb_svc_clone(const sdb_object_t *obj)
179 {
180 const sdb_service_t *svc = (const sdb_service_t *)obj;
181 sdb_service_t *new;
183 new = sdb_service_create(svc->hostname, obj->name);
184 if (! new)
185 return NULL;
187 new->_last_update = svc->_last_update;
188 return SDB_OBJ(new);
189 } /* sdb_svc_clone */
191 const sdb_type_t sdb_host_type = {
192 sizeof(sdb_host_t),
194 sdb_host_init,
195 sdb_host_destroy,
196 sdb_host_do_clone
197 };
199 const sdb_type_t sdb_attribute_type = {
200 sizeof(sdb_attribute_t),
202 sdb_attr_init,
203 sdb_attr_destroy,
204 sdb_attr_clone
205 };
207 const sdb_type_t sdb_service_type = {
208 sizeof(sdb_service_t),
210 sdb_svc_init,
211 sdb_svc_destroy,
212 sdb_svc_clone
213 };
215 /*
216 * public API
217 */
219 sdb_host_t *
220 sdb_host_create(const char *name)
221 {
222 sdb_object_t *obj;
224 if (! name)
225 return NULL;
227 obj = sdb_object_create(name, sdb_host_type);
228 if (! obj)
229 return NULL;
230 return SDB_HOST(obj);
231 } /* sdb_host_create */
233 int
234 sdb_store_host(const sdb_host_t *host)
235 {
236 sdb_time_t last_update;
237 sdb_host_t *old;
238 int status = 0;
240 if ((! host) || (! SDB_CONST_OBJ(host)->name))
241 return -1;
243 last_update = host->_last_update;
244 if (last_update <= 0)
245 last_update = sdb_gettime();
247 pthread_rwlock_wrlock(&host_lock);
249 if (! host_list) {
250 if (! (host_list = sdb_llist_create())) {
251 pthread_rwlock_unlock(&host_lock);
252 return -1;
253 }
254 }
256 old = SDB_HOST(sdb_llist_search_by_name(host_list,
257 SDB_CONST_OBJ(host)->name));
259 if (old) {
260 if (old->_last_update > last_update) {
261 sdb_log(SDB_LOG_DEBUG, "store: Cannot update host '%s' - "
262 "value too old (%"PRIscTIME" < %"PRIscTIME")",
263 SDB_CONST_OBJ(host)->name, last_update, old->_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->_last_update = last_update;
270 }
271 }
272 else {
273 sdb_host_t *new = SDB_HOST(sdb_object_clone(SDB_CONST_OBJ(host)));
274 if (! new) {
275 char errbuf[1024];
276 sdb_log(SDB_LOG_ERR, "store: Failed to clone host object: %s",
277 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_log(SDB_LOG_ERR, "store: Failed to initialize "
286 "host object '%s': %s", SDB_CONST_OBJ(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_log(SDB_LOG_ERR, "store: Failed to initialize "
298 "host object '%s': %s", SDB_CONST_OBJ(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_object_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)
319 {
320 sdb_host_t *host;
322 if (! name)
323 return NULL;
325 host = SDB_HOST(sdb_llist_search_by_name(host_list, name));
326 if (! host)
327 return NULL;
328 return host;
329 } /* sdb_store_get_host */
331 sdb_attribute_t *
332 sdb_attribute_create(const char *hostname,
333 const char *name, const char *value)
334 {
335 sdb_object_t *obj;
337 if ((! hostname) || (! name) || (! value))
338 return NULL;
340 obj = sdb_object_create(name, sdb_attribute_type, hostname, value);
341 if (! obj)
342 return NULL;
343 return SDB_ATTR(obj);
344 } /* sdb_attribute_create */
346 int
347 sdb_store_attribute(const sdb_attribute_t *attr)
348 {
349 sdb_host_t *host;
350 sdb_attribute_t *old;
351 sdb_time_t last_update;
353 int status = 0;
355 if (! attr)
356 return -1;
358 last_update = attr->_last_update;
359 if (last_update <= 0)
360 last_update = sdb_gettime();
362 if (! host_list)
363 return -1;
365 pthread_rwlock_wrlock(&host_lock);
367 host = SDB_HOST(sdb_llist_search_by_name(host_list, attr->hostname));
368 if (! host) {
369 pthread_rwlock_unlock(&host_lock);
370 return -1;
371 }
373 old = SDB_ATTR(sdb_llist_search_by_name(host->attributes,
374 SDB_CONST_OBJ(attr)->name));
375 if (old) {
376 if (old->_last_update > last_update) {
377 sdb_log(SDB_LOG_DEBUG, "store: Cannot update attribute "
378 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
379 attr->hostname, SDB_CONST_OBJ(attr)->name, last_update,
380 old->_last_update);
381 status = 1;
382 }
383 else {
384 old->_last_update = last_update;
385 }
386 }
387 else {
388 sdb_attribute_t *new = SDB_ATTR(sdb_object_clone(SDB_CONST_OBJ(attr)));
389 if (! new) {
390 char errbuf[1024];
391 sdb_log(SDB_LOG_ERR, "store: Failed to clone attribute "
392 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
393 pthread_rwlock_unlock(&host_lock);
394 return -1;
395 }
397 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
398 sdb_object_cmp_by_name);
400 /* pass control to the list or destroy in case of an error */
401 sdb_object_deref(SDB_OBJ(new));
402 }
404 pthread_rwlock_unlock(&host_lock);
405 return status;
406 } /* sdb_store_attribute */
408 sdb_service_t *
409 sdb_service_create(const char *hostname, const char *name)
410 {
411 sdb_object_t *obj;
413 if ((! hostname) || (! name))
414 return NULL;
416 obj = sdb_object_create(name, sdb_service_type, hostname);
417 if (! obj)
418 return NULL;
419 return SDB_SVC(obj);
420 } /* sdb_service_create */
422 int
423 sdb_store_service(const sdb_service_t *svc)
424 {
425 sdb_host_t *host;
426 sdb_service_t *old;
427 sdb_time_t last_update;
429 int status = 0;
431 if (! svc)
432 return -1;
434 last_update = svc->_last_update;
435 if (last_update <= 0)
436 last_update = sdb_gettime();
438 if (! host_list)
439 return -1;
441 pthread_rwlock_wrlock(&host_lock);
443 host = SDB_HOST(sdb_llist_search_by_name(host_list, svc->hostname));
444 if (! host) {
445 pthread_rwlock_unlock(&host_lock);
446 return -1;
447 }
449 old = SDB_SVC(sdb_llist_search_by_name(host->services,
450 SDB_CONST_OBJ(svc)->name));
451 if (old) {
452 if (old->_last_update > last_update) {
453 sdb_log(SDB_LOG_DEBUG, "store: Cannot update service "
454 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
455 svc->hostname, SDB_CONST_OBJ(svc)->name, last_update,
456 old->_last_update);
457 status = 1;
458 }
459 else {
460 old->_last_update = last_update;
461 }
462 }
463 else {
464 sdb_service_t *new = SDB_SVC(sdb_object_clone(SDB_CONST_OBJ(svc)));
465 if (! new) {
466 char errbuf[1024];
467 sdb_log(SDB_LOG_ERR, "store: Failed to clone service "
468 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
469 pthread_rwlock_unlock(&host_lock);
470 return -1;
471 }
473 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
474 sdb_object_cmp_by_name);
476 /* pass control to the list or destroy in case of an error */
477 sdb_object_deref(SDB_OBJ(new));
478 }
480 pthread_rwlock_unlock(&host_lock);
481 return status;
482 } /* sdb_store_service */
484 const sdb_service_t *
485 sdb_store_get_service(const sdb_host_t *host, const char *name)
486 {
487 sdb_service_t *svc;
489 if ((! host) || (! name))
490 return NULL;
492 svc = SDB_SVC(sdb_llist_search_by_name(host->services, name));
493 if (! svc)
494 return NULL;
495 return svc;
496 } /* sdb_store_get_service */
498 int
499 sdb_store_dump(FILE *fh)
500 {
501 sdb_llist_iter_t *host_iter;
503 if (! fh)
504 return -1;
506 pthread_rwlock_rdlock(&host_lock);
508 host_iter = sdb_llist_get_iter(host_list);
509 if (! host_iter) {
510 pthread_rwlock_unlock(&host_lock);
511 return -1;
512 }
514 while (sdb_llist_iter_has_next(host_iter)) {
515 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
516 sdb_llist_iter_t *svc_iter;
517 sdb_llist_iter_t *attr_iter;
519 char time_str[64];
521 assert(host);
523 if (! sdb_strftime(time_str, sizeof(time_str),
524 "%F %T %z", host->_last_update))
525 snprintf(time_str, sizeof(time_str), "<error>");
526 time_str[sizeof(time_str) - 1] = '\0';
528 fprintf(fh, "Host '%s' (last updated: %s):\n",
529 SDB_OBJ(host)->name, time_str);
531 attr_iter = sdb_llist_get_iter(host->attributes);
532 if (! attr_iter) {
533 char errbuf[1024];
534 fprintf(fh, "Failed to retrieve attributes: %s\n",
535 sdb_strerror(errno, errbuf, sizeof(errbuf)));
536 continue;
537 }
539 while (sdb_llist_iter_has_next(attr_iter)) {
540 sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
541 assert(attr);
543 if (! sdb_strftime(time_str, sizeof(time_str),
544 "%F %T %z", attr->_last_update))
545 snprintf(time_str, sizeof(time_str), "<error>");
546 time_str[sizeof(time_str) - 1] = '\0';
548 fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
549 SDB_OBJ(attr)->name, attr->attr_value, time_str);
550 }
552 sdb_llist_iter_destroy(attr_iter);
554 svc_iter = sdb_llist_get_iter(host->services);
555 if (! svc_iter) {
556 char errbuf[1024];
557 fprintf(fh, "Failed to retrieve services: %s\n",
558 sdb_strerror(errno, errbuf, sizeof(errbuf)));
559 continue;
560 }
562 while (sdb_llist_iter_has_next(svc_iter)) {
563 sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
564 assert(svc);
566 if (! sdb_strftime(time_str, sizeof(time_str),
567 "%F %T %z", svc->_last_update))
568 snprintf(time_str, sizeof(time_str), "<error>");
569 time_str[sizeof(time_str) - 1] = '\0';
571 fprintf(fh, "\tService '%s' (last updated: %s)\n",
572 SDB_OBJ(svc)->name, time_str);
573 }
575 sdb_llist_iter_destroy(svc_iter);
576 }
578 sdb_llist_iter_destroy(host_iter);
579 pthread_rwlock_unlock(&host_lock);
580 return 0;
581 } /* sdb_store_dump */
583 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */