Added the `MulticastTTL' option so users can set a TTL different to `1'.
[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 /* TODO
24  * make internal-only functions `static' */
25
26 #include "collectd.h"
27
28 #include "libconfig/libconfig.h"
29
30 #include "common.h"
31 #include "plugin.h"
32 #include "configfile.h"
33 #include "network.h"
34 #include "utils_debug.h"
35
36 #define SHORTOPT_NONE 0
37
38 #define ERR_NOT_NESTED "Sections cannot be nested.\n"
39 #define ERR_SECTION_ONLY "`%s' can only be used as section.\n"
40 #define ERR_NEEDS_ARG "Section `%s' needs an argument.\n"
41 #define ERR_NEEDS_SECTION "`%s' can only be used within a section.\n"
42
43 #ifdef HAVE_LIBRRD
44 extern int operating_mode;
45 #else
46 static int operating_mode = MODE_CLIENT;
47 #endif
48
49 typedef struct cf_callback
50 {
51         char  *type;
52         int  (*callback) (char *, char *);
53         char **keys;
54         int    keys_num;
55         struct cf_callback *next;
56 } cf_callback_t;
57
58 static cf_callback_t *first_callback = NULL;
59
60 typedef struct cf_mode_item
61 {
62         char *key;
63         char *value;
64         int   mode;
65 } cf_mode_item_t;
66
67 /* TODO
68  * - LogFile
69  */
70 static cf_mode_item_t cf_mode_list[] =
71 {
72         {"MulticastTTL",NULL, MODE_CLIENT                           },
73         {"PIDFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL},
74         {"DataDir",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL},
75         {"LogFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL}
76 };
77 static int cf_mode_num = 4;
78
79 static int nesting_depth = 0;
80 static char *current_module = NULL;
81
82 /* `cf_register' needs this prototype */
83 int cf_callback_plugin_dispatch (const char *, const char *, const char *,
84                 const char *, lc_flags_t, void *);
85
86 /*
87  * Functions to handle register/unregister, search, and other plugin related
88  * stuff
89  */
90 cf_callback_t *cf_search (char *type)
91 {
92         cf_callback_t *cf_cb;
93
94         if (type == NULL)
95                 return (NULL);
96
97         for (cf_cb = first_callback; cf_cb != NULL; cf_cb = cf_cb->next)
98                 if (strcasecmp (cf_cb->type, type) == 0)
99                         break;
100
101         return (cf_cb);
102 }
103
104 int cf_dispatch (char *type, const char *orig_key, const char *orig_value)
105 {
106         cf_callback_t *cf_cb;
107         char *key;
108         char *value;
109         int ret;
110         int i;
111
112         DBG ("type = %s, key = %s, value = %s", type, orig_key, orig_value);
113
114         if ((cf_cb = cf_search (type)) == NULL)
115         {
116                 syslog (LOG_WARNING, "Plugin `%s' did not register a callback.\n", type);
117                 return (-1);
118         }
119
120         if ((key = strdup (orig_key)) == NULL)
121                 return (1);
122         if ((value = strdup (orig_value)) == NULL)
123         {
124                 free (key);
125                 return (2);
126         }
127
128         ret = -1;
129
130         for (i = 0; i < cf_cb->keys_num; i++)
131         {
132                 if (strcasecmp (cf_cb->keys[i], key) == 0)
133                 {
134                         ret = (*cf_cb->callback) (key, value);
135                         break;
136                 }
137         }
138
139         if (i >= cf_cb->keys_num)
140                 syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.\n", type, key);
141
142         free (key);
143         free (value);
144
145         return (ret);
146 }
147
148 void cf_unregister (char *type)
149 {
150         cf_callback_t *this, *prev;
151
152         for (prev = NULL, this = first_callback;
153                         this != NULL;
154                         prev = this, this = this->next)
155                 if (strcasecmp (this->type, type) == 0)
156                 {
157                         if (prev == NULL)
158                                 first_callback = this->next;
159                         else
160                                 prev->next = this->next;
161
162                         free (this);
163                         break;
164                 }
165 }
166
167 void cf_register (char *type,
168                 int (*callback) (char *, char *),
169                 char **keys, int keys_num)
170 {
171         cf_callback_t *cf_cb;
172         char buf[64];
173         int i;
174
175         /* Remove this module from the list, if it already exists */
176         cf_unregister (type);
177
178         /* This pointer will be free'd in `cf_unregister' */
179         if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
180                 return;
181
182         cf_cb->type     = type;
183         cf_cb->callback = callback;
184         cf_cb->keys     = keys;
185         cf_cb->keys_num = keys_num;
186
187         cf_cb->next = first_callback;
188         first_callback = cf_cb;
189
190         for (i = 0; i < keys_num; i++)
191         {
192                 if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64)
193                 {
194                         /* This may be called multiple times for the same
195                          * `key', but apparently `lc_register_*' can handle
196                          * it.. */
197                         lc_register_callback (buf, SHORTOPT_NONE,
198                                         LC_VAR_STRING, cf_callback_plugin_dispatch,
199                                         NULL);
200                 }
201                 else
202                 {
203                         DBG ("Key was truncated: `%s'", keys[i]);
204                 }
205         }
206 }
207
208 /*
209  * Other query functions
210  */
211 char *cf_get_option (const char *key, char *def)
212 {
213         int i;
214
215         for (i = 0; i < cf_mode_num; i++)
216         {
217                 if ((cf_mode_list[i].mode & operating_mode) == 0)
218                         continue;
219
220                 if (strcasecmp (cf_mode_list[i].key, key) != 0)
221                         continue;
222
223                 if (cf_mode_list[i].value != NULL)
224                         return (cf_mode_list[i].value);
225                 return (def);
226         }
227
228         return (NULL);
229 }
230
231 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
232  * Functions for the actual parsing                                    *
233  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
234
235 /*
236  * `cf_callback_mode'
237  *   Chose the `operating_mode'
238  *
239  * Mode `value'
240  */
241 int cf_callback_mode (const char *shortvar, const char *var,
242                 const char *arguments, const char *value, lc_flags_t flags,
243                 void *extra)
244 {
245         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
246                         shortvar, var, arguments, value);
247
248         if (strcasecmp (value, "Client") == 0)
249                 operating_mode = MODE_CLIENT;
250         else if (strcasecmp (value, "Server") == 0)
251                 operating_mode = MODE_SERVER;
252         else if (strcasecmp (value, "Local") == 0)
253                 operating_mode = MODE_LOCAL;
254         else
255         {
256                 syslog (LOG_ERR, "Invalid value for config option `Mode': `%s'", value);
257                 return (LC_CBRET_ERROR);
258         }
259
260         return (LC_CBRET_OKAY);
261 }
262
263 /*
264  * `cf_callback_mode_plugindir'
265  *   Change the plugin directory
266  *
267  * <Mode xxx>
268  *   PluginDir `value'
269  * </Mode>
270  */
271 int cf_callback_mode_plugindir (const char *shortvar, const char *var,
272                 const char *arguments, const char *value, lc_flags_t flags,
273                 void *extra)
274 {
275         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
276                         shortvar, var, arguments, value);
277
278         plugin_set_dir (value);
279
280         return (LC_CBRET_OKAY);
281 }
282
283 int cf_callback_mode_option (const char *shortvar, const char *var,
284                 const char *arguments, const char *value, lc_flags_t flags,
285                 void *extra)
286 {
287         cf_mode_item_t *item;
288
289         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
290                         shortvar, var, arguments, value);
291
292         if (extra == NULL)
293         {
294                 fprintf (stderr, "No extra..?\n");
295                 return (LC_CBRET_ERROR);
296         }
297
298         item = (cf_mode_item_t *) extra;
299
300         if (strcasecmp (item->key, shortvar))
301         {
302                 fprintf (stderr, "Wrong extra..\n");
303                 return (LC_CBRET_ERROR);
304         }
305
306         if ((operating_mode & item->mode) == 0)
307         {
308                 fprintf (stderr, "Option `%s' is not valid in this mode!\n", shortvar);
309                 return (LC_CBRET_ERROR);
310         }
311
312         if (item->value != NULL)
313         {
314                 free (item->value);
315                 item->value = NULL;
316         }
317
318         if ((item->value = strdup (value)) == NULL)
319         {
320                 perror ("strdup");
321                 return (LC_CBRET_ERROR);
322         }
323
324         return (LC_CBRET_OKAY);
325 }
326
327 /*
328  * `cf_callback_mode_loadmodule':
329  *   Load a plugin.
330  *
331  * <Mode xxx>
332  *   LoadPlugin `value'
333  * </Mode>
334  */
335 int cf_callback_mode_loadmodule (const char *shortvar, const char *var,
336                 const char *arguments, const char *value, lc_flags_t flags,
337                 void *extra)
338 {
339         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
340                         shortvar, var, arguments, value);
341
342         if (plugin_load (value))
343                 syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", value);
344
345         /* Return `okay' even if there was an error, because it's not a syntax
346          * problem.. */
347         return (LC_CBRET_OKAY);
348 }
349
350 /*
351  * `cf_callback_mode_switch'
352  *   Change the contents of the global variable `operating_mode'
353  *
354  *   This should be command line options. One *can* do this in the config
355  *   files, but I will not document this. Don't whine abount it not working as
356  *   you expect if you do it anyways.
357  */
358 int cf_callback_mode_switch (const char *shortvar, const char *var,
359                 const char *arguments, const char *value, lc_flags_t flags,
360                 void *extra)
361 {
362         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
363                         shortvar, var, arguments, value);
364
365         if (strcasecmp (shortvar, "Client") == 0)
366                 operating_mode = MODE_CLIENT;
367         else if (strcasecmp (shortvar, "Local") == 0)
368                 operating_mode = MODE_LOCAL;
369         else if (strcasecmp (shortvar, "Server") == 0)
370                 operating_mode = MODE_SERVER;
371         else
372         {
373                 fprintf (stderr, "cf_callback_mode_switch: Wrong mode!\n");
374                 return (LC_CBRET_ERROR);
375         }
376
377         return (LC_CBRET_OKAY);
378 }
379
380 int cf_callback_socket (const char *shortvar, const char *var,
381                 const char *arguments, const char *value, lc_flags_t flags,
382                 void *extra)
383 {
384         char *buffer;
385
386         char *fields[3];
387         int   numfields;
388
389         char *node;
390         char *service = NET_DEFAULT_PORT;
391
392         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
393                         shortvar, var, arguments, value);
394
395         buffer = strdup (value);
396         if (buffer == NULL)
397                 return (LC_CBRET_ERROR);
398
399         numfields = strsplit (buffer, fields, 3);
400
401         if ((numfields != 1) && (numfields != 2))
402         {
403                 syslog (LOG_ERR, "Invalid number of arguments to `%s'",
404                                 shortvar);
405                 free (buffer);
406                 return (LC_CBRET_ERROR);
407         }
408
409         node = fields[0];
410         if (numfields == 2)
411                 service = fields[1];
412
413         /* Still return `LC_CBRET_OKAY' because this is not an syntax error */
414         if (network_create_socket (node, service) < 1)
415                 syslog (LOG_ERR, "network_create_socket (%s, %s) failed",
416                                 node, service);
417
418         free (buffer);
419
420         return (LC_CBRET_OKAY);
421 }
422
423 /*
424  * `cf_callback_plugin'
425  *   Start/end section `plugin'
426  *
427  * <Plugin `arguments'>
428  *   ...
429  * </Plugin>
430  */
431 int cf_callback_plugin (const char *shortvar, const char *var,
432                 const char *arguments, const char *value, lc_flags_t flags,
433                 void *extra)
434 {
435         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
436                         shortvar, var, arguments, value);
437
438         if (flags == LC_FLAGS_SECTIONSTART)
439         {
440                 if (nesting_depth != 0)
441                 {
442                         fprintf (stderr, ERR_NOT_NESTED);
443                         return (LC_CBRET_ERROR);
444                 }
445
446                 if (arguments == NULL)
447                 {
448                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
449                         return (LC_CBRET_ERROR);
450                 }
451
452                 if ((current_module = strdup (arguments)) == NULL)
453                 {
454                         perror ("strdup");
455                         return (LC_CBRET_ERROR);
456                 }
457
458                 nesting_depth++;
459
460                 if (cf_search (current_module) != NULL)
461                         return (LC_CBRET_OKAY);
462                 else
463                         return (LC_CBRET_IGNORESECTION);
464         }
465         else if (flags == LC_FLAGS_SECTIONEND)
466         {
467                 if (current_module != NULL)
468                 {
469                         free (current_module);
470                         current_module = NULL;
471                 }
472
473                 nesting_depth--;
474
475                 return (LC_CBRET_OKAY);
476         }
477         else
478         {
479                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
480                 return (LC_CBRET_ERROR);
481         }
482 }
483
484 /*
485  * `cf_callback_plugin_dispatch'
486  *   Send options within `plugin' sections to the plugin that requests it.
487  *
488  * <Plugin `current_module'>
489  *   `var' `value'
490  * </Plugin>
491  */
492 int cf_callback_plugin_dispatch (const char *shortvar, const char *var,
493                 const char *arguments, const char *value, lc_flags_t flags,
494                 void *extra)
495 {
496         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
497                         shortvar, var, arguments, value);
498
499         if ((nesting_depth == 0) || (current_module == NULL))
500         {
501                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
502                 return (LC_CBRET_ERROR);
503         }
504
505         /* Send the data to the plugin */
506         if (cf_dispatch (current_module, shortvar, value) < 0)
507                 return (LC_CBRET_ERROR);
508
509         return (LC_CBRET_OKAY);
510 }
511
512 void cf_init (void)
513 {
514         static int run_once = 0;
515         int i;
516
517         if (run_once != 0)
518                 return;
519         run_once = 1;
520
521         lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_STRING,
522                         cf_callback_mode, NULL);
523         lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION,
524                         cf_callback_plugin, NULL);
525
526         lc_register_callback ("PluginDir", SHORTOPT_NONE,
527                         LC_VAR_STRING, cf_callback_mode_plugindir, NULL);
528         lc_register_callback ("LoadPlugin", SHORTOPT_NONE,
529                         LC_VAR_STRING, cf_callback_mode_loadmodule, NULL);
530
531         lc_register_callback ("Listen", SHORTOPT_NONE,
532                         LC_VAR_STRING, cf_callback_socket, NULL);
533         lc_register_callback ("Server", SHORTOPT_NONE,
534                         LC_VAR_STRING, cf_callback_socket, NULL);
535
536         for (i = 0; i < cf_mode_num; i++)
537         {
538                 cf_mode_item_t *item;
539
540                 item = &cf_mode_list[i];
541
542                 lc_register_callback (item->key, SHORTOPT_NONE, LC_VAR_STRING,
543                                 cf_callback_mode_option, (void *) item);
544         }
545 }
546
547 int cf_read (char *filename)
548 {
549         cf_init ();
550
551         if (filename == NULL)
552                 filename = CONFIGFILE;
553
554         DBG ("Starting to parse file `%s'", filename);
555
556         /* int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type); */
557         if (lc_process_file ("collectd", filename, LC_CONF_APACHE))
558         {
559                 syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ());
560                 return (-1);
561         }
562
563         DBG ("Done parsing file `%s'", filename);
564
565         /* free memory and stuff */
566         lc_cleanup ();
567
568         return (0);
569 }