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