Code

parser: Let the TIMESERIES command accept optional data-source names.
[sysdb.git] / src / tools / sysdbd / configfile.c
1 /*
2  * SysDB - src/tools/sysdbd/configfile.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "tools/sysdbd/configfile.h"
34 #include "sysdb.h"
35 #include "core/plugin.h"
36 #include "core/time.h"
37 #include "utils/error.h"
39 #include "liboconfig/oconfig.h"
40 #include "liboconfig/utils.h"
42 #include <assert.h>
43 #include <errno.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <strings.h>
49 /*
50  * Parser error values:
51  *  - Values less than zero indicate an error in the daemon or libsysdb.
52  *  - Zero indicates success.
53  *  - Any other values indicate parsing errors.
54  */
56 enum {
57         ERR_UNKNOWN_OPTION = 1,
58         ERR_UNKNOWN_ARG    = 2,
59         ERR_INVALID_ARG    = 3,
60         ERR_PARSE_FAILED   = 4,
61 };
63 /*
64  * private variables
65  */
67 static sdb_time_t default_interval = 0;
68 static char *plugin_dir = NULL;
70 /*
71  * private helper functions
72  */
74 static int
75 config_get_interval(oconfig_item_t *ci, sdb_time_t *interval)
76 {
77         double interval_dbl = 0.0;
79         assert(ci && interval);
81         if (oconfig_get_number(ci, &interval_dbl)) {
82                 sdb_log(SDB_LOG_ERR, "config: Interval requires "
83                                 "a single numeric argument\n"
84                                 "\tUsage: Interval SECONDS");
85                 return ERR_INVALID_ARG;
86         }
88         if (interval_dbl <= 0.0) {
89                 sdb_log(SDB_LOG_ERR, "config: Invalid interval: %f\n"
90                                 "\tInterval may not be less than or equal to zero.",
91                                 interval_dbl);
92                 return ERR_INVALID_ARG;
93         }
95         *interval = DOUBLE_TO_SDB_TIME(interval_dbl);
96         return 0;
97 } /* config_get_interval */
99 /*
100  * public parse results
101  */
103 daemon_listener_t *listen_addresses = NULL;
104 size_t listen_addresses_num = 0;
106 /*
107  * token parser
108  */
110 typedef struct {
111         char *name;
112         int (*dispatcher)(oconfig_item_t *);
113 } token_parser_t;
115 static int
116 daemon_add_listener(oconfig_item_t *ci)
118         daemon_listener_t *listener;
119         char *address;
120         int i, ret = 0;
122         if (oconfig_get_string(ci, &address)) {
123                 sdb_log(SDB_LOG_ERR, "config: Listen requires a single "
124                                 "string argument\n"
125                                 "\tUsage: Listen ADDRESS");
126                 return ERR_INVALID_ARG;
127         }
129         listener = realloc(listen_addresses,
130                         (listen_addresses_num + 1) * sizeof(*listen_addresses));
131         if (! listener) {
132                 char buf[1024];
133                 sdb_log(SDB_LOG_ERR, "config: Failed to allocate memory: %s",
134                                 sdb_strerror(errno, buf, sizeof(buf)));
135                 return -1;
136         }
138         listen_addresses = listener;
139         listener = listen_addresses + listen_addresses_num;
140         memset(listener, 0, sizeof(*listener));
141         listener->address = strdup(address);
142         if (! listener->address) {
143                 char buf[1024];
144                 sdb_log(SDB_LOG_ERR, "config: Failed to allocate memory: %s",
145                                 sdb_strerror(errno, buf, sizeof(buf)));
146                 return -1;
147         }
149         for (i = 0; i < ci->children_num; ++i) {
150                 oconfig_item_t *child = ci->children + i;
151                 char *tmp = NULL;
153                 if (! strcasecmp(child->key, "SSLCertificate")) {
154                         if (oconfig_get_string(child, &tmp)) {
155                                 ret = ERR_INVALID_ARG;
156                                 break;
157                         }
158                         listener->ssl_opts.cert_file = strdup(tmp);
159                 }
160                 else if (! strcasecmp(child->key, "SSLCertificateKey")) {
161                         if (oconfig_get_string(child, &tmp)) {
162                                 ret = ERR_INVALID_ARG;
163                                 break;
164                         }
165                         listener->ssl_opts.key_file = strdup(tmp);
166                 }
167                 else if (! strcasecmp(child->key, "SSLCACertificates")) {
168                         if (oconfig_get_string(child, &tmp)) {
169                                 ret = ERR_INVALID_ARG;
170                                 break;
171                         }
172                         listener->ssl_opts.ca_file = strdup(tmp);
173                 }
174                 else {
175                         sdb_log(SDB_LOG_WARNING, "config: Unknown option '%s' "
176                                         "inside 'Listen' -- see the documentation for "
177                                         "details.", child->key);
178                         continue;
179                 }
180         }
182         if (ret) {
183                 sdb_ssl_free_options(&listener->ssl_opts);
184                 return ret;
185         }
187         ++listen_addresses_num;
188         return 0;
189 } /* daemon_add_listener */
191 static int
192 daemon_set_interval(oconfig_item_t *ci)
194         return config_get_interval(ci, &default_interval);
195 } /* daemon_set_interval */
197 static int
198 daemon_set_plugindir(oconfig_item_t *ci)
200         if (oconfig_get_string(ci, &plugin_dir)) {
201                 sdb_log(SDB_LOG_ERR, "config: PluginDir requires a single "
202                                 "string argument\n"
203                                 "\tUsage: PluginDir DIR");
204                 return ERR_INVALID_ARG;
205         }
206         plugin_dir = strdup(plugin_dir);
207         return 0;
208 } /* daemon_set_plugindir */
210 static int
211 daemon_load_plugin(oconfig_item_t *ci)
213         char *name;
214         int i;
216         if (oconfig_get_string(ci, &name)) {
217                 sdb_log(SDB_LOG_ERR, "config: LoadPlugin requires a single "
218                                 "string argument\n"
219                                 "\tUsage: LoadPlugin PLUGIN");
220                 return ERR_INVALID_ARG;
221         }
223         for (i = 0; i < ci->children_num; ++i) {
224                 oconfig_item_t *child = ci->children + i;
226                 /* we don't currently support any options */
227                 sdb_log(SDB_LOG_WARNING, "config: Unknown option '%s' "
228                                 "inside 'LoadPlugin' -- see the documentation for "
229                                 "details.", child->key);
230                 continue;
231         }
233         /* returns a negative value on error */
234         return sdb_plugin_load(plugin_dir, name, NULL);
235 } /* daemon_load_plugin */
237 static int
238 daemon_load_backend(oconfig_item_t *ci)
240         sdb_plugin_ctx_t ctx = SDB_PLUGIN_CTX_INIT;
242         char plugin_name[1024];
243         char *name;
245         int i;
247         ctx.interval = default_interval;
249         if (oconfig_get_string(ci, &name)) {
250                 sdb_log(SDB_LOG_ERR, "config: LoadBackend requires a single "
251                                 "string argument\n"
252                                 "\tUsage: LoadBackend BACKEND");
253                 return ERR_INVALID_ARG;
254         }
256         snprintf(plugin_name, sizeof(plugin_name), "backend::%s", name);
258         for (i = 0; i < ci->children_num; ++i) {
259                 oconfig_item_t *child = ci->children + i;
261                 if (! strcasecmp(child->key, "Interval")) {
262                         if (config_get_interval(child, &ctx.interval))
263                                 return ERR_INVALID_ARG;
264                 }
265                 else {
266                         sdb_log(SDB_LOG_WARNING, "config: Unknown option '%s' "
267                                         "inside 'LoadBackend' -- see the documentation for "
268                                         "details.", child->key);
269                         continue;
270                 }
271         }
273         return sdb_plugin_load(plugin_dir, plugin_name, &ctx);
274 } /* daemon_load_backend */
276 static int
277 daemon_configure_plugin(oconfig_item_t *ci)
279         char plugin_name[1024];
280         char *name;
282         assert(ci);
284         if (oconfig_get_string(ci, &name)) {
285                 sdb_log(SDB_LOG_ERR, "config: %s requires a single "
286                                 "string argument\n"
287                                 "\tUsage: <%s NAME>...</%s>",
288                                 ci->key, ci->key, ci->key);
289                 return ERR_INVALID_ARG;
290         }
292         if (!strcasecmp(ci->key, "Backend"))
293                 snprintf(plugin_name, sizeof(plugin_name), "Backend::%s", name);
294         else
295                 strncpy(plugin_name, name, sizeof(plugin_name));
296         return sdb_plugin_configure(plugin_name, ci);
297 } /* daemon_configure_backend */
299 static token_parser_t token_parser_list[] = {
300         { "Listen", daemon_add_listener },
301         { "Interval", daemon_set_interval },
302         { "PluginDir", daemon_set_plugindir },
303         { "LoadPlugin", daemon_load_plugin },
304         { "LoadBackend", daemon_load_backend },
305         { "Backend", daemon_configure_plugin },
306         { "Plugin", daemon_configure_plugin },
307         { NULL, NULL },
308 };
310 /*
311  * public API
312  */
314 void
315 daemon_free_listen_addresses(void)
317         size_t i;
319         if (! listen_addresses)
320                 return;
322         for (i = 0; i < listen_addresses_num; ++i) {
323                 free(listen_addresses[i].address);
324                 sdb_ssl_free_options(&listen_addresses[i].ssl_opts);
325         }
326         free(listen_addresses);
328         listen_addresses = NULL;
329         listen_addresses_num = 0;
330 } /* daemon_free_listen_addresses */
332 int
333 daemon_parse_config(const char *filename)
335         oconfig_item_t *ci;
336         int retval = 0, i;
338         ci = oconfig_parse_file(filename);
339         if (! ci)
340                 return ERR_PARSE_FAILED;
342         for (i = 0; i < ci->children_num; ++i) {
343                 oconfig_item_t *child = ci->children + i;
344                 int status = ERR_UNKNOWN_OPTION, j;
346                 for (j = 0; token_parser_list[j].name; ++j) {
347                         if (! strcasecmp(token_parser_list[j].name, child->key))
348                                 status = token_parser_list[j].dispatcher(child);
349                 }
351                 if (status) {
352                         sdb_error_set("config: Failed to parse option '%s'\n",
353                                         child->key);
354                         if (status == ERR_UNKNOWN_OPTION)
355                                 sdb_error_append("\tUnknown option '%s' -- "
356                                                 "see the documentation for details\n",
357                                                 child->key);
358                         sdb_error_chomp();
359                         sdb_error_log(SDB_LOG_ERR);
360                         retval = status;
361                 }
362         }
364         oconfig_free(ci);
365         free(ci);
367         if (plugin_dir) {
368                 free(plugin_dir);
369                 plugin_dir = NULL;
370         }
371         return retval;
372 } /* daemon_parse_config */
374 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */