Added `configfile.[ch]' to configfile-branch (forgot that yesterday)
[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 /*
24  * FIXME:
25  * - remove all (I mean *ALL*) calls to `fprintf': `stderr' will have been
26  *   closed.
27  */
28
29 #include "collectd.h"
30
31 #include "libconfig/libconfig.h"
32
33 #include "configfile.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 typedef struct cf_callback
44 {
45         char  *type;
46         int  (*callback) (char *, char *);
47         char **keys;
48         int    keys_num;
49         struct cf_callback *next;
50 } cf_callback_t;
51
52 static cf_callback_t *first_callback = NULL;
53
54 static int nesting_depth = 0;
55 static char *current_module = NULL;
56
57 /* cf_register needs this prototype */
58 int cf_callback_general (const char *, const char *, const char *,
59                 const char *, lc_flags_t, void *);
60
61 /*
62  * Functions to handle register/unregister, search, ...
63  */
64 cf_callback_t *cf_search (char *type)
65 {
66         cf_callback_t *cf_cb;
67
68         if (type == NULL)
69                 return (NULL);
70
71         for (cf_cb = first_callback; cf_cb != NULL; cf_cb = cf_cb->next)
72                 if (strcasecmp (cf_cb->type, type) == 0)
73                         break;
74
75         return (cf_cb);
76 }
77
78 int cf_dispatch (char *type, const char *orig_key, const char *orig_value)
79 {
80         cf_callback_t *cf_cb;
81         char *key;
82         char *value;
83         int ret;
84         int i;
85
86         if ((cf_cb = cf_search (type)) == NULL)
87         {
88                 fprintf (stderr, "Plugin `%s' did not register a callback.\n", type);
89                 return (-1);
90         }
91
92         if ((key = strdup (orig_key)) == NULL)
93                 return (1);
94         if ((value = strdup (orig_value)) == NULL)
95         {
96                 free (key);
97                 return (2);
98         }
99
100         ret = -1;
101
102         for (i = 0; i < cf_cb->keys_num; i++)
103         {
104                 if (strcasecmp (cf_cb->keys[i], key) == 0)
105                         ret = (*cf_cb->callback) (key, value);
106         }
107
108         if (i >= cf_cb->keys_num)
109                 fprintf (stderr, "Plugin `%s' did not register for value `%s'.\n", type, key);
110
111         free (key);
112         free (value);
113
114         return (ret);
115 }
116
117 void cf_unregister (char *type)
118 {
119         cf_callback_t *this, *prev;
120
121         for (prev = NULL, this = first_callback;
122                         this != NULL;
123                         prev = this, this = this->next)
124                 if (strcasecmp (this->type, type) == 0)
125                 {
126                         if (prev == NULL)
127                                 first_callback = this->next;
128                         else
129                                 prev->next = this->next;
130
131                         free (this);
132                         break;
133                 }
134 }
135
136 void cf_register (char *type,
137                 int (*callback) (char *, char *),
138                 char **keys, int keys_num)
139 {
140         cf_callback_t *cf_cb;
141         char buf[64];
142         int i;
143
144         /* Remove this module from the list, if it already exists */
145         cf_unregister (type);
146
147         /* This pointer will be free'd in `cf_unregister' */
148         if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
149                 return;
150
151         cf_cb->type     = type;
152         cf_cb->callback = callback;
153         cf_cb->keys     = keys;
154         cf_cb->keys_num = keys_num;
155
156         cf_cb->next = first_callback;
157         first_callback = cf_cb;
158
159         for (i = 0; i < keys_num; i++)
160         {
161                 if (snprintf (buf, 64, "Module.%s", keys[i]) < 64)
162                 {
163                         /* This may be called multiple times for the same
164                          * `key', but apparently `lc_register_*' can handle
165                          * it.. */
166                         lc_register_callback (buf, SHORTOPT_NONE,
167                                         LC_VAR_STRING, cf_callback_general,
168                                         NULL);
169                 }
170                 else
171                 {
172                         DBG ("Key was truncated: `%s'", keys[i]);
173                 }
174         }
175 }
176
177 /* 
178  * Functions for the actual parsing
179  */
180 int cf_callback_general (const char *shortvar, const char *var,
181                 const char *arguments, const char *value, lc_flags_t flags,
182                 void *extra)
183 {
184         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
185                         shortvar, var, arguments, value);
186
187         if ((nesting_depth == 0) || (current_module == NULL))
188         {
189                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
190                 return (LC_CBRET_ERROR);
191         }
192
193         /* Send the data to the plugin */
194         if (cf_dispatch (current_module, shortvar, value) < 0)
195                 return (LC_CBRET_ERROR);
196
197         return (LC_CBRET_OKAY);
198 }
199
200 int cf_callback_section_mode (const char *shortvar, const char *var,
201                 const char *arguments, const char *value, lc_flags_t flags,
202                 void *extra)
203 {
204         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
205                         shortvar, var, arguments, value);
206
207         if (flags == LC_FLAGS_SECTIONSTART)
208         {
209                 if (nesting_depth != 0)
210                 {
211                         fprintf (stderr, ERR_NOT_NESTED);
212                         return (LC_CBRET_ERROR);
213                 }
214
215                 if (arguments == NULL)
216                 {
217                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
218                         return (LC_CBRET_ERROR);
219                 }
220
221                 nesting_depth++;
222
223                 if (((operating_mode == MODE_CLIENT)
224                                         && (strcasecmp (arguments, "Client") == 0))
225                                 || ((operating_mode == MODE_SERVER)
226                                         && (strcasecmp (arguments, "Server") == 0))
227                                 || ((operating_mode == MODE_LOCAL)
228                                         && (strcasecmp (arguments, "Local") == 0)))
229                 {
230                         return (LC_CBRET_OKAY);
231                 }
232                 else
233                 {
234                         return (LC_CBRET_IGNORESECTION);
235                 }
236         }
237         else if (flags == LC_FLAGS_SECTIONEND)
238         {
239                 nesting_depth--;
240
241                 return (LC_CBRET_OKAY);
242         }
243         else
244         {
245                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
246                 return (LC_CBRET_ERROR);
247         }
248
249 }
250
251 int cf_callback_section_module (const char *shortvar, const char *var,
252                 const char *arguments, const char *value, lc_flags_t flags,
253                 void *extra)
254 {
255         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
256                         shortvar, var, arguments, value);
257
258         if (flags == LC_FLAGS_SECTIONSTART)
259         {
260                 if (nesting_depth != 0)
261                 {
262                         fprintf (stderr, ERR_NOT_NESTED);
263                         return (LC_CBRET_ERROR);
264                 }
265
266                 if (arguments == NULL)
267                 {
268                         fprintf (stderr, ERR_NEEDS_ARG, shortvar);
269                         return (LC_CBRET_ERROR);
270                 }
271
272                 if ((current_module = strdup (arguments)) == NULL)
273                 {
274                         perror ("strdup");
275                         return (LC_CBRET_ERROR);
276                 }
277
278                 nesting_depth++;
279
280                 if (cf_search (current_module) != NULL)
281                         return (LC_CBRET_OKAY);
282                 else
283                         return (LC_CBRET_IGNORESECTION);
284         }
285         else if (flags == LC_FLAGS_SECTIONEND)
286         {
287                 if (current_module != NULL)
288                 {
289                         free (current_module);
290                         current_module == NULL;
291                 }
292
293                 nesting_depth--;
294
295                 return (LC_CBRET_OKAY);
296         }
297         else
298         {
299                 fprintf (stderr, ERR_SECTION_ONLY, shortvar);
300                 return (LC_CBRET_ERROR);
301         }
302 }
303
304 int cf_callback_loadmodule (const char *shortvar, const char *var,
305                 const char *arguments, const char *value, lc_flags_t flags,
306                 void *extra)
307 {
308         DBG ("shortvar = %s, var = %s, arguments = %s, value = %s, ...",
309                         shortvar, var, arguments, value);
310
311         if (nesting_depth == 0)
312         {
313                 fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
314                 return (LC_CBRET_ERROR);
315         }
316
317         /*
318          * TODO:
319          * - Write wrapper around `plugin_load' to resolve path/filename
320          * - Call this new, public function here
321          */
322         DBG ("Implement me, idiot!");
323
324         return (LC_CBRET_OKAY);
325 }
326
327 int cf_read (char *filename)
328 {
329         if (filename == NULL)
330                 filename = CONFIGFILE;
331
332         lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_SECTION,
333                         cf_callback_section_mode, NULL);
334         lc_register_callback ("Module", SHORTOPT_NONE, LC_VAR_SECTION,
335                         cf_callback_section_module, NULL);
336
337         /*
338          * TODO:
339          * - Add more directives, such as `DefaultMode', `DataDir', `PIDFile', ...
340          */
341
342         lc_register_callback ("Mode.LoadModule", SHORTOPT_NONE,
343                         LC_VAR_STRING, cf_callback_loadmodule,
344                         NULL);
345
346         if (lc_process_file ("collectd", filename, LC_CONF_APACHE))
347         {
348                 /* FIXME: Use syslog here */
349                 fprintf (stderr, "Error loading config file `%s': %s\n",
350                                 filename, lc_geterrstr ());
351                 return (-1);
352         }
353
354         /* free memory and stuff */
355         lc_cleanup ();
356
357         return (0);
358 }