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 char **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)
117 {
118 char **tmp;
119 char *address;
121 if (oconfig_get_string(ci, &address)) {
122 sdb_log(SDB_LOG_ERR, "config: Listen requires a single "
123 "string argument\n"
124 "\tUsage: Listen ADDRESS");
125 return ERR_INVALID_ARG;
126 }
128 tmp = realloc(listen_addresses,
129 (listen_addresses_num + 1) * sizeof(*listen_addresses));
130 if (! tmp) {
131 char buf[1024];
132 sdb_log(SDB_LOG_ERR, "config: Failed to allocate memory: %s",
133 sdb_strerror(errno, buf, sizeof(buf)));
134 return -1;
135 }
137 listen_addresses = tmp;
138 listen_addresses[listen_addresses_num] = strdup(address);
139 if (! listen_addresses[listen_addresses_num]) {
140 char buf[1024];
141 sdb_log(SDB_LOG_ERR, "config: Failed to allocate memory: %s",
142 sdb_strerror(errno, buf, sizeof(buf)));
143 return -1;
144 }
146 ++listen_addresses_num;
147 return 0;
148 } /* daemon_add_listener */
150 static int
151 daemon_set_interval(oconfig_item_t *ci)
152 {
153 return config_get_interval(ci, &default_interval);
154 } /* daemon_set_interval */
156 static int
157 daemon_set_plugindir(oconfig_item_t *ci)
158 {
159 if (oconfig_get_string(ci, &plugin_dir)) {
160 sdb_log(SDB_LOG_ERR, "config: PluginDir requires a single "
161 "string argument\n"
162 "\tUsage: PluginDir DIR");
163 return ERR_INVALID_ARG;
164 }
165 plugin_dir = strdup(plugin_dir);
166 return 0;
167 } /* daemon_set_plugindir */
169 static int
170 daemon_load_plugin(oconfig_item_t *ci)
171 {
172 char *name;
173 int i;
175 if (oconfig_get_string(ci, &name)) {
176 sdb_log(SDB_LOG_ERR, "config: LoadPlugin requires a single "
177 "string argument\n"
178 "\tUsage: LoadPlugin PLUGIN");
179 return ERR_INVALID_ARG;
180 }
182 for (i = 0; i < ci->children_num; ++i) {
183 oconfig_item_t *child = ci->children + i;
185 /* we don't currently support any options */
186 sdb_log(SDB_LOG_WARNING, "config: Unknown option '%s' "
187 "inside 'LoadPlugin' -- see the documentation for "
188 "details.", child->key);
189 continue;
190 }
192 /* returns a negative value on error */
193 return sdb_plugin_load(plugin_dir, name, NULL);
194 } /* daemon_load_plugin */
196 static int
197 daemon_load_backend(oconfig_item_t *ci)
198 {
199 sdb_plugin_ctx_t ctx = SDB_PLUGIN_CTX_INIT;
201 char plugin_name[1024];
202 char *name;
204 int i;
206 ctx.interval = default_interval;
208 if (oconfig_get_string(ci, &name)) {
209 sdb_log(SDB_LOG_ERR, "config: LoadBackend requires a single "
210 "string argument\n"
211 "\tUsage: LoadBackend BACKEND");
212 return ERR_INVALID_ARG;
213 }
215 snprintf(plugin_name, sizeof(plugin_name), "backend::%s", name);
217 for (i = 0; i < ci->children_num; ++i) {
218 oconfig_item_t *child = ci->children + i;
220 if (! strcasecmp(child->key, "Interval")) {
221 if (config_get_interval(child, &ctx.interval))
222 return ERR_INVALID_ARG;
223 }
224 else {
225 sdb_log(SDB_LOG_WARNING, "config: Unknown option '%s' "
226 "inside 'LoadBackend' -- see the documentation for "
227 "details.", child->key);
228 continue;
229 }
230 }
232 return sdb_plugin_load(plugin_dir, plugin_name, &ctx);
233 } /* daemon_load_backend */
235 static int
236 daemon_configure_plugin(oconfig_item_t *ci)
237 {
238 char *name;
240 assert(ci);
242 if (oconfig_get_string(ci, &name)) {
243 sdb_log(SDB_LOG_ERR, "config: %s requires a single "
244 "string argument\n"
245 "\tUsage: <%s NAME>...</%s>",
246 ci->key, ci->key, ci->key);
247 return ERR_INVALID_ARG;
248 }
250 return sdb_plugin_configure(name, ci);
251 } /* daemon_configure_backend */
253 static token_parser_t token_parser_list[] = {
254 { "Listen", daemon_add_listener },
255 { "Interval", daemon_set_interval },
256 { "PluginDir", daemon_set_plugindir },
257 { "LoadPlugin", daemon_load_plugin },
258 { "LoadBackend", daemon_load_backend },
259 { "Backend", daemon_configure_plugin },
260 { "Plugin", daemon_configure_plugin },
261 { NULL, NULL },
262 };
264 /*
265 * public API
266 */
268 void
269 daemon_free_listen_addresses(void)
270 {
271 size_t i;
273 if (! listen_addresses)
274 return;
276 for (i = 0; i < listen_addresses_num; ++i)
277 free(listen_addresses[i]);
278 free(listen_addresses);
280 listen_addresses = NULL;
281 listen_addresses_num = 0;
282 } /* daemon_free_listen_addresses */
284 int
285 daemon_parse_config(const char *filename)
286 {
287 oconfig_item_t *ci;
288 int retval = 0, i;
290 ci = oconfig_parse_file(filename);
291 if (! ci)
292 return ERR_PARSE_FAILED;
294 for (i = 0; i < ci->children_num; ++i) {
295 oconfig_item_t *child = ci->children + i;
296 int status = ERR_UNKNOWN_OPTION, j;
298 for (j = 0; token_parser_list[j].name; ++j) {
299 if (! strcasecmp(token_parser_list[j].name, child->key))
300 status = token_parser_list[j].dispatcher(child);
301 }
303 if (status) {
304 sdb_error_set("config: Failed to parse option '%s'\n",
305 child->key);
306 if (status == ERR_UNKNOWN_OPTION)
307 sdb_error_append("\tUnknown option '%s' -- "
308 "see the documentation for details\n",
309 child->key);
310 sdb_error_chomp();
311 sdb_error_log(SDB_LOG_ERR);
312 retval = status;
313 }
314 }
316 oconfig_free(ci);
317 free(ci);
319 if (plugin_dir) {
320 free(plugin_dir);
321 plugin_dir = NULL;
322 }
323 return retval;
324 } /* daemon_parse_config */
326 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */