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