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 status = sdb_llist_insert_sorted(host_list, SDB_OBJ(new),
320 sdb_object_cmp_by_name);
322 /* pass control to the list or destroy in case of an error */
323 sdb_object_deref(SDB_OBJ(new));
324 }
326 pthread_rwlock_unlock(&host_lock);
327 return status;
328 } /* sdb_store_host */
330 _Bool
331 sdb_store_has_host(const char *name)
332 {
333 sdb_host_t *host;
335 if (! name)
336 return NULL;
338 host = SDB_HOST(sdb_llist_search_by_name(host_list, name));
339 return host != NULL;
340 } /* sdb_store_has_host */
342 int
343 sdb_store_attribute(const char *hostname, const char *key, const char *value,
344 sdb_time_t last_update)
345 {
346 sdb_host_t *host;
347 sdb_attribute_t *old;
349 int status = 0;
351 if ((! hostname) || (! key))
352 return -1;
354 if (last_update <= 0)
355 last_update = sdb_gettime();
357 if (! host_list)
358 return -1;
360 pthread_rwlock_wrlock(&host_lock);
362 host = SDB_HOST(sdb_llist_search_by_name(host_list, hostname));
363 if (! host) {
364 pthread_rwlock_unlock(&host_lock);
365 return -1;
366 }
368 old = SDB_ATTR(sdb_llist_search_by_name(host->attributes, key));
369 if (old) {
370 if (old->_last_update > last_update) {
371 sdb_log(SDB_LOG_DEBUG, "store: Cannot update attribute "
372 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
373 hostname, key, last_update, old->_last_update);
374 status = 1;
375 }
376 else {
377 old->_last_update = last_update;
378 }
379 }
380 else {
381 sdb_attribute_t *new = SDB_ATTR(sdb_object_create(key,
382 sdb_attribute_type, hostname, value));
383 if (! new) {
384 char errbuf[1024];
385 sdb_log(SDB_LOG_ERR, "store: Failed to clone attribute "
386 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
387 pthread_rwlock_unlock(&host_lock);
388 return -1;
389 }
391 status = sdb_llist_insert_sorted(host->attributes, SDB_OBJ(new),
392 sdb_object_cmp_by_name);
394 /* pass control to the list or destroy in case of an error */
395 sdb_object_deref(SDB_OBJ(new));
396 }
398 pthread_rwlock_unlock(&host_lock);
399 return status;
400 } /* sdb_store_attribute */
402 int
403 sdb_store_service(const char *hostname, const char *name,
404 sdb_time_t last_update)
405 {
406 sdb_host_t *host;
407 sdb_service_t *old;
409 int status = 0;
411 if ((! hostname) || (! name))
412 return -1;
414 if (last_update <= 0)
415 last_update = sdb_gettime();
417 if (! host_list)
418 return -1;
420 pthread_rwlock_wrlock(&host_lock);
422 host = SDB_HOST(sdb_llist_search_by_name(host_list, hostname));
423 if (! host) {
424 pthread_rwlock_unlock(&host_lock);
425 return -1;
426 }
428 old = SDB_SVC(sdb_llist_search_by_name(host->services, name));
429 if (old) {
430 if (old->_last_update > last_update) {
431 sdb_log(SDB_LOG_DEBUG, "store: Cannot update service "
432 "'%s/%s' - value too old (%"PRIscTIME" < %"PRIscTIME")",
433 hostname, name, last_update, old->_last_update);
434 status = 1;
435 }
436 else {
437 old->_last_update = last_update;
438 }
439 }
440 else {
441 sdb_service_t *new = SDB_SVC(sdb_object_create(name, sdb_service_type,
442 hostname));
443 if (! new) {
444 char errbuf[1024];
445 sdb_log(SDB_LOG_ERR, "store: Failed to clone service "
446 "object: %s", sdb_strerror(errno, errbuf, sizeof(errbuf)));
447 pthread_rwlock_unlock(&host_lock);
448 return -1;
449 }
451 status = sdb_llist_insert_sorted(host->services, SDB_OBJ(new),
452 sdb_object_cmp_by_name);
454 /* pass control to the list or destroy in case of an error */
455 sdb_object_deref(SDB_OBJ(new));
456 }
458 pthread_rwlock_unlock(&host_lock);
459 return status;
460 } /* sdb_store_service */
462 int
463 sdb_store_dump(FILE *fh)
464 {
465 sdb_llist_iter_t *host_iter;
467 if (! fh)
468 return -1;
470 pthread_rwlock_rdlock(&host_lock);
472 host_iter = sdb_llist_get_iter(host_list);
473 if (! host_iter) {
474 pthread_rwlock_unlock(&host_lock);
475 return -1;
476 }
478 while (sdb_llist_iter_has_next(host_iter)) {
479 sdb_host_t *host = SDB_HOST(sdb_llist_iter_get_next(host_iter));
480 sdb_llist_iter_t *svc_iter;
481 sdb_llist_iter_t *attr_iter;
483 char time_str[64];
485 assert(host);
487 if (! sdb_strftime(time_str, sizeof(time_str),
488 "%F %T %z", host->_last_update))
489 snprintf(time_str, sizeof(time_str), "<error>");
490 time_str[sizeof(time_str) - 1] = '\0';
492 fprintf(fh, "Host '%s' (last updated: %s):\n",
493 SDB_OBJ(host)->name, time_str);
495 attr_iter = sdb_llist_get_iter(host->attributes);
496 if (! attr_iter) {
497 char errbuf[1024];
498 fprintf(fh, "Failed to retrieve attributes: %s\n",
499 sdb_strerror(errno, errbuf, sizeof(errbuf)));
500 continue;
501 }
503 while (sdb_llist_iter_has_next(attr_iter)) {
504 sdb_attribute_t *attr = SDB_ATTR(sdb_llist_iter_get_next(attr_iter));
505 assert(attr);
507 if (! sdb_strftime(time_str, sizeof(time_str),
508 "%F %T %z", attr->_last_update))
509 snprintf(time_str, sizeof(time_str), "<error>");
510 time_str[sizeof(time_str) - 1] = '\0';
512 fprintf(fh, "\tAttribute '%s' -> '%s' (last updated: %s)\n",
513 SDB_OBJ(attr)->name, attr->attr_value, time_str);
514 }
516 sdb_llist_iter_destroy(attr_iter);
518 svc_iter = sdb_llist_get_iter(host->services);
519 if (! svc_iter) {
520 char errbuf[1024];
521 fprintf(fh, "Failed to retrieve services: %s\n",
522 sdb_strerror(errno, errbuf, sizeof(errbuf)));
523 continue;
524 }
526 while (sdb_llist_iter_has_next(svc_iter)) {
527 sdb_service_t *svc = SDB_SVC(sdb_llist_iter_get_next(svc_iter));
528 assert(svc);
530 if (! sdb_strftime(time_str, sizeof(time_str),
531 "%F %T %z", svc->_last_update))
532 snprintf(time_str, sizeof(time_str), "<error>");
533 time_str[sizeof(time_str) - 1] = '\0';
535 fprintf(fh, "\tService '%s' (last updated: %s)\n",
536 SDB_OBJ(svc)->name, time_str);
537 }
539 sdb_llist_iter_destroy(svc_iter);
540 }
542 sdb_llist_iter_destroy(host_iter);
543 pthread_rwlock_unlock(&host_lock);
544 return 0;
545 } /* sdb_store_dump */
547 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */