1 /**
2 * collectd - src/utils_fbhash.c
3 * Copyright (C) 2009 Florian octo Forster
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Florian octo Forster <octo at collectd.org>
25 **/
27 #include "collectd.h"
29 #include "plugin.h"
31 #include "utils_avltree.h"
32 #include "utils_fbhash.h"
34 struct fbhash_s {
35 char *filename;
36 time_t mtime;
38 pthread_mutex_t lock;
39 c_avl_tree_t *tree;
40 };
42 /*
43 * Private functions
44 */
45 static void fbh_free_tree(c_avl_tree_t *tree) /* {{{ */
46 {
47 int status;
49 if (tree == NULL)
50 return;
52 while (42) {
53 char *key = NULL;
54 char *value = NULL;
56 status = c_avl_pick(tree, (void *)&key, (void *)&value);
57 if (status != 0)
58 break;
60 free(key);
61 free(value);
62 }
64 c_avl_destroy(tree);
65 } /* }}} void fbh_free_tree */
67 static int fbh_read_file(fbhash_t *h) /* {{{ */
68 {
69 FILE *fh;
70 char buffer[4096];
71 struct flock fl = {0};
72 c_avl_tree_t *tree;
73 int status;
75 fh = fopen(h->filename, "r");
76 if (fh == NULL)
77 return (-1);
79 fl.l_type = F_RDLCK;
80 fl.l_whence = SEEK_SET;
81 /* TODO: Lock file? -> fcntl */
83 status = fcntl(fileno(fh), F_SETLK, &fl);
84 if (status != 0) {
85 fclose(fh);
86 return (-1);
87 }
89 tree = c_avl_create((int (*)(const void *, const void *))strcmp);
90 if (tree == NULL) {
91 fclose(fh);
92 return (-1);
93 }
95 /* Read `fh' into `tree' */
96 while (fgets(buffer, sizeof(buffer), fh) != NULL) /* {{{ */
97 {
98 size_t len;
99 char *key;
100 char *value;
102 char *key_copy;
103 char *value_copy;
105 buffer[sizeof(buffer) - 1] = 0;
106 len = strlen(buffer);
108 /* Remove trailing newline characters. */
109 while ((len > 0) &&
110 ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r'))) {
111 len--;
112 buffer[len] = 0;
113 }
115 /* Seek first non-space character */
116 key = buffer;
117 while ((*key != 0) && isspace((int)*key))
118 key++;
120 /* Skip empty lines and comments */
121 if ((key[0] == 0) || (key[0] == '#'))
122 continue;
124 /* Seek first colon */
125 value = strchr(key, ':');
126 if (value == NULL)
127 continue;
129 /* Null-terminate `key'. */
130 *value = 0;
131 value++;
133 /* Skip leading whitespace */
134 while ((*value != 0) && isspace((int)*value))
135 value++;
137 /* Skip lines without value */
138 if (value[0] == 0)
139 continue;
141 key_copy = strdup(key);
142 value_copy = strdup(value);
144 if ((key_copy == NULL) || (value_copy == NULL)) {
145 free(key_copy);
146 free(value_copy);
147 continue;
148 }
150 status = c_avl_insert(tree, key_copy, value_copy);
151 if (status != 0) {
152 free(key_copy);
153 free(value_copy);
154 continue;
155 }
157 DEBUG("utils_fbhash: fbh_read_file: key = %s; value = %s;", key, value);
158 } /* }}} while (fgets) */
160 fclose(fh);
162 fbh_free_tree(h->tree);
163 h->tree = tree;
165 return (0);
166 } /* }}} int fbh_read_file */
168 static int fbh_check_file(fbhash_t *h) /* {{{ */
169 {
170 struct stat statbuf = {0};
171 int status;
173 status = stat(h->filename, &statbuf);
174 if (status != 0)
175 return (-1);
177 if (h->mtime >= statbuf.st_mtime)
178 return (0);
180 status = fbh_read_file(h);
181 if (status == 0)
182 h->mtime = statbuf.st_mtime;
184 return (status);
185 } /* }}} int fbh_check_file */
187 /*
188 * Public functions
189 */
190 fbhash_t *fbh_create(const char *file) /* {{{ */
191 {
192 fbhash_t *h;
193 int status;
195 if (file == NULL)
196 return (NULL);
198 h = calloc(1, sizeof(*h));
199 if (h == NULL)
200 return (NULL);
202 h->filename = strdup(file);
203 if (h->filename == NULL) {
204 free(h);
205 return (NULL);
206 }
208 h->mtime = 0;
209 pthread_mutex_init(&h->lock, /* attr = */ NULL);
211 status = fbh_check_file(h);
212 if (status != 0) {
213 fbh_destroy(h);
214 free(h);
215 return (NULL);
216 }
218 return (h);
219 } /* }}} fbhash_t *fbh_create */
221 void fbh_destroy(fbhash_t *h) /* {{{ */
222 {
223 if (h == NULL)
224 return;
226 pthread_mutex_destroy(&h->lock);
227 free(h->filename);
228 fbh_free_tree(h->tree);
229 } /* }}} void fbh_destroy */
231 char *fbh_get(fbhash_t *h, const char *key) /* {{{ */
232 {
233 char *value;
234 char *value_copy;
235 int status;
237 if ((h == NULL) || (key == NULL))
238 return (NULL);
240 value = NULL;
241 value_copy = NULL;
243 pthread_mutex_lock(&h->lock);
245 /* TODO: Checking this every time may be a bit much..? */
246 fbh_check_file(h);
248 status = c_avl_get(h->tree, key, (void *)&value);
249 if (status == 0) {
250 assert(value != NULL);
251 value_copy = strdup(value);
252 }
254 pthread_mutex_unlock(&h->lock);
256 return (value_copy);
257 } /* }}} char *fbh_get */
259 /* vim: set sw=2 sts=2 et fdm=marker : */