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_fbhash.h"
32 #include "utils_avltree.h"
34 struct fbhash_s
35 {
36 char *filename;
37 time_t mtime;
39 pthread_mutex_t lock;
40 c_avl_tree_t *tree;
41 };
43 /*
44 * Private functions
45 */
46 static void fbh_free_tree (c_avl_tree_t *tree) /* {{{ */
47 {
48 int status;
50 if (tree == NULL)
51 return;
53 while (42)
54 {
55 char *key = NULL;
56 char *value = NULL;
58 status = c_avl_pick (tree, (void *) &key, (void *) &value);
59 if (status != 0)
60 break;
62 free (key);
63 free (value);
64 }
66 c_avl_destroy (tree);
67 } /* }}} void fbh_free_tree */
69 static int fbh_read_file (fbhash_t *h) /* {{{ */
70 {
71 FILE *fh;
72 char buffer[4096];
73 struct flock fl = { 0 };
74 c_avl_tree_t *tree;
75 int status;
77 fh = fopen (h->filename, "r");
78 if (fh == NULL)
79 return (-1);
81 fl.l_type = F_RDLCK;
82 fl.l_whence = SEEK_SET;
83 /* TODO: Lock file? -> fcntl */
85 status = fcntl (fileno (fh), F_SETLK, &fl);
86 if (status != 0)
87 {
88 fclose (fh);
89 return (-1);
90 }
92 tree = c_avl_create ((int (*) (const void *, const void *)) strcmp);
93 if (tree == NULL)
94 {
95 fclose (fh);
96 return (-1);
97 }
99 /* Read `fh' into `tree' */
100 while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
101 {
102 size_t len;
103 char *key;
104 char *value;
106 char *key_copy;
107 char *value_copy;
109 buffer[sizeof (buffer) - 1] = 0;
110 len = strlen (buffer);
112 /* Remove trailing newline characters. */
113 while ((len > 0)
114 && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
115 {
116 len--;
117 buffer[len] = 0;
118 }
120 /* Seek first non-space character */
121 key = buffer;
122 while ((*key != 0) && isspace ((int) *key))
123 key++;
125 /* Skip empty lines and comments */
126 if ((key[0] == 0) || (key[0] == '#'))
127 continue;
129 /* Seek first colon */
130 value = strchr (key, ':');
131 if (value == NULL)
132 continue;
134 /* Null-terminate `key'. */
135 *value = 0;
136 value++;
138 /* Skip leading whitespace */
139 while ((*value != 0) && isspace ((int) *value))
140 value++;
142 /* Skip lines without value */
143 if (value[0] == 0)
144 continue;
146 key_copy = strdup (key);
147 value_copy = strdup (value);
149 if ((key_copy == NULL) || (value_copy == NULL))
150 {
151 free (key_copy);
152 free (value_copy);
153 continue;
154 }
156 status = c_avl_insert (tree, key_copy, value_copy);
157 if (status != 0)
158 {
159 free (key_copy);
160 free (value_copy);
161 continue;
162 }
164 DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
165 key, value);
166 } /* }}} while (fgets) */
168 fclose (fh);
170 fbh_free_tree (h->tree);
171 h->tree = tree;
173 return (0);
174 } /* }}} int fbh_read_file */
176 static int fbh_check_file (fbhash_t *h) /* {{{ */
177 {
178 struct stat statbuf = { 0 };
179 int status;
181 status = stat (h->filename, &statbuf);
182 if (status != 0)
183 return (-1);
185 if (h->mtime >= statbuf.st_mtime)
186 return (0);
188 status = fbh_read_file (h);
189 if (status == 0)
190 h->mtime = statbuf.st_mtime;
192 return (status);
193 } /* }}} int fbh_check_file */
195 /*
196 * Public functions
197 */
198 fbhash_t *fbh_create (const char *file) /* {{{ */
199 {
200 fbhash_t *h;
201 int status;
203 if (file == NULL)
204 return (NULL);
206 h = calloc (1, sizeof (*h));
207 if (h == NULL)
208 return (NULL);
210 h->filename = strdup (file);
211 if (h->filename == NULL)
212 {
213 free (h);
214 return (NULL);
215 }
217 h->mtime = 0;
218 pthread_mutex_init (&h->lock, /* attr = */ NULL);
220 status = fbh_check_file (h);
221 if (status != 0)
222 {
223 fbh_destroy (h);
224 free (h);
225 return (NULL);
226 }
228 return (h);
229 } /* }}} fbhash_t *fbh_create */
231 void fbh_destroy (fbhash_t *h) /* {{{ */
232 {
233 if (h == NULL)
234 return;
236 pthread_mutex_destroy (&h->lock);
237 free (h->filename);
238 fbh_free_tree (h->tree);
239 } /* }}} void fbh_destroy */
241 char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
242 {
243 char *value;
244 char *value_copy;
245 int status;
247 if ((h == NULL) || (key == NULL))
248 return (NULL);
250 value = NULL;
251 value_copy = NULL;
253 pthread_mutex_lock (&h->lock);
255 /* TODO: Checking this every time may be a bit much..? */
256 fbh_check_file (h);
258 status = c_avl_get (h->tree, key, (void *) &value);
259 if (status == 0)
260 {
261 assert (value != NULL);
262 value_copy = strdup (value);
263 }
265 pthread_mutex_unlock (&h->lock);
267 return (value_copy);
268 } /* }}} char *fbh_get */
270 /* vim: set sw=2 sts=2 et fdm=marker : */