Added to src/configfile.c:
[collectd.git] / src / configfile.c
1 /**
2  * collectd - src/configfile.c
3  * Copyright (C) 2005  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24
25 #include "libconfig/libconfig.h"
26
27 #include "plugin.h"
28 #include "configfile.h"
29 #include "utils_debug.h"
30
31 #define SHORTOPT_NONE 0
32
33 #define ERR_NOT_NESTED "Sections cannot be nested.\n"
34 #define ERR_SECTION_ONLY "`%s' can only be used as section.\n"
35 #define ERR_NEEDS_ARG "Section `%s' needs an argument.\n"
36 #define ERR_NEEDS_SECTION "`%s' can only be used within a section.\n"
37
38 #ifdef HAVE_LIBRRD
39 extern int operating_mode;
40 #else
41 static int operating_mode = MODE_CLIENT;
42 #endif
43
44 typedef struct cf_callback
45 {
46         char  *type;
47         int  (*callback) (char *, char *);
48         char **keys;
49         int    keys_num;
50         struct cf_callback *next;
51 } cf_callback_t;
52
53 static cf_callback_t *first_callback = NULL;
54
55 typedef struct cf_mode_item
56 {
57         char *key;
58         char *value;
59         int   mode;
60 } cf_mode_item_t;
61
62 static cf_mode_item_t cf_mode_list[] =
63 {
64         {"Server",      NULL, MODE_CLIENT                           },
65         {"Port",        NULL, MODE_CLIENT | MODE_SERVER             },
66         {"PIDFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL},
67         {"DataDir",     NULL, MODE_SERVER |               MODE_LOCAL},
68         {"PluginDir",   NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL}
69 };
70 static int cf_mode_num = 5;
71
72 static int nesting_depth = 0;
73 static char *current_module = NULL;
74
75 /* cf_register needs this prototype */
76 int cf_callback_dispatch (const char *, const char *, const char *,
77                 const char *, lc_flags_t, void *);
78
79 /*
80  * Functions to handle register/unregister, search, and other plugin related
81  * stuff
82  */
83 cf_callback_t *cf_search (char *type)
84 {
85         cf_callback_t *cf_cb;
86
87         if (type == NULL)
88                 return (NULL);
89
90         for (cf_cb = first_callback; cf_cb != NULL; cf_cb = cf_cb->next)
91                 if (strcasecmp (cf_cb->type, type) == 0)
92                         break;
93
94         return (cf_cb);
95 }
96
97 int cf_dispatch (char *type, const char *orig_key, const char *orig_value)
98 {
99         cf_callback_t *cf_cb;
100         char *key;
101         char *value;
102         int ret;
103         int i;
104
105         if ((cf_cb = cf_search (type)) == NULL)
106         {
107                 syslog (LOG_WARNING, "Plugin `%s' did not register a callback.\n", type);
108                 return (-1);
109         }
110
111         if ((key = strdup (orig_key)) == NULL)
112                 return (1);
113         if ((value = strdup (orig_value)) == NULL)
114         {
115                 free (key);
116                 return (2);
117         }
118
119         ret = -1;
120
121         for (i = 0; i < cf_cb->keys_num; i++)
122         {
123                 if (strcasecmp (cf_cb->keys[i], key) == 0)
124                 {
125                         ret = (*cf_cb->callback) (key, value);
126                         break;
127                 }
128         }
129
130         if (i >= cf_cb->keys_num)
131                 syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.\n", type, key);
132
133         free (key);
134         free (value);
135
136         return (ret);
137 }
138
139 void cf_unregister (char *type)
140 {
141         cf_callback_t *this, *prev;
142
143         for (prev = NULL, this = first_callback;
144                         this != NULL;
145                         prev = this, this = this->next)
146                 if (strcasecmp (this->type, type) == 0)
147                 {
148                         if (prev == NULL)
149                                 first_callback = this->next;
150                         else
151                                 prev->next = this->next;
152
153                         free (this);
154                         break;
155                 }
156 }
157
158 void cf_register (char *type,
159                 int (*callback) (char *, char *),
160                 char **keys, int keys_num)
161 {
162         cf_callback_t *cf_cb;
163         char buf[64];
164         int i;
165
166         /* Remove this module from the list, if it already exists */
167         cf_unregister (type);
168
169         /* This pointer will be free'd in `cf_unregister' */
170         if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
171                 return;
172
173         cf_cb->type     = type;
174         cf_cb->callback = callback;
175         cf_cb->keys     = keys;
176         cf_cb->keys_num = keys_num;
177
178         cf_cb->next = first_callback;
179         first_callback = cf_cb;
180
181         for (i = 0; i < keys_num; i++)
182         {
183                 if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64)
184                 {
185                         /* This may be called multiple times for the same
186                          * `key', but apparently `lc_register_*' can handle
187                          * it.. */
188                         lc_register_callback (buf, SHORTOPT_NONE,
189                                         LC_VAR_STRING, cf_callback_dispatch,
190                                         NULL);
191                 }
192                 else
193                 {
194                         DBG ("Key was truncated: `%s'", keys[i]);
195                 }
196         }
197 }
198
199 /*
200  * Other query functions
201  */
202 char *cf_get_mode_option (const char *key)
203 {
204         int i;
205
206         for (i = 0; i < cf_mode_num; i++)
207         {
208                 if ((cf_mode_list[i].mode & operating_mode) == 0)
209                         continue;
210
211                 if (strcasecmp (cf_mode_list[i].key, key) == 0)
212                         return (cf_mode_list[i].value);
213         }
214
215         return (NULL);
216 }
217
218 /* 
219  * Functions for the actual parsing
220  */
221 int cf_callback_dispatch (const char *shortvar, const char *var,
222                 const char *arguments, const char *value, lc_flags_t flags,
223                 void *extra)
224 {
225         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
226                         shortvar, var, arguments, value);
227
228         if ((nesting_depth == 0) || (current_module == NULL))
229         {
230                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
231                 return (LC_CBRET_ERROR);
232         }
233
234         /* Send the data to the plugin */
235         if (cf_dispatch (current_module, shortvar, value) < 0)
236                 return (LC_CBRET_ERROR);
237
238         return (LC_CBRET_OKAY);
239 }
240
241 int cf_callback_options_mode (const char *shortvar, const char *var,
242                 const char *arguments, const char *value, lc_flags_t flags,
243                 void *extra)
244 {
245         cf_mode_item_t *item;
246
247         if (extra == NULL)
248         {
249                 fprintf (stderr, "No extra..?\n");
250                 return (LC_CBRET_ERROR);
251         }
252
253         item = (cf_mode_item_t *) extra;
254
255         if (strcasecmp (item->key, shortvar))
256         {
257                 fprintf (stderr, "Wrong extra..\n");
258                 return (LC_CBRET_ERROR);
259         }
260
261         if ((operating_mode & item->mode) == 0)
262         {
263                 fprintf (stderr, "Option `%s' is not valid in this mode!\n", shortvar);
264                 return (LC_CBRET_ERROR);
265         }
266
267         if (item->value != NULL)
268         {
269                 free (item->value);
270                 item->value = NULL;
271         }
272
273         if ((item->value = strdup (value)) == NULL)
274         {
275                 perror ("strdup");
276                 return (LC_CBRET_ERROR);
277         }
278
279         return (LC_CBRET_OKAY);
280 }
281
282 int cf_callback_section_mode (const char *shortvar, const char *var,
283                 const char *arguments, const char *value, lc_flags_t flags,
284                 void *extra)
285 {
286         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
287                         shortvar, var, arguments, value);
288
289         if (flags == LC_FLAGS_SECTIONSTART)
290         {
291                 if (nesting_depth != 0)
292                 {
293                         fprintf (stderr, ERR_NOT_NESTED);
294                         return (LC_CBRET_ERROR);
295                 }
296
297                 if (arguments == NULL)
298                 {
299                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
300                         return (LC_CBRET_ERROR);
301                 }
302
303                 nesting_depth++;
304
305                 if (((operating_mode == MODE_CLIENT)
306                                         && (strcasecmp (arguments, "Client") == 0))
307                                 || ((operating_mode == MODE_SERVER)
308                                         && (strcasecmp (arguments, "Server") == 0))
309                                 || ((operating_mode == MODE_LOCAL)
310                                         && (strcasecmp (arguments, "Local") == 0)))
311                 {
312                         return (LC_CBRET_OKAY);
313                 }
314                 else
315                 {
316                         return (LC_CBRET_IGNORESECTION);
317                 }
318         }
319         else if (flags == LC_FLAGS_SECTIONEND)
320         {
321                 nesting_depth--;
322
323                 return (LC_CBRET_OKAY);
324         }
325         else
326         {
327                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
328                 return (LC_CBRET_ERROR);
329         }
330
331 }
332
333 int cf_callback_section_module (const char *shortvar, const char *var,
334                 const char *arguments, const char *value, lc_flags_t flags,
335                 void *extra)
336 {
337         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
338                         shortvar, var, arguments, value);
339
340         if (flags == LC_FLAGS_SECTIONSTART)
341         {
342                 if (nesting_depth != 0)
343                 {
344                         fprintf (stderr, ERR_NOT_NESTED);
345                         return (LC_CBRET_ERROR);
346                 }
347
348                 if (arguments == NULL)
349                 {
350                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
351                         return (LC_CBRET_ERROR);
352                 }
353
354                 if ((current_module = strdup (arguments)) == NULL)
355                 {
356                         perror ("strdup");
357                         return (LC_CBRET_ERROR);
358                 }
359
360                 nesting_depth++;
361
362                 if (cf_search (current_module) != NULL)
363                         return (LC_CBRET_OKAY);
364                 else
365                         return (LC_CBRET_IGNORESECTION);
366         }
367         else if (flags == LC_FLAGS_SECTIONEND)
368         {
369                 if (current_module != NULL)
370                 {
371                         free (current_module);
372                         current_module = NULL;
373                 }
374
375                 nesting_depth--;
376
377                 return (LC_CBRET_OKAY);
378         }
379         else
380         {
381                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
382                 return (LC_CBRET_ERROR);
383         }
384 }
385
386 int cf_callback_loadmodule (const char *shortvar, const char *var,
387                 const char *arguments, const char *value, lc_flags_t flags,
388                 void *extra)
389 {
390         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
391                         shortvar, var, arguments, value);
392
393         if (nesting_depth == 0)
394         {
395                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
396                 return (LC_CBRET_ERROR);
397         }
398
399         if (plugin_load (value))
400                 syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", shortvar);
401
402         /* Return `okay' even if there was an error, because it's not a syntax
403          * problem.. */
404         return (LC_CBRET_OKAY);
405 }
406
407 int cf_read (char *filename)
408 {
409         int i;
410
411         if (filename == NULL)
412                 filename = CONFIGFILE;
413
414         lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_SECTION,
415                         cf_callback_section_mode, NULL);
416         lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION,
417                         cf_callback_section_module, NULL);
418
419         lc_register_callback ("Mode.LoadPlugin", SHORTOPT_NONE,
420                         LC_VAR_STRING, cf_callback_loadmodule,
421                         NULL);
422
423         for (i = 0; i < cf_mode_num; i++)
424         {
425                 char            longvar[256];
426                 cf_mode_item_t *item;
427
428                 item = &cf_mode_list[i];
429
430                 if (snprintf (longvar, 256, "Mode.%s", item->key) >= 256)
431                         continue;
432
433                 lc_register_callback (longvar, SHORTOPT_NONE, LC_VAR_STRING,
434                                 cf_callback_options_mode, (void *) item);
435         }
436
437         if (lc_process_file ("collectd", filename, LC_CONF_APACHE))
438         {
439                 syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ());
440                 return (-1);
441         }
442
443         /* free memory and stuff */
444         lc_cleanup ();
445
446         return (0);
447 }