9b1b83a163533bb3e3848ece22997914d6324f68
[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",     PIDFILE,          MODE_CLIENT | MODE_SERVER | MODE_LOCAL},
67         {"DataDir",     PKGLOCALSTATEDIR, MODE_SERVER |               MODE_LOCAL}
68 };
69 static int cf_mode_num = 4;
70
71 static int nesting_depth = 0;
72 static char *current_module = NULL;
73
74 /* `cf_register' needs this prototype */
75 int cf_callback_plugin_dispatch (const char *, const char *, const char *,
76                 const char *, lc_flags_t, void *);
77
78 /*
79  * Functions to handle register/unregister, search, and other plugin related
80  * stuff
81  */
82 cf_callback_t *cf_search (char *type)
83 {
84         cf_callback_t *cf_cb;
85
86         if (type == NULL)
87                 return (NULL);
88
89         for (cf_cb = first_callback; cf_cb != NULL; cf_cb = cf_cb->next)
90                 if (strcasecmp (cf_cb->type, type) == 0)
91                         break;
92
93         return (cf_cb);
94 }
95
96 int cf_dispatch (char *type, const char *orig_key, const char *orig_value)
97 {
98         cf_callback_t *cf_cb;
99         char *key;
100         char *value;
101         int ret;
102         int i;
103
104         DBG ("type = %s, key = %s, value = %s", type, orig_key, orig_value);
105
106         if ((cf_cb = cf_search (type)) == NULL)
107         {
108                 syslog (LOG_WARNING, "Plugin `%s' did not register a callback.\n", type);
109                 return (-1);
110         }
111
112         if ((key = strdup (orig_key)) == NULL)
113                 return (1);
114         if ((value = strdup (orig_value)) == NULL)
115         {
116                 free (key);
117                 return (2);
118         }
119
120         ret = -1;
121
122         for (i = 0; i < cf_cb->keys_num; i++)
123         {
124                 if (strcasecmp (cf_cb->keys[i], key) == 0)
125                 {
126                         ret = (*cf_cb->callback) (key, value);
127                         break;
128                 }
129         }
130
131         if (i >= cf_cb->keys_num)
132                 syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.\n", type, key);
133
134         free (key);
135         free (value);
136
137         return (ret);
138 }
139
140 void cf_unregister (char *type)
141 {
142         cf_callback_t *this, *prev;
143
144         for (prev = NULL, this = first_callback;
145                         this != NULL;
146                         prev = this, this = this->next)
147                 if (strcasecmp (this->type, type) == 0)
148                 {
149                         if (prev == NULL)
150                                 first_callback = this->next;
151                         else
152                                 prev->next = this->next;
153
154                         free (this);
155                         break;
156                 }
157 }
158
159 void cf_register (char *type,
160                 int (*callback) (char *, char *),
161                 char **keys, int keys_num)
162 {
163         cf_callback_t *cf_cb;
164         char buf[64];
165         int i;
166
167         /* Remove this module from the list, if it already exists */
168         cf_unregister (type);
169
170         /* This pointer will be free'd in `cf_unregister' */
171         if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
172                 return;
173
174         cf_cb->type     = type;
175         cf_cb->callback = callback;
176         cf_cb->keys     = keys;
177         cf_cb->keys_num = keys_num;
178
179         cf_cb->next = first_callback;
180         first_callback = cf_cb;
181
182         for (i = 0; i < keys_num; i++)
183         {
184                 if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64)
185                 {
186                         /* This may be called multiple times for the same
187                          * `key', but apparently `lc_register_*' can handle
188                          * it.. */
189                         lc_register_callback (buf, SHORTOPT_NONE,
190                                         LC_VAR_STRING, cf_callback_plugin_dispatch,
191                                         NULL);
192                 }
193                 else
194                 {
195                         DBG ("Key was truncated: `%s'", keys[i]);
196                 }
197         }
198 }
199
200 /*
201  * Other query functions
202  */
203 char *cf_get_mode_option (const char *key)
204 {
205         int i;
206
207         for (i = 0; i < cf_mode_num; i++)
208         {
209                 if ((cf_mode_list[i].mode & operating_mode) == 0)
210                         continue;
211
212                 if (strcasecmp (cf_mode_list[i].key, key) == 0)
213                         return (cf_mode_list[i].value);
214         }
215
216         return (NULL);
217 }
218
219 int cf_callback_usage (const char *shortvar, const char *var,
220                 const char *arguments, const char *value, lc_flags_t flags,
221                 void *extra)
222 {
223         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
224                         shortvar, var, arguments, value);
225
226         printf ("Usage: "PACKAGE" [OPTIONS]\n\n"
227                         
228                         "Available options:\n"
229 #if COLLECT_DAEMON
230                         "    -P <file>       PID file.\n"
231                         "                    Default: "PIDFILE"\n"
232 #endif
233                         "    -M <dir>        Module/Plugin directory.\n"
234                         "                    Default: "PLUGINDIR"\n"
235                         "    -D <dir>        Data storage directory.\n"
236                         "                    Default: "PKGLOCALSTATEDIR"\n"
237 #if COLLECT_DEBUG
238                         "    -L <file>       Log file.\n"
239                         "                    Default: "LOGFILE"\n"
240 #endif
241 #if COLLECT_DAEMON
242                         "    -f              Don't fork to the background.\n"
243 #endif
244 #if HAVE_LIBRRD
245                         "    -l              Start in local mode (no network).\n"
246                         "    -c              Start in client (sender) mode.\n"
247                         "    -s              Start in server (listener) mode.\n"
248 #endif /* HAVE_LIBRRD */
249 #if COLLECT_PING
250                         "  Ping:\n"
251                         "    -p <host>       Host to ping periodically, may be repeated to ping\n"
252                         "                    more than one host.\n"
253 #endif /* COLLECT_PING */
254                         "\n"PACKAGE" "VERSION", http://verplant.org/collectd/\n"
255                         "by Florian octo Forster <octo@verplant.org>\n"
256                         "for contributions see `AUTHORS'\n");
257         exit (0);
258 } /* exit_usage */
259
260 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
261  * Functions for the actual parsing                                    *
262  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
263
264 /*
265  * `cf_callback_mode'
266  *   Start/end the `mode' section
267  *
268  * <Mode `arguments'>
269  *   ...
270  * </Mode>
271  */
272 int cf_callback_mode (const char *shortvar, const char *var,
273                 const char *arguments, const char *value, lc_flags_t flags,
274                 void *extra)
275 {
276         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
277                         shortvar, var, arguments, value);
278
279         if (flags == LC_FLAGS_SECTIONSTART)
280         {
281                 if (nesting_depth != 0)
282                 {
283                         fprintf (stderr, ERR_NOT_NESTED);
284                         return (LC_CBRET_ERROR);
285                 }
286
287                 if (arguments == NULL)
288                 {
289                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
290                         return (LC_CBRET_ERROR);
291                 }
292
293                 nesting_depth++;
294
295                 if (((operating_mode == MODE_CLIENT)
296                                         && (strcasecmp (arguments, "Client") == 0))
297                                 || ((operating_mode == MODE_SERVER)
298                                         && (strcasecmp (arguments, "Server") == 0))
299                                 || ((operating_mode == MODE_LOCAL)
300                                         && (strcasecmp (arguments, "Local") == 0)))
301                 {
302                         return (LC_CBRET_OKAY);
303                 }
304                 else
305                 {
306                         return (LC_CBRET_IGNORESECTION);
307                 }
308         }
309         else if (flags == LC_FLAGS_SECTIONEND)
310         {
311                 nesting_depth--;
312
313                 return (LC_CBRET_OKAY);
314         }
315         else
316         {
317                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
318                 return (LC_CBRET_ERROR);
319         }
320
321 }
322
323 /*
324  * `cf_callback_mode_plugindir'
325  *   Change the plugin directory
326  *
327  * <Mode xxx>
328  *   PluginDir `value'
329  * </Mode>
330  */
331 int cf_callback_mode_plugindir (const char *shortvar, const char *var,
332                 const char *arguments, const char *value, lc_flags_t flags,
333                 void *extra)
334 {
335         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
336                         shortvar, var, arguments, value);
337
338         plugin_set_dir (value);
339
340         return (LC_CBRET_OKAY);
341 }
342
343 int cf_callback_mode_option (const char *shortvar, const char *var,
344                 const char *arguments, const char *value, lc_flags_t flags,
345                 void *extra)
346 {
347         cf_mode_item_t *item;
348
349         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
350                         shortvar, var, arguments, value);
351
352         if (extra == NULL)
353         {
354                 fprintf (stderr, "No extra..?\n");
355                 return (LC_CBRET_ERROR);
356         }
357
358         item = (cf_mode_item_t *) extra;
359
360         if (strcasecmp (item->key, shortvar))
361         {
362                 fprintf (stderr, "Wrong extra..\n");
363                 return (LC_CBRET_ERROR);
364         }
365
366         if ((operating_mode & item->mode) == 0)
367         {
368                 fprintf (stderr, "Option `%s' is not valid in this mode!\n", shortvar);
369                 return (LC_CBRET_ERROR);
370         }
371
372         if (item->value != NULL)
373         {
374                 free (item->value);
375                 item->value = NULL;
376         }
377
378         if ((item->value = strdup (value)) == NULL)
379         {
380                 perror ("strdup");
381                 return (LC_CBRET_ERROR);
382         }
383
384         return (LC_CBRET_OKAY);
385 }
386
387 /*
388  * `cf_callback_mode_loadmodule':
389  *   Load a plugin.
390  *
391  * <Mode xxx>
392  *   LoadPlugin `value'
393  * </Mode>
394  */
395 int cf_callback_mode_loadmodule (const char *shortvar, const char *var,
396                 const char *arguments, const char *value, lc_flags_t flags,
397                 void *extra)
398 {
399         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
400                         shortvar, var, arguments, value);
401
402         if (nesting_depth == 0)
403         {
404                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
405                 return (LC_CBRET_ERROR);
406         }
407
408         if (plugin_load (value))
409                 syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", value);
410
411         /* Return `okay' even if there was an error, because it's not a syntax
412          * problem.. */
413         return (LC_CBRET_OKAY);
414 }
415
416 /* XXX think about how to do the command line stuff */
417 int cf_callback_mode_switch (const char *shortvar, const char *var,
418                 const char *arguments, const char *value, lc_flags_t flags,
419                 void *extra)
420 {
421         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
422                         shortvar, var, arguments, value);
423
424         if (strcasecmp (shortvar, "Client") == 0)
425                 operating_mode = MODE_CLIENT;
426         else if (strcasecmp (shortvar, "Local") == 0)
427                 operating_mode = MODE_LOCAL;
428         else if (strcasecmp (shortvar, "Server") == 0)
429                 operating_mode = MODE_SERVER;
430         else
431         {
432                 fprintf (stderr, "cf_callback_mode_switch: Wrong mode!\n");
433                 return (LC_CBRET_ERROR);
434         }
435
436         return (LC_CBRET_OKAY);
437 }
438
439 /*
440  * `cf_callback_plugin'
441  *   Start/end section `plugin'
442  *
443  * <Plugin `arguments'>
444  *   ...
445  * </Plugin>
446  */
447 int cf_callback_plugin (const char *shortvar, const char *var,
448                 const char *arguments, const char *value, lc_flags_t flags,
449                 void *extra)
450 {
451         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
452                         shortvar, var, arguments, value);
453
454         if (flags == LC_FLAGS_SECTIONSTART)
455         {
456                 if (nesting_depth != 0)
457                 {
458                         fprintf (stderr, ERR_NOT_NESTED);
459                         return (LC_CBRET_ERROR);
460                 }
461
462                 if (arguments == NULL)
463                 {
464                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
465                         return (LC_CBRET_ERROR);
466                 }
467
468                 if ((current_module = strdup (arguments)) == NULL)
469                 {
470                         perror ("strdup");
471                         return (LC_CBRET_ERROR);
472                 }
473
474                 nesting_depth++;
475
476                 if (cf_search (current_module) != NULL)
477                         return (LC_CBRET_OKAY);
478                 else
479                         return (LC_CBRET_IGNORESECTION);
480         }
481         else if (flags == LC_FLAGS_SECTIONEND)
482         {
483                 if (current_module != NULL)
484                 {
485                         free (current_module);
486                         current_module = NULL;
487                 }
488
489                 nesting_depth--;
490
491                 return (LC_CBRET_OKAY);
492         }
493         else
494         {
495                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
496                 return (LC_CBRET_ERROR);
497         }
498 }
499
500 /*
501  * `cf_callback_plugin_dispatch'
502  *   Send options within `plugin' sections to the plugin that requests it.
503  *
504  * <Plugin `current_module'>
505  *   `var' `value'
506  * </Plugin>
507  */
508 int cf_callback_plugin_dispatch (const char *shortvar, const char *var,
509                 const char *arguments, const char *value, lc_flags_t flags,
510                 void *extra)
511 {
512         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
513                         shortvar, var, arguments, value);
514
515         if ((nesting_depth == 0) || (current_module == NULL))
516         {
517                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
518                 return (LC_CBRET_ERROR);
519         }
520
521         /* Send the data to the plugin */
522         if (cf_dispatch (current_module, shortvar, value) < 0)
523                 return (LC_CBRET_ERROR);
524
525         return (LC_CBRET_OKAY);
526 }
527
528 void cf_init (void)
529 {
530         static int run_once = 0;
531         int i;
532
533         if (run_once != 0)
534                 return;
535         run_once = 1;
536
537         lc_register_callback ("Help", 'h', LC_VAR_NONE,
538                         cf_callback_usage, NULL);
539
540         lc_register_callback ("Client", 'c', LC_VAR_NONE,
541                         cf_callback_mode_switch, NULL);
542         lc_register_callback ("Local", 'l', LC_VAR_NONE,
543                         cf_callback_mode_switch, NULL);
544         lc_register_callback ("Server", 's', LC_VAR_NONE,
545                         cf_callback_mode_switch, NULL);
546
547         lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_SECTION,
548                         cf_callback_mode, NULL);
549         lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION,
550                         cf_callback_plugin, NULL);
551
552         lc_register_callback ("Mode.PluginDir", 'P',
553                         LC_VAR_STRING, cf_callback_mode_plugindir, NULL);
554         lc_register_callback ("Mode.LoadPlugin", SHORTOPT_NONE,
555                         LC_VAR_STRING, cf_callback_mode_loadmodule, NULL);
556
557         for (i = 0; i < cf_mode_num; i++)
558         {
559                 char            longvar[256];
560                 cf_mode_item_t *item;
561
562                 item = &cf_mode_list[i];
563
564                 if (snprintf (longvar, 256, "Mode.%s", item->key) >= 256)
565                         continue;
566
567                 lc_register_callback (longvar, SHORTOPT_NONE, LC_VAR_STRING,
568                                 cf_callback_mode_option, (void *) item);
569         }
570 }
571
572 int cf_read (int argc, char **argv, char *filename)
573 {
574         cf_init ();
575
576         if (filename == NULL)
577                 filename = CONFIGFILE;
578
579         if (lc_process (argc, argv, "collectd", LC_CONF_APACHE, filename))
580         {
581                 syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ());
582                 return (-1);
583         }
584
585         /* free memory and stuff */
586         lc_cleanup ();
587
588         return (0);
589 }