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