1 /**
2 * collectd - src/utils_fbhash.c
3 * Copyright (C) 2009 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Florian octo Forster <octo at verplant.org>
20 **/
22 #include "collectd.h"
23 #include "plugin.h"
25 #include <pthread.h>
27 #include "utils_fbhash.h"
28 #include "utils_avltree.h"
30 struct fbhash_s
31 {
32 char *filename;
33 time_t mtime;
35 pthread_mutex_t lock;
36 c_avl_tree_t *tree;
37 };
39 /*
40 * Private functions
41 */
42 static void fbh_free_tree (c_avl_tree_t *tree) /* {{{ */
43 {
44 int status;
46 if (tree == NULL)
47 return;
49 while (42)
50 {
51 char *key = NULL;
52 char *value = NULL;
54 status = c_avl_pick (tree, (void *) &key, (void *) &value);
55 if (status != 0)
56 break;
58 free (key);
59 free (value);
60 }
62 c_avl_destroy (tree);
63 } /* }}} void fbh_free_tree */
65 static int fbh_read_file (fbhash_t *h) /* {{{ */
66 {
67 FILE *fh;
68 char buffer[4096];
69 struct flock fl;
70 c_avl_tree_t *tree;
71 int status;
73 fh = fopen (h->filename, "r");
74 if (fh == NULL)
75 return (-1);
77 memset (&fl, 0, sizeof (fl));
78 fl.l_type = F_RDLCK;
79 fl.l_whence = SEEK_SET;
80 fl.l_start = 0;
81 fl.l_len = 0; /* == entire file */
82 /* TODO: Lock file? -> fcntl */
84 status = fcntl (fileno (fh), F_SETLK, &fl);
85 if (status != 0)
86 {
87 fclose (fh);
88 return (-1);
89 }
91 tree = c_avl_create ((void *) strcmp);
92 if (tree == NULL)
93 {
94 fclose (fh);
95 return (-1);
96 }
98 /* Read `fh' into `tree' */
99 while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
100 {
101 size_t len;
102 char *key;
103 char *value;
105 char *key_copy;
106 char *value_copy;
108 buffer[sizeof (buffer) - 1] = 0;
109 len = strlen (buffer);
111 /* Remove trailing newline characters. */
112 while ((len > 0)
113 && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
114 {
115 len--;
116 buffer[len] = 0;
117 }
119 /* Seek first non-space character */
120 key = buffer;
121 while ((*key != 0) && isspace ((int) *key))
122 key++;
124 /* Skip empty lines and comments */
125 if ((key[0] == 0) || (key[0] == '#'))
126 continue;
128 /* Seek first colon */
129 value = strchr (key, ':');
130 if (value == NULL)
131 continue;
133 /* Null-terminate `key'. */
134 *value = 0;
135 value++;
137 /* Skip leading whitespace */
138 while ((*value != 0) && isspace ((int) *value))
139 value++;
141 /* Skip lines without value */
142 if (value[0] == 0)
143 continue;
145 key_copy = strdup (key);
146 value_copy = strdup (value);
148 if ((key_copy == NULL) || (value_copy == NULL))
149 {
150 free (key_copy);
151 free (value_copy);
152 continue;
153 }
155 status = c_avl_insert (tree, key_copy, value_copy);
156 if (status != 0)
157 {
158 free (key_copy);
159 free (value_copy);
160 continue;
161 }
163 DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
164 key, value);
165 } /* }}} while (fgets) */
167 fclose (fh);
169 fbh_free_tree (h->tree);
170 h->tree = tree;
172 return (0);
173 } /* }}} int fbh_read_file */
175 static int fbh_check_file (fbhash_t *h) /* {{{ */
176 {
177 struct stat statbuf;
178 int status;
180 memset (&statbuf, 0, sizeof (statbuf));
182 status = stat (h->filename, &statbuf);
183 if (status != 0)
184 return (-1);
186 if (h->mtime >= statbuf.st_mtime)
187 return (0);
189 status = fbh_read_file (h);
190 if (status == 0)
191 h->mtime = statbuf.st_mtime;
193 return (status);
194 } /* }}} int fbh_check_file */
196 /*
197 * Public functions
198 */
199 fbhash_t *fbh_create (const char *file) /* {{{ */
200 {
201 fbhash_t *h;
202 int status;
204 if (file == NULL)
205 return (NULL);
207 h = malloc (sizeof (*h));
208 if (h == NULL)
209 return (NULL);
210 memset (h, 0, sizeof (*h));
212 h->filename = strdup (file);
213 if (h->filename == NULL)
214 {
215 free (h);
216 return (NULL);
217 }
219 h->mtime = 0;
220 pthread_mutex_init (&h->lock, /* attr = */ NULL);
222 status = fbh_check_file (h);
223 if (status != 0)
224 {
225 fbh_destroy (h);
226 return (NULL);
227 }
229 return (h);
230 } /* }}} fbhash_t *fbh_create */
232 void fbh_destroy (fbhash_t *h) /* {{{ */
233 {
234 if (h == NULL)
235 return;
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 everytime 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 : */