/** * collectd - src/utils_fbhash.c * Copyright (C) 2009 Florian octo Forster * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Florian octo Forster **/ #include "collectd.h" #include "plugin.h" #include "utils/avltree/avltree.h" #include "utils_fbhash.h" struct fbhash_s { char *filename; time_t mtime; pthread_mutex_t lock; c_avl_tree_t *tree; }; /* * Private functions */ static void fbh_free_tree(c_avl_tree_t *tree) /* {{{ */ { int status; if (tree == NULL) return; while (42) { char *key = NULL; char *value = NULL; status = c_avl_pick(tree, (void *)&key, (void *)&value); if (status != 0) break; free(key); free(value); } c_avl_destroy(tree); } /* }}} void fbh_free_tree */ static int fbh_read_file(fbhash_t *h) /* {{{ */ { FILE *fh; char buffer[4096]; struct flock fl = {0}; c_avl_tree_t *tree; int status; fh = fopen(h->filename, "r"); if (fh == NULL) return -1; fl.l_type = F_RDLCK; fl.l_whence = SEEK_SET; /* TODO: Lock file? -> fcntl */ status = fcntl(fileno(fh), F_SETLK, &fl); if (status != 0) { fclose(fh); return -1; } tree = c_avl_create((int (*)(const void *, const void *))strcmp); if (tree == NULL) { fclose(fh); return -1; } /* Read `fh' into `tree' */ while (fgets(buffer, sizeof(buffer), fh) != NULL) /* {{{ */ { size_t len; char *key; char *value; char *key_copy; char *value_copy; buffer[sizeof(buffer) - 1] = '\0'; len = strlen(buffer); /* Remove trailing newline characters. */ while ((len > 0) && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r'))) { len--; buffer[len] = 0; } /* Seek first non-space character */ key = buffer; while ((*key != 0) && isspace((int)*key)) key++; /* Skip empty lines and comments */ if ((key[0] == 0) || (key[0] == '#')) continue; /* Seek first colon */ value = strchr(key, ':'); if (value == NULL) continue; /* Null-terminate `key'. */ *value = 0; value++; /* Skip leading whitespace */ while ((*value != 0) && isspace((int)*value)) value++; /* Skip lines without value */ if (value[0] == 0) continue; key_copy = strdup(key); value_copy = strdup(value); if ((key_copy == NULL) || (value_copy == NULL)) { free(key_copy); free(value_copy); continue; } status = c_avl_insert(tree, key_copy, value_copy); if (status != 0) { free(key_copy); free(value_copy); continue; } DEBUG("utils_fbhash: fbh_read_file: key = %s; value = %s;", key, value); } /* }}} while (fgets) */ fclose(fh); fbh_free_tree(h->tree); h->tree = tree; return 0; } /* }}} int fbh_read_file */ static int fbh_check_file(fbhash_t *h) /* {{{ */ { struct stat statbuf = {0}; int status; status = stat(h->filename, &statbuf); if (status != 0) return -1; if (h->mtime >= statbuf.st_mtime) return 0; status = fbh_read_file(h); if (status == 0) h->mtime = statbuf.st_mtime; return status; } /* }}} int fbh_check_file */ /* * Public functions */ fbhash_t *fbh_create(const char *file) /* {{{ */ { fbhash_t *h; int status; if (file == NULL) return NULL; h = calloc(1, sizeof(*h)); if (h == NULL) return NULL; h->filename = strdup(file); if (h->filename == NULL) { free(h); return NULL; } h->mtime = 0; pthread_mutex_init(&h->lock, /* attr = */ NULL); status = fbh_check_file(h); if (status != 0) { fbh_destroy(h); free(h); return NULL; } return h; } /* }}} fbhash_t *fbh_create */ void fbh_destroy(fbhash_t *h) /* {{{ */ { if (h == NULL) return; pthread_mutex_destroy(&h->lock); free(h->filename); fbh_free_tree(h->tree); } /* }}} void fbh_destroy */ char *fbh_get(fbhash_t *h, const char *key) /* {{{ */ { char *value; char *value_copy; int status; if ((h == NULL) || (key == NULL)) return NULL; value = NULL; value_copy = NULL; pthread_mutex_lock(&h->lock); /* TODO: Checking this every time may be a bit much..? */ fbh_check_file(h); status = c_avl_get(h->tree, key, (void *)&value); if (status == 0) { assert(value != NULL); value_copy = strdup(value); } pthread_mutex_unlock(&h->lock); return value_copy; } /* }}} char *fbh_get */