Merge branch 'collectd-5.5'
[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;
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   memset (&fl, 0, sizeof (fl));
81   fl.l_type = F_RDLCK;
82   fl.l_whence = SEEK_SET;
83   fl.l_start = 0;
84   fl.l_len = 0; /* == entire file */
85   /* TODO: Lock file? -> fcntl */
86
87   status = fcntl (fileno (fh), F_SETLK, &fl);
88   if (status != 0)
89   {
90     fclose (fh);
91     return (-1);
92   }
93
94   tree = c_avl_create ((int (*) (const void *, const void *)) strcmp);
95   if (tree == NULL)
96   {
97     fclose (fh);
98     return (-1);
99   }
100
101   /* Read `fh' into `tree' */
102   while (fgets (buffer, sizeof (buffer), fh) != NULL) /* {{{ */
103   {
104     size_t len;
105     char *key;
106     char *value;
107
108     char *key_copy;
109     char *value_copy;
110
111     buffer[sizeof (buffer) - 1] = 0;
112     len = strlen (buffer);
113
114     /* Remove trailing newline characters. */
115     while ((len > 0)
116         && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
117     {
118       len--;
119       buffer[len] = 0;
120     }
121
122     /* Seek first non-space character */
123     key = buffer;
124     while ((*key != 0) && isspace ((int) *key))
125       key++;
126
127     /* Skip empty lines and comments */
128     if ((key[0] == 0) || (key[0] == '#'))
129       continue;
130
131     /* Seek first colon */
132     value = strchr (key, ':');
133     if (value == NULL)
134       continue;
135
136     /* Null-terminate `key'. */
137     *value = 0;
138     value++;
139
140     /* Skip leading whitespace */
141     while ((*value != 0) && isspace ((int) *value))
142       value++;
143
144     /* Skip lines without value */
145     if (value[0] == 0)
146       continue;
147
148     key_copy = strdup (key);
149     value_copy = strdup (value);
150
151     if ((key_copy == NULL) || (value_copy == NULL))
152     {
153       free (key_copy);
154       free (value_copy);
155       continue;
156     }
157
158     status = c_avl_insert (tree, key_copy, value_copy);
159     if (status != 0)
160     {
161       free (key_copy);
162       free (value_copy);
163       continue;
164     }
165
166     DEBUG ("utils_fbhash: fbh_read_file: key = %s; value = %s;",
167         key, value);
168   } /* }}} while (fgets) */
169
170   fclose (fh);
171
172   fbh_free_tree (h->tree);
173   h->tree = tree;
174
175   return (0);
176 } /* }}} int fbh_read_file */
177
178 static int fbh_check_file (fbhash_t *h) /* {{{ */
179 {
180   struct stat statbuf;
181   int status;
182
183   memset (&statbuf, 0, sizeof (statbuf));
184
185   status = stat (h->filename, &statbuf);
186   if (status != 0)
187     return (-1);
188
189   if (h->mtime >= statbuf.st_mtime)
190     return (0);
191
192   status = fbh_read_file (h);
193   if (status == 0)
194     h->mtime = statbuf.st_mtime;
195
196   return (status);
197 } /* }}} int fbh_check_file */
198
199 /*
200  * Public functions
201  */
202 fbhash_t *fbh_create (const char *file) /* {{{ */
203 {
204   fbhash_t *h;
205   int status;
206
207   if (file == NULL)
208     return (NULL);
209
210   h = calloc (1, sizeof (*h));
211   if (h == NULL)
212     return (NULL);
213
214   h->filename = strdup (file);
215   if (h->filename == NULL)
216   {
217     free (h);
218     return (NULL);
219   }
220
221   h->mtime = 0;
222   pthread_mutex_init (&h->lock, /* attr = */ NULL);
223
224   status = fbh_check_file (h);
225   if (status != 0)
226   {
227     fbh_destroy (h);
228     free (h);
229     return (NULL);
230   }
231
232   return (h);
233 } /* }}} fbhash_t *fbh_create */
234
235 void fbh_destroy (fbhash_t *h) /* {{{ */
236 {
237   if (h == NULL)
238     return;
239
240   pthread_mutex_destroy (&h->lock);
241   free (h->filename);
242   fbh_free_tree (h->tree);
243 } /* }}} void fbh_destroy */
244
245 char *fbh_get (fbhash_t *h, const char *key) /* {{{ */
246 {
247   char *value;
248   char *value_copy;
249   int status;
250
251   if ((h == NULL) || (key == NULL))
252     return (NULL);
253
254   value = NULL;
255   value_copy = NULL;
256
257   pthread_mutex_lock (&h->lock);
258
259   /* TODO: Checking this every time may be a bit much..? */
260   fbh_check_file (h);
261
262   status = c_avl_get (h->tree, key, (void *) &value);
263   if (status == 0)
264   {
265     assert (value != NULL);
266     value_copy = strdup (value);
267   }
268
269   pthread_mutex_unlock (&h->lock);
270
271   return (value_copy);
272 } /* }}} char *fbh_get */
273
274 /* vim: set sw=2 sts=2 et fdm=marker : */