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"
28 #include "plugin.h"
30 #include "utils_fbhash.h"
31 #include "utils_avltree.h"
33 struct fbhash_s
34 {
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 {
54 char *key = NULL;
55 char *value = NULL;
57 status = c_avl_pick (tree, (void *) &key, (void *) &value);
58 if (status != 0)
59 break;
61 free (key);
62 free (value);
63 }
65 c_avl_destroy (tree);
66 } /* }}} void fbh_free_tree */
68 static int fbh_read_file (fbhash_t *h) /* {{{ */
69 {
70 FILE *fh;
71 char buffer[4096];
72 struct flock fl;
73 c_avl_tree_t *tree;
74 int status;
76 fh = fopen (h->filename, "r");
77 if (fh == NULL)
78 return (-1);
80 memset (&fl, 0, sizeof (fl));
81 fl.l_type = F_RDLCK;
82 fl.l_whence = SEEK_SET;
83 fl.l_start = 0;
84 fl.l_len = 0; /* == entire file */
85 /* TODO: Lock file? -> fcntl */
87 status = fcntl (fileno (fh), F_SETLK, &fl);
88 if (status != 0)
89 {
90 fclose (fh);
91 return (-1);
92 }
94 tree = c_avl_create ((int (*) (const void *, const void *)) strcmp);
95 if (tree == NULL)
96 {
97 fclose (fh);
98 return (-1);
99 }
101 /* Read `fh' into `tree' */
102 while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
103 {
104 size_t len;
105 char *key;
106 char *value;
108 char *key_copy;
109 char *value_copy;
111 buffer[sizeof (buffer) - 1] = 0;
112 len = strlen (buffer);
114 /* Remove trailing newline characters. */
115 while ((len > 0)
116 && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
117 {
118 len--;
119 buffer[len] = 0;
120 }
122 /* Seek first non-space character */
123 key = buffer;
124 while ((*key != 0) && isspace ((int) *key))
125 key++;
127 /* Skip empty lines and comments */
128 if ((key[0] == 0) || (key[0] == '#'))
129 continue;
131 /* Seek first colon */
132 value = strchr (key, ':');
133 if (value == NULL)
134 continue;
136 /* Null-terminate `key'. */
137 *value = 0;
138 value++;
140 /* Skip leading whitespace */
141 while ((*value != 0) && isspace ((int) *value))
142 value++;
144 /* Skip lines without value */
145 if (value[0] == 0)
146 continue;
148 key_copy = strdup (key);
149 value_copy = strdup (value);
151 if ((key_copy == NULL) || (value_copy == NULL))
152 {
153 free (key_copy);
154 free (value_copy);
155 continue;
156 }
158 status = c_avl_insert (tree, key_copy, value_copy);
159 if (status != 0)
160 {
161 free (key_copy);
162 free (value_copy);
163 continue;
164 }
166 DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
167 key, value);
168 } /* }}} while (fgets) */
170 fclose (fh);
172 fbh_free_tree (h->tree);
173 h->tree = tree;
175 return (0);
176 } /* }}} int fbh_read_file */
178 static int fbh_check_file (fbhash_t *h) /* {{{ */
179 {
180 struct stat statbuf;
181 int status;
183 memset (&statbuf, 0, sizeof (statbuf));
185 status = stat (h->filename, &statbuf);
186 if (status != 0)
187 return (-1);
189 if (h->mtime >= statbuf.st_mtime)
190 return (0);
192 status = fbh_read_file (h);
193 if (status == 0)
194 h->mtime = statbuf.st_mtime;
196 return (status);
197 } /* }}} int fbh_check_file */
199 /*
200 * Public functions
201 */
202 fbhash_t *fbh_create (const char *file) /* {{{ */
203 {
204 fbhash_t *h;
205 int status;
207 if (file == NULL)
208 return (NULL);
210 h = calloc (1, sizeof (*h));
211 if (h == NULL)
212 return (NULL);
214 h->filename = strdup (file);
215 if (h->filename == NULL)
216 {
217 free (h);
218 return (NULL);
219 }
221 h->mtime = 0;
222 pthread_mutex_init (&h->lock, /* attr = */ NULL);
224 status = fbh_check_file (h);
225 if (status != 0)
226 {
227 fbh_destroy (h);
228 free (h);
229 return (NULL);
230 }
232 return (h);
233 } /* }}} fbhash_t *fbh_create */
235 void fbh_destroy (fbhash_t *h) /* {{{ */
236 {
237 if (h == NULL)
238 return;
240 pthread_mutex_destroy (&h->lock);
241 free (h->filename);
242 fbh_free_tree (h->tree);
243 } /* }}} void fbh_destroy */
245 char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
246 {
247 char *value;
248 char *value_copy;
249 int status;
251 if ((h == NULL) || (key == NULL))
252 return (NULL);
254 value = NULL;
255 value_copy = NULL;
257 pthread_mutex_lock (&h->lock);
259 /* TODO: Checking this every time may be a bit much..? */
260 fbh_check_file (h);
262 status = c_avl_get (h->tree, key, (void *) &value);
263 if (status == 0)
264 {
265 assert (value != NULL);
266 value_copy = strdup (value);
267 }
269 pthread_mutex_unlock (&h->lock);
271 return (value_copy);
272 } /* }}} char *fbh_get */
274 /* vim: set sw=2 sts=2 et fdm=marker : */