38b713e7f0d7a8536fb76101bd16bcea5a42bce1
[collectd.git] / src / utils_fbhash.c
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  **/
21
22 #include "collectd.h"
23 #include "plugin.h"
24
25 #include <pthread.h>
26
27 #include "utils_fbhash.h"
28 #include "utils_avltree.h"
29
30 struct fbhash_s
31 {
32   char *filename;
33   time_t mtime;
34
35   pthread_mutex_t lock;
36   c_avl_tree_t *tree;
37 };
38
39 /* 
40  * Private functions
41  */
42 static void fbh_free_tree (c_avl_tree_t *tree) /* {{{ */
43 {
44   int status;
45
46   if (tree == NULL)
47     return;
48
49   while (42)
50   {
51     char *key = NULL;
52     char *value = NULL;
53
54     status = c_avl_pick (tree, (void *) &key, (void *) &value);
55     if (status != 0)
56       break;
57
58     free (key);
59     free (value);
60   }
61
62   c_avl_destroy (tree);
63 } /* }}} void fbh_free_tree */
64
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;
72
73   fh = fopen (h->filename, "r");
74   if (fh == NULL)
75     return (-1);
76
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 */
83
84   status = fcntl (fileno (fh), F_SETLK, &fl);
85   if (status != 0)
86   {
87     fclose (fh);
88     return (-1);
89   }
90
91   tree = c_avl_create ((void *) strcmp);
92   if (tree == NULL)
93   {
94     fclose (fh);
95     return (-1);
96   }
97
98   /* Read `fh' into `tree' */
99   while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
100   {
101     size_t len;
102     char *key;
103     char *value;
104
105     char *key_copy;
106     char *value_copy;
107
108     buffer[sizeof (buffer) - 1] = 0;
109     len = strlen (buffer);
110
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     }
118
119     /* Seek first non-space character */
120     key = buffer;
121     while ((*key != 0) && isspace ((int) *key))
122       key++;
123
124     /* Skip empty lines and comments */
125     if ((key[0] == 0) || (key[0] == '#'))
126       continue;
127
128     /* Seek first colon */
129     value = strchr (key, ':');
130     if (value == NULL)
131       continue;
132
133     /* Null-terminate `key'. */
134     *value = 0;
135     value++;
136
137     /* Skip leading whitespace */
138     while ((*value != 0) && isspace ((int) *value))
139       value++;
140
141     /* Skip lines without value */
142     if (value[0] == 0)
143       continue;
144
145     key_copy = strdup (key);
146     value_copy = strdup (value);
147
148     if ((key_copy == NULL) || (value_copy == NULL))
149     {
150       free (key_copy);
151       free (value_copy);
152       continue;
153     }
154
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     }
162
163     DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
164         key, value);
165   } /* }}} while (fgets) */
166
167   fclose (fh);
168
169   fbh_free_tree (h->tree);
170   h->tree = tree;
171
172   return (0);
173 } /* }}} int fbh_read_file */
174
175 static int fbh_check_file (fbhash_t *h) /* {{{ */
176 {
177   struct stat statbuf;
178   int status;
179
180   memset (&statbuf, 0, sizeof (statbuf));
181
182   status = stat (h->filename, &statbuf);
183   if (status != 0)
184     return (-1);
185
186   if (h->mtime >= statbuf.st_mtime)
187     return (0);
188
189   status = fbh_read_file (h);
190   if (status == 0)
191     h->mtime = statbuf.st_mtime;
192
193   return (status);
194 } /* }}} int fbh_check_file */
195
196 /* 
197  * Public functions
198  */
199 fbhash_t *fbh_create (const char *file) /* {{{ */
200 {
201   fbhash_t *h;
202   int status;
203
204   if (file == NULL)
205     return (NULL);
206
207   h = malloc (sizeof (*h));
208   if (h == NULL)
209     return (NULL);
210   memset (h, 0, sizeof (*h));
211
212   h->filename = strdup (file);
213   if (h->filename == NULL)
214   {
215     free (h);
216     return (NULL);
217   }
218
219   h->mtime = 0;
220   pthread_mutex_init (&h->lock, /* attr = */ NULL);
221
222   status = fbh_check_file (h);
223   if (status != 0)
224   {
225     fbh_destroy (h);
226     return (NULL);
227   }
228
229   return (h);
230 } /* }}} fbhash_t *fbh_create */
231
232 void fbh_destroy (fbhash_t *h) /* {{{ */
233 {
234   if (h == NULL)
235     return;
236
237   pthread_mutex_destroy (&h->lock);
238   free (h->filename);
239   fbh_free_tree (h->tree);
240 } /* }}} void fbh_destroy */
241
242 char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
243 {
244   char *value;
245   char *value_copy;
246   int status;
247
248   if ((h == NULL) || (key == NULL))
249     return (NULL);
250
251   value = NULL;
252   value_copy = NULL;
253
254   pthread_mutex_lock (&h->lock);
255
256   /* TODO: Checking this every time may be a bit much..? */
257   fbh_check_file (h);
258
259   status = c_avl_get (h->tree, key, (void *) &value);
260   if (status == 0)
261   {
262     assert (value != NULL);
263     value_copy = strdup (value);
264   }
265
266   pthread_mutex_unlock (&h->lock);
267
268   return (value_copy);
269 } /* }}} char *fbh_get */
270
271 /* vim: set sw=2 sts=2 et fdm=marker : */