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