Merge pull request #1821 from rubenk/memset
[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 "utils_fbhash.h"
31 #include "utils_avltree.h"
32
33 struct fbhash_s
34 {
35   char *filename;
36   time_t mtime;
37
38   pthread_mutex_t lock;
39   c_avl_tree_t *tree;
40 };
41
42 /*
43  * Private functions
44  */
45 static void fbh_free_tree (c_avl_tree_t *tree) /* {{{ */
46 {
47   int status;
48
49   if (tree == NULL)
50     return;
51
52   while (42)
53   {
54     char *key = NULL;
55     char *value = NULL;
56
57     status = c_avl_pick (tree, (void *) &key, (void *) &value);
58     if (status != 0)
59       break;
60
61     free (key);
62     free (value);
63   }
64
65   c_avl_destroy (tree);
66 } /* }}} void fbh_free_tree */
67
68 static int fbh_read_file (fbhash_t *h) /* {{{ */
69 {
70   FILE *fh;
71   char buffer[4096];
72   struct flock fl = { 0 };
73   c_avl_tree_t *tree;
74   int status;
75
76   fh = fopen (h->filename, "r");
77   if (fh == NULL)
78     return (-1);
79
80   fl.l_type = F_RDLCK;
81   fl.l_whence = SEEK_SET;
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 ((int (*) (const void *, const 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 = { 0 };
178   int status;
179
180   status = stat (h->filename, &statbuf);
181   if (status != 0)
182     return (-1);
183
184   if (h->mtime >= statbuf.st_mtime)
185     return (0);
186
187   status = fbh_read_file (h);
188   if (status == 0)
189     h->mtime = statbuf.st_mtime;
190
191   return (status);
192 } /* }}} int fbh_check_file */
193
194 /*
195  * Public functions
196  */
197 fbhash_t *fbh_create (const char *file) /* {{{ */
198 {
199   fbhash_t *h;
200   int status;
201
202   if (file == NULL)
203     return (NULL);
204
205   h = calloc (1, sizeof (*h));
206   if (h == NULL)
207     return (NULL);
208
209   h->filename = strdup (file);
210   if (h->filename == NULL)
211   {
212     free (h);
213     return (NULL);
214   }
215
216   h->mtime = 0;
217   pthread_mutex_init (&h->lock, /* attr = */ NULL);
218
219   status = fbh_check_file (h);
220   if (status != 0)
221   {
222     fbh_destroy (h);
223     free (h);
224     return (NULL);
225   }
226
227   return (h);
228 } /* }}} fbhash_t *fbh_create */
229
230 void fbh_destroy (fbhash_t *h) /* {{{ */
231 {
232   if (h == NULL)
233     return;
234
235   pthread_mutex_destroy (&h->lock);
236   free (h->filename);
237   fbh_free_tree (h->tree);
238 } /* }}} void fbh_destroy */
239
240 char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
241 {
242   char *value;
243   char *value_copy;
244   int status;
245
246   if ((h == NULL) || (key == NULL))
247     return (NULL);
248
249   value = NULL;
250   value_copy = NULL;
251
252   pthread_mutex_lock (&h->lock);
253
254   /* TODO: Checking this every time may be a bit much..? */
255   fbh_check_file (h);
256
257   status = c_avl_get (h->tree, key, (void *) &value);
258   if (status == 0)
259   {
260     assert (value != NULL);
261     value_copy = strdup (value);
262   }
263
264   pthread_mutex_unlock (&h->lock);
265
266   return (value_copy);
267 } /* }}} char *fbh_get */
268
269 /* vim: set sw=2 sts=2 et fdm=marker : */