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 <pthread.h>
32 #include "utils_fbhash.h"
33 #include "utils_avltree.h"
35 struct fbhash_s
36 {
37 char *filename;
38 time_t mtime;
40 pthread_mutex_t lock;
41 c_avl_tree_t *tree;
42 };
44 /*
45 * Private functions
46 */
47 static void fbh_free_tree (c_avl_tree_t *tree) /* {{{ */
48 {
49 int status;
51 if (tree == NULL)
52 return;
54 while (42)
55 {
56 char *key = NULL;
57 char *value = NULL;
59 status = c_avl_pick (tree, (void *) &key, (void *) &value);
60 if (status != 0)
61 break;
63 free (key);
64 free (value);
65 }
67 c_avl_destroy (tree);
68 } /* }}} void fbh_free_tree */
70 static int fbh_read_file (fbhash_t *h) /* {{{ */
71 {
72 FILE *fh;
73 char buffer[4096];
74 struct flock fl;
75 c_avl_tree_t *tree;
76 int status;
78 fh = fopen (h->filename, "r");
79 if (fh == NULL)
80 return (-1);
82 memset (&fl, 0, sizeof (fl));
83 fl.l_type = F_RDLCK;
84 fl.l_whence = SEEK_SET;
85 fl.l_start = 0;
86 fl.l_len = 0; /* == entire file */
87 /* TODO: Lock file? -> fcntl */
89 status = fcntl (fileno (fh), F_SETLK, &fl);
90 if (status != 0)
91 {
92 fclose (fh);
93 return (-1);
94 }
96 tree = c_avl_create ((void *) strcmp);
97 if (tree == NULL)
98 {
99 fclose (fh);
100 return (-1);
101 }
103 /* Read `fh' into `tree' */
104 while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
105 {
106 size_t len;
107 char *key;
108 char *value;
110 char *key_copy;
111 char *value_copy;
113 buffer[sizeof (buffer) - 1] = 0;
114 len = strlen (buffer);
116 /* Remove trailing newline characters. */
117 while ((len > 0)
118 && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
119 {
120 len--;
121 buffer[len] = 0;
122 }
124 /* Seek first non-space character */
125 key = buffer;
126 while ((*key != 0) && isspace ((int) *key))
127 key++;
129 /* Skip empty lines and comments */
130 if ((key[0] == 0) || (key[0] == '#'))
131 continue;
133 /* Seek first colon */
134 value = strchr (key, ':');
135 if (value == NULL)
136 continue;
138 /* Null-terminate `key'. */
139 *value = 0;
140 value++;
142 /* Skip leading whitespace */
143 while ((*value != 0) && isspace ((int) *value))
144 value++;
146 /* Skip lines without value */
147 if (value[0] == 0)
148 continue;
150 key_copy = strdup (key);
151 value_copy = strdup (value);
153 if ((key_copy == NULL) || (value_copy == NULL))
154 {
155 free (key_copy);
156 free (value_copy);
157 continue;
158 }
160 status = c_avl_insert (tree, key_copy, value_copy);
161 if (status != 0)
162 {
163 free (key_copy);
164 free (value_copy);
165 continue;
166 }
168 DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
169 key, value);
170 } /* }}} while (fgets) */
172 fclose (fh);
174 fbh_free_tree (h->tree);
175 h->tree = tree;
177 return (0);
178 } /* }}} int fbh_read_file */
180 static int fbh_check_file (fbhash_t *h) /* {{{ */
181 {
182 struct stat statbuf;
183 int status;
185 memset (&statbuf, 0, sizeof (statbuf));
187 status = stat (h->filename, &statbuf);
188 if (status != 0)
189 return (-1);
191 if (h->mtime >= statbuf.st_mtime)
192 return (0);
194 status = fbh_read_file (h);
195 if (status == 0)
196 h->mtime = statbuf.st_mtime;
198 return (status);
199 } /* }}} int fbh_check_file */
201 /*
202 * Public functions
203 */
204 fbhash_t *fbh_create (const char *file) /* {{{ */
205 {
206 fbhash_t *h;
207 int status;
209 if (file == NULL)
210 return (NULL);
212 h = calloc (1, sizeof (*h));
213 if (h == NULL)
214 return (NULL);
216 h->filename = strdup (file);
217 if (h->filename == NULL)
218 {
219 free (h);
220 return (NULL);
221 }
223 h->mtime = 0;
224 pthread_mutex_init (&h->lock, /* attr = */ NULL);
226 status = fbh_check_file (h);
227 if (status != 0)
228 {
229 fbh_destroy (h);
230 free (h);
231 return (NULL);
232 }
234 return (h);
235 } /* }}} fbhash_t *fbh_create */
237 void fbh_destroy (fbhash_t *h) /* {{{ */
238 {
239 if (h == NULL)
240 return;
242 pthread_mutex_destroy (&h->lock);
243 free (h->filename);
244 fbh_free_tree (h->tree);
245 } /* }}} void fbh_destroy */
247 char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
248 {
249 char *value;
250 char *value_copy;
251 int status;
253 if ((h == NULL) || (key == NULL))
254 return (NULL);
256 value = NULL;
257 value_copy = NULL;
259 pthread_mutex_lock (&h->lock);
261 /* TODO: Checking this every time may be a bit much..? */
262 fbh_check_file (h);
264 status = c_avl_get (h->tree, key, (void *) &value);
265 if (status == 0)
266 {
267 assert (value != NULL);
268 value_copy = strdup (value);
269 }
271 pthread_mutex_unlock (&h->lock);
273 return (value_copy);
274 } /* }}} char *fbh_get */
276 /* vim: set sw=2 sts=2 et fdm=marker : */