summaryrefslogtreecommitdiff
path: root/src/daemon/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/config.c')
-rw-r--r--src/daemon/config.c1550
1 files changed, 1550 insertions, 0 deletions
diff --git a/src/daemon/config.c b/src/daemon/config.c
new file mode 100644
index 0000000..7691e2a
--- /dev/null
+++ b/src/daemon/config.c
@@ -0,0 +1,1550 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common/log.h>
+#include <murphy/core/context.h>
+#include <murphy/core/plugin.h>
+#include <murphy/daemon/config.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+#define MAX_ARGS 64
+
+static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
+ int saved_argc, char **saved_argv, char **envp);
+
+/*
+ * command line processing
+ */
+
+static void print_usage(mrp_context_t *ctx, const char *argv0, int exit_code,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [-V [valgrind-path] [valgrind-options]]\n\n"
+ "The possible options are:\n"
+ " -c, --config-file=PATH main configuration file to use\n"
+ " The default configuration file is '%s'.\n"
+ " -C, --config-dir=PATH configuration directory to use\n"
+ " If omitted, defaults to '%s'.\n"
+ " -P, --plugin-dir=PATH load plugins from DIR\n"
+ " The default plugin directory is '%s'.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable given debug configuration\n"
+ " -D, --list-debug list known debug sites\n"
+ " -f, --foreground don't daemonize\n"
+ " -h, --help show help on usage\n"
+ " -q, --query-plugins show detailed information about\n"
+ " all the available plugins\n"
+ " -B, --blacklist-plugins <list> disable list of plugins\n"
+ " -I, --blacklist-builtin <list> disable list of builtin plugins\n"
+ " -E, --blacklist-dynamic <list> disable list of dynamic plugins\n"
+ " -w, --whitelist-plugins <list> disable list of plugins\n"
+ " -i, --whitelist-builtin <list> disable list of builtin plugins\n"
+ " -e, --whitelist-dynamic <list> disable list of dynamic plugins\n"
+ " -R, --no-poststart-load "
+ "disable post-startup plugin loading\n"
+ " -p, --disable-console disable Murphy debug console\n"
+ " -V, --valgrind run through valgrind\n",
+ argv0, ctx->config_file, ctx->config_dir, ctx->plugin_dir);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void print_plugin_help(mrp_context_t *ctx, int detailed)
+{
+#define PRNT(fmt, arg) snprintf(defval, sizeof(defval), fmt, arg)
+
+ mrp_plugin_t *plugin;
+ mrp_plugin_descr_t *descr;
+ mrp_plugin_arg_t *arg;
+ mrp_list_hook_t *p, *n;
+ char *type, defval[64];
+ int i;
+
+ mrp_load_all_plugins(ctx);
+
+ printf("\nAvailable plugins:\n\n");
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+ descr = plugin->descriptor;
+
+ printf("- %splugin %s:", plugin->handle ? "" : "Builtin ", descr->name);
+ if (detailed) {
+ printf(" (%s, version %d.%d.%d)\n", plugin->path,
+ MRP_VERSION_MAJOR(descr->version),
+ MRP_VERSION_MINOR(descr->version),
+ MRP_VERSION_MICRO(descr->version));
+ printf(" Authors: %s\n", descr->authors);
+ }
+ else
+ printf("\n");
+
+ if (detailed)
+ printf(" Description:\n %s\n", descr->description);
+
+ if (descr->args != NULL) {
+ printf(" Arguments:\n");
+
+ for (i = 0, arg = descr->args; i < descr->narg; i++, arg++) {
+ printf(" %s: ", arg->key);
+ switch (arg->type) {
+ case MRP_PLUGIN_ARG_TYPE_STRING:
+ type = "string";
+ PRNT("%s", arg->str ? arg->str : "<none>");
+ break;
+ case MRP_PLUGIN_ARG_TYPE_BOOL:
+ type = "boolean";
+ PRNT("%s", arg->bln ? "TRUE" : "FALSE");
+ break;
+ case MRP_PLUGIN_ARG_TYPE_UINT32:
+ type = "unsigned 32-bit integer";
+ PRNT("%u", arg->u32);
+ break;
+ case MRP_PLUGIN_ARG_TYPE_INT32:
+ type = "signed 32-bit integer";
+ PRNT("%d", arg->i32);
+ break;
+ case MRP_PLUGIN_ARG_TYPE_DOUBLE:
+ type = "double-precision floating point";
+ PRNT("%f", arg->dbl);
+ break;
+ default:
+ type = "<unknown argument type>";
+ PRNT("%s", "<unknown>");
+ }
+
+ printf("%s, default value=%s\n", type, defval);
+ }
+ }
+
+ if (descr->help != NULL && descr->help[0])
+ printf(" Help:\n %s\n", descr->help);
+
+ printf("\n");
+ }
+
+ printf("\n");
+
+#if 0
+ printf("Note that you can disable any plugin from the command line by\n");
+ printf("using the '-a name:%s' option.\n", MURPHY_PLUGIN_ARG_DISABLED);
+#endif
+}
+
+
+static void config_set_defaults(mrp_context_t *ctx, char *argv0)
+{
+ static char cfg_file[PATH_MAX], cfg_dir[PATH_MAX], plugin_dir[PATH_MAX];
+ char *e;
+ int l;
+
+ if ((e = strstr(argv0, "/src/murphyd")) != NULL ||
+ (e = strstr(argv0, "/src/.libs/lt-murphyd")) != NULL) {
+ mrp_log_mask_t saved = mrp_log_set_mask(MRP_LOG_MASK_WARNING);
+ mrp_log_warning("***");
+ mrp_log_warning("*** Looks like we are run from the source tree.");
+ mrp_log_warning("*** Runtime defaults will be set accordingly...");
+ mrp_log_warning("***");
+ mrp_log_set_mask(saved);
+
+ l = e - argv0;
+ snprintf(cfg_dir, sizeof(cfg_dir), "%*.*s/src/daemon", l, l, argv0);
+ snprintf(cfg_file, sizeof(cfg_file), "%s/murphy-lua.conf", cfg_dir);
+ snprintf(plugin_dir, sizeof(plugin_dir), "%*.*s/src/.libs",
+ l, l, argv0);
+
+ ctx->config_file = cfg_file;
+ ctx->config_dir = cfg_dir;
+ ctx->plugin_dir = plugin_dir;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+ ctx->foreground = TRUE;
+ }
+ else {
+ ctx->config_file = MRP_DEFAULT_CONFIG_FILE;
+ ctx->config_dir = MRP_DEFAULT_CONFIG_DIR;
+ ctx->plugin_dir = MRP_DEFAULT_PLUGIN_DIR;
+ ctx->log_mask = MRP_LOG_MASK_ERROR;
+ ctx->log_target = MRP_LOG_TO_STDERR;
+ }
+}
+
+
+void mrp_parse_cmdline(mrp_context_t *ctx, int argc, char **argv, char **envp)
+{
+# define OPTIONS "c:C:l:t:fP:a:vd:hHqB:I:E:w:i:e:RpV"
+ struct option options[] = {
+ { "config-file" , required_argument, NULL, 'c' },
+ { "config-dir" , required_argument, NULL, 'C' },
+ { "plugin-dir" , required_argument, NULL, 'P' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target" , required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "foreground" , no_argument , NULL, 'f' },
+ { "help" , no_argument , NULL, 'h' },
+ { "more-help" , no_argument , NULL, 'H' },
+ { "query-plugins" , no_argument , NULL, 'q' },
+ { "blacklist" , required_argument, NULL, 'B' },
+ { "blacklist-plugins", required_argument, NULL, 'B' },
+ { "blacklist-builtin", required_argument, NULL, 'I' },
+ { "blacklist-dynamic", required_argument, NULL, 'E' },
+ { "whitelist" , required_argument, NULL, 'w' },
+ { "whitelist-plugins", required_argument, NULL, 'w' },
+ { "whitelist-builtin", required_argument, NULL, 'i' },
+ { "whitelist-dynamic", required_argument, NULL, 'e' },
+ { "no-poststart-load", no_argument , NULL, 'R' },
+ { "disable-console" , no_argument , NULL, 'p' },
+ { "valgrind" , optional_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+# define SAVE_ARG(a) do { \
+ if (saved_argc >= MAX_ARGS) \
+ print_usage(ctx, argv[0], EINVAL, \
+ "too many command line arguments"); \
+ else \
+ saved_argv[saved_argc++] = a; \
+ } while (0)
+# define SAVE_OPT(o) SAVE_ARG(o)
+# define SAVE_OPTARG(o, a) SAVE_ARG(o); SAVE_ARG(a)
+ char *saved_argv[MAX_ARGS];
+ int saved_argc;
+
+ int opt, help;
+
+ config_set_defaults(ctx, argv[0]);
+ mrp_log_set_mask(ctx->log_mask);
+ mrp_log_set_target(ctx->log_target);
+
+ saved_argc = 0;
+ saved_argv[saved_argc++] = argv[0];
+
+ help = FALSE;
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ SAVE_OPTARG("-c", optarg);
+ ctx->config_file = optarg;
+ break;
+
+ case 'C':
+ SAVE_OPTARG("-C", optarg);
+ ctx->config_dir = optarg;
+ break;
+
+ case 'P':
+ SAVE_OPTARG("-P", optarg);
+ ctx->plugin_dir = optarg;
+ break;
+
+ case 'v':
+ SAVE_OPT("-v");
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ mrp_log_set_mask(ctx->log_mask);
+ break;
+
+ case 'l':
+ SAVE_OPTARG("-l", optarg);
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(ctx, argv[0], EINVAL,
+ "invalid log level '%s'", optarg);
+ else
+ mrp_log_set_mask(ctx->log_mask);
+ break;
+
+ case 't':
+ SAVE_OPTARG("-t", optarg);
+ ctx->log_target = optarg;
+ break;
+
+ case 'd':
+ SAVE_OPTARG("-d", optarg);
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'f':
+ SAVE_OPT("-f");
+ ctx->foreground = TRUE;
+ break;
+
+ case 'h':
+ SAVE_OPT("-h");
+ help++;
+ break;
+
+ case 'H':
+ SAVE_OPT("-H");
+ help += 2;
+ break;
+
+ case 'q':
+ SAVE_OPT("-q");
+ print_plugin_help(ctx, TRUE);
+ break;
+
+ case 'B':
+ if (ctx->blacklist_plugins != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "blacklist option given multiple times");
+ SAVE_OPTARG("-B", optarg);
+ ctx->blacklist_plugins = optarg;
+ break;
+ case 'I':
+ if (ctx->blacklist_builtin != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "builtin blacklist option given multiple times");
+ SAVE_OPTARG("-I", optarg);
+ ctx->blacklist_builtin = optarg;
+ break;
+ case 'E':
+ if (ctx->blacklist_dynamic != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "dynamic blacklist option given multiple times");
+ SAVE_OPTARG("-E", optarg);
+ ctx->blacklist_dynamic = optarg;
+ break;
+ case 'w':
+ if (ctx->whitelist_plugins != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "whitelist option given multiple times");
+ SAVE_OPTARG("-w", optarg);
+ ctx->whitelist_plugins = optarg;
+ break;
+ case 'i':
+ if (ctx->whitelist_builtin != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "builtin whitelist option given multiple times");
+ SAVE_OPTARG("-i", optarg);
+ ctx->whitelist_builtin = optarg;
+ break;
+ case 'e':
+ if (ctx->whitelist_dynamic != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "dynamic whitelist option given multiple times");
+ SAVE_OPTARG("-e", optarg);
+ ctx->whitelist_dynamic = optarg;
+ break;
+
+ case 'R':
+ SAVE_OPT("-R");
+ ctx->disable_runtime_load = true;
+ break;
+
+ case 'p':
+ SAVE_OPT("-p");
+ ctx->disable_console = TRUE;
+ break;
+ case 'V':
+ valgrind(optarg, argc, argv, optind, saved_argc, saved_argv, envp);
+ break;
+
+ default:
+ print_usage(ctx, argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (help) {
+ print_usage(ctx, argv[0], -1, "");
+ if (help > 1)
+ print_plugin_help(ctx, FALSE);
+ exit(0);
+ }
+
+}
+
+
+
+/*
+ * configuration file processing
+ */
+
+typedef struct {
+ char buf[MRP_CFG_MAXLINE]; /* input buffer */
+ char *token; /* current token */
+ char *in; /* filling pointer */
+ char *out; /* consuming pointer */
+ char *next; /* next token buffer position */
+ int fd; /* input file */
+ int error; /* whether has encounted and error */
+ char *file; /* file being processed */
+ int line; /* line number */
+ int next_newline;
+ int was_newline;
+} input_t;
+
+
+#define COMMON_ACTION_FIELDS \
+ action_type_t type; /* action to execute */ \
+ mrp_list_hook_t hook /* to command sequence */
+
+typedef enum { /* action types */
+ ACTION_UNKNOWN = 0,
+ ACTION_LOAD, /* load a plugin */
+ ACTION_TRYLOAD, /* load a plugin, ignore errors */
+ ACTION_IF, /* if-else branch */
+ ACTION_SETCFG, /* set a config variable */
+ ACTION_INFO, /* emit an info message */
+ ACTION_WARNING, /* emit a warning message */
+ ACTION_ERROR, /* emit and error message and exit */
+ ACTION_MAX,
+} action_type_t;
+
+typedef enum { /* branch operators */
+ BR_UNKNOWN = 0,
+ BR_PLUGIN_EXISTS, /* test if a plugin exists */
+} branch_op_t;
+
+typedef struct { /* a generic action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+} any_action_t;
+
+typedef struct { /* a command-type of action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ char **args; /* arguments for the action */
+ int narg; /* number of arguments */
+} cmd_action_t;
+
+typedef struct { /* a command-type of action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ char *name; /* plugin to load */
+ char *instance; /* load as this instance */
+ mrp_plugin_arg_t *args; /* plugin arguments */
+ int narg; /* number of arguments */
+} load_action_t;
+
+typedef struct { /* a branch test action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ branch_op_t op; /* branch operator */
+ char *arg1; /* argument for the operator */
+ char *arg2; /* argument for the operator */
+ mrp_list_hook_t pos; /* postitive branch */
+ mrp_list_hook_t neg; /* negative branch */
+} branch_action_t;
+
+typedef struct { /* a branch test action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ char *message; /* message to show */
+} message_action_t;
+
+typedef enum {
+ CFGVAR_UNKNOWN = 0,
+ CFGVAR_RESOLVER_RULES, /* resolver ruleset file */
+} cfgvar_t;
+
+typedef struct {
+ COMMON_ACTION_FIELDS;
+ cfgvar_t id; /* confguration variable */
+ char *value; /* value for variable */
+} setcfg_action_t;
+
+typedef struct {
+ const char *keyword;
+ any_action_t *(*parse)(input_t *in, char **args, int narg);
+ int (*exec)(mrp_context_t *ctx, any_action_t *action);
+ void (*free)(any_action_t *a);
+} action_descr_t;
+
+
+
+static any_action_t *parse_action(input_t *in, char **args, int narg);
+static any_action_t *parse_load(input_t *in, char **argv, int argc);
+static any_action_t *parse_if_else(input_t *in, char **argv, int argc);
+static any_action_t *parse_setcfg(input_t *in, char **argv, int argc);
+static any_action_t *parse_message(input_t *in, char **argv, int argc);
+static int exec_action(mrp_context_t *ctx, any_action_t *action);
+static int exec_load(mrp_context_t *ctx, any_action_t *action);
+static int exec_if_else(mrp_context_t *ctx, any_action_t *action);
+static int exec_setcfg(mrp_context_t *ctx, any_action_t *action);
+static int exec_message(mrp_context_t *ctx, any_action_t *action);
+static void free_action(any_action_t *action);
+static void free_if_else(any_action_t *action);
+static void free_load(any_action_t *action);
+static void free_setcfg(any_action_t *action);
+static void free_message(any_action_t *action);
+
+static char *get_next_token(input_t *in);
+static int get_next_line(input_t *in, char **args, size_t size);
+static char *replace_tokens(input_t *in, char *first, char *last,
+ char *token, int size);
+
+#define A(type, keyword, parse, exec, free) \
+ [ACTION_##type] = { MRP_KEYWORD_##keyword, parse, exec, free }
+
+static action_descr_t actions[] = {
+ [ACTION_UNKNOWN] = { NULL, NULL, NULL, NULL },
+
+ A(LOAD , LOAD , parse_load , exec_load , free_load),
+ A(TRYLOAD, TRYLOAD, parse_load , exec_load , free_load),
+ A(IF , IF , parse_if_else, exec_if_else, free_if_else),
+ A(SETCFG , SETCFG , parse_setcfg , exec_setcfg , free_setcfg),
+ A(INFO , INFO , parse_message, exec_message, free_message),
+ A(WARNING, WARNING, parse_message, exec_message, free_message),
+ A(ERROR , ERROR , parse_message, exec_message, free_message),
+
+ [ACTION_MAX] = { NULL, NULL, NULL, NULL }
+};
+
+#undef A
+
+
+
+mrp_cfgfile_t *mrp_parse_cfgfile(const char *path)
+{
+ mrp_cfgfile_t *cfg = NULL;
+ input_t input;
+ char *args[MRP_CFG_MAXARGS];
+ int narg;
+ any_action_t *a;
+
+ memset(&input, 0, sizeof(input));
+ input.token = input.buf;
+ input.in = input.buf;
+ input.out = input.buf;
+ input.next = input.buf;
+ input.fd = open(path, O_RDONLY);
+ input.file = (char *)path;
+ input.line = 1;
+
+ if (input.fd < 0) {
+ mrp_log_error("Failed to open configuration file '%s' (%d: %s).",
+ path, errno, strerror(errno));
+ goto fail;
+ }
+
+ cfg = mrp_allocz(sizeof(*cfg));
+
+ if (cfg == NULL) {
+ mrp_log_error("Failed to allocate configuration file buffer.");
+ goto fail;
+ }
+
+ mrp_list_init(&cfg->actions);
+
+ while ((narg = get_next_line(&input, args, MRP_ARRAY_SIZE(args))) > 0) {
+ a = parse_action(&input, args, narg);
+
+ if (a != NULL)
+ mrp_list_append(&cfg->actions, &a->hook);
+ else
+ goto fail;
+ }
+
+ if (narg == 0)
+ return cfg;
+
+ fail:
+ if (input.fd >= 0)
+ close(input.fd);
+ if (cfg)
+ mrp_free_cfgfile(cfg);
+
+ return NULL;
+}
+
+
+void mrp_free_cfgfile(mrp_cfgfile_t *cfg)
+{
+ mrp_list_hook_t *p, *n;
+ any_action_t *a;
+
+ mrp_list_foreach(&cfg->actions, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_action(a);
+ }
+
+ mrp_free(cfg);
+}
+
+
+int mrp_exec_cfgfile(mrp_context_t *ctx, mrp_cfgfile_t *cfg)
+{
+ mrp_list_hook_t *p, *n;
+ any_action_t *a;
+
+ mrp_list_foreach(&cfg->actions, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ if (!exec_action(ctx, a))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static any_action_t *parse_action(input_t *in, char **args, int narg)
+{
+ action_descr_t *ad = actions + 1;
+
+ while (ad->keyword != NULL) {
+ if (!strcmp(args[0], ad->keyword))
+ return ad->parse(in, args, narg);
+ ad++;
+ }
+
+ mrp_log_error("Unknown command '%s' in file '%s'.", args[0], in->file);
+ return NULL;
+}
+
+
+static void free_action(any_action_t *action)
+{
+ mrp_list_delete(&action->hook);
+
+ if (ACTION_UNKNOWN < action->type && action->type < ACTION_MAX)
+ actions[action->type].free(action);
+ else {
+ mrp_log_error("Unknown configuration action of type 0x%x.",
+ action->type);
+ mrp_free(action);
+ }
+}
+
+
+static int exec_action(mrp_context_t *ctx, any_action_t *action)
+{
+ if (ACTION_UNKNOWN < action->type && action->type < ACTION_MAX)
+ return actions[action->type].exec(ctx, action);
+ else {
+ mrp_log_error("Unknown configuration action of type 0x%x.",
+ action->type);
+ return FALSE;
+ }
+}
+
+
+static any_action_t *parse_load(input_t *in, char **argv, int argc)
+{
+ load_action_t *action;
+ action_type_t type;
+ mrp_plugin_arg_t *args, *a;
+ int narg, i, start;
+ char *k, *v;
+
+ MRP_UNUSED(in);
+
+ if (!strcmp(argv[0], MRP_KEYWORD_LOAD))
+ type = ACTION_LOAD;
+ else
+ type = ACTION_TRYLOAD;
+
+ if (argc < 2 || (action = mrp_allocz(sizeof(*action))) == NULL) {
+ mrp_log_error("Failed to allocate load config action.");
+ return NULL;
+ }
+
+ mrp_list_init(&action->hook);
+ action->type = type;
+ action->name = mrp_strdup(argv[1]);
+
+ if (action->name == NULL) {
+ mrp_log_error("Failed to allocate load config action.");
+ mrp_free(action);
+ return NULL;
+ }
+
+ args = NULL;
+
+ if (argc > 3 && !strcmp(argv[2], MRP_KEYWORD_AS)) {
+ /* [try-]load-plugin name as instance [args...] */
+ action->instance = mrp_strdup(argv[3]);
+ start = 4;
+
+ if (action->instance == NULL) {
+ mrp_log_error("Failed to allocate load config action.");
+ mrp_free(action->name);
+ mrp_free(action);
+ goto fail;
+ }
+ }
+ else {
+ /* [try-]load-plugin name [args...] */
+ start = 2;
+ }
+
+ narg = 0;
+ if (start < argc) {
+ if ((args = mrp_allocz_array(typeof(*args), argc - 1)) != NULL) {
+ for (i = start, a = args; i < argc; i++, a++) {
+ if (*argv[i] == MRP_START_COMMENT)
+ break;
+
+ mrp_debug("argument #%d: '%s'", i - start, argv[i]);
+
+ k = argv[i];
+ v = strchr(k, '=');
+
+ if (v != NULL)
+ *v++ = '\0';
+ else {
+ if (i + 2 < argc) {
+ if (argv[i+1][0] == '=' && argv[i+1][1] == '\0') {
+ v = argv[i + 2];
+ i += 2;
+ }
+ }
+ else {
+ mrp_log_error("Invalid plugin load argument '%s'.", k);
+ goto fail;
+ }
+ }
+
+ a->type = MRP_PLUGIN_ARG_TYPE_STRING;
+ a->key = mrp_strdup(k);
+ a->str = v ? mrp_strdup(v) : NULL;
+ narg++;
+
+ if (a->key == NULL || (a->str == NULL && v != NULL)) {
+ mrp_log_error("Failed to allocate plugin arg %s%s%s.",
+ k, v ? "=" : "", v ? v : "");
+ goto fail;
+ }
+ }
+ }
+ }
+
+ action->args = args;
+ action->narg = narg;
+
+ return (any_action_t *)action;
+
+
+ fail:
+ if (args != NULL) {
+ for (i = 1; i < argc && args[i].key != NULL; i++) {
+ mrp_free(args[i].key);
+ mrp_free(args[i].str);
+ }
+ mrp_free(args);
+ }
+
+ return NULL;
+}
+
+
+static void free_load(any_action_t *action)
+{
+ load_action_t *load = (load_action_t *)action;
+ int i;
+
+ if (load != NULL) {
+ mrp_free(load->name);
+
+ for (i = 0; i < load->narg; i++) {
+ mrp_free(load->args[i].key);
+ mrp_free(load->args[i].str);
+ }
+
+ mrp_free(load->args);
+ }
+}
+
+
+static int exec_load(mrp_context_t *ctx, any_action_t *action)
+{
+ load_action_t *load = (load_action_t *)action;
+ mrp_plugin_t *plugin;
+
+ plugin = mrp_load_plugin(ctx, load->name, load->instance,
+ load->args, load->narg);
+
+ if (plugin != NULL) {
+ plugin->may_fail = (load->type == ACTION_TRYLOAD);
+
+ return TRUE;
+ }
+ else
+ return (load->type == ACTION_TRYLOAD);
+}
+
+
+static any_action_t *parse_if_else(input_t *in, char **argv, int argc)
+{
+ branch_action_t *branch;
+ mrp_list_hook_t *actions;
+ any_action_t *a;
+ char *args[MRP_CFG_MAXARGS], *op, *name;
+ int start, narg, pos;
+
+ if (argc < 2) {
+ mrp_log_error("%s:%d: invalid use of if-conditional.",
+ in->file, in->line - 1);
+ return NULL;
+ }
+
+ start = in->line - 1;
+ op = argv[1];
+ name = argv[2];
+
+ if (strcmp(op, MRP_KEYWORD_EXISTS)) {
+ mrp_log_error("%s:%d: unknown operator '%s' in if-conditional.",
+ in->file, in->line - 1, op);
+ }
+
+ branch = mrp_allocz(sizeof(*branch));
+
+ if (branch != NULL) {
+ mrp_list_init(&branch->hook);
+ mrp_list_init(&branch->pos);
+ mrp_list_init(&branch->neg);
+
+ branch->type = ACTION_IF;
+ branch->op = BR_PLUGIN_EXISTS;
+ branch->arg1 = mrp_strdup(name);
+
+ if (branch->arg1 == NULL) {
+ mrp_log_error("Failed to allocate configuration if-conditional.");
+ goto fail;
+ }
+
+ pos = TRUE;
+ actions = &branch->pos;
+ while ((narg = get_next_line(in, args, sizeof(args))) > 0) {
+ if (narg == 1) {
+ if (!strcmp(args[0], MRP_KEYWORD_END))
+ return (any_action_t *)branch;
+
+ if (!strcmp(args[0], MRP_KEYWORD_ELSE)) {
+ if (pos) {
+ actions = &branch->neg;
+ pos = FALSE;
+ }
+ else {
+ mrp_log_error("%s:%d: extra else without if.",
+ in->file, in->line - 1);
+ goto fail;
+ }
+ }
+ }
+ else {
+ a = parse_action(in, args, narg);
+
+ if (a != NULL)
+ mrp_list_append(actions, &a->hook);
+ else
+ goto fail;
+ }
+ }
+ }
+ else {
+ mrp_log_error("Failed to allocate configuration if-conditional.");
+ return NULL;
+ }
+
+ mrp_log_error("%s:%d: unterminated if-conditional (missing 'end')",
+ in->file, start);
+
+ fail:
+ free_action((any_action_t *)branch);
+ return NULL;
+}
+
+
+static void free_if_else(any_action_t *action)
+{
+ branch_action_t *branch = (branch_action_t *)action;
+ any_action_t *a;
+ mrp_list_hook_t *p, *n;
+
+ if (branch != NULL) {
+ mrp_free(branch->arg1);
+ mrp_free(branch->arg2);
+
+ mrp_list_foreach(&branch->pos, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_action(a);
+ }
+
+ mrp_list_foreach(&branch->neg, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_action(a);
+ }
+
+ mrp_free(branch);
+ }
+}
+
+
+static int exec_if_else(mrp_context_t *ctx, any_action_t *action)
+{
+ branch_action_t *branch = (branch_action_t *)action;
+ mrp_list_hook_t *p, *n, *actions;
+ any_action_t *a;
+
+ if (branch->op != BR_PLUGIN_EXISTS || branch->arg1 == NULL)
+ return FALSE;
+
+ if (mrp_plugin_exists(ctx, branch->arg1))
+ actions = &branch->pos;
+ else
+ actions = &branch->neg;
+
+ mrp_list_foreach(actions, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+
+ if (!exec_action(ctx, a))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static any_action_t *parse_setcfg(input_t *in, char **argv, int argc)
+{
+ setcfg_action_t *action;
+ struct {
+ const char *name;
+ cfgvar_t id;
+ } *var, vartbl[] = {
+ { MRP_CFGVAR_RESOLVER, CFGVAR_RESOLVER_RULES },
+ { NULL , 0 },
+ };
+
+ if (argc < 3) {
+ mrp_log_error("%s:%d: configuration directive %s requires two "
+ "arguments, %d given.", in->file, in->line,
+ MRP_KEYWORD_SETCFG, argc - 1);
+ return NULL;
+ }
+
+ for (var = vartbl; var->name != NULL; var++)
+ if (!strcmp(var->name, argv[1]))
+ break;
+
+ if (var->name == NULL) {
+ mrp_log_error("%s:%d: unknown configuration variable '%s'.",
+ in->file, in->line, argv[1]);
+ return NULL;
+ }
+
+ if ((action = mrp_allocz(sizeof(*action))) == NULL) {
+ mrp_log_error("Failed to allocate %s %s configuration action.",
+ MRP_KEYWORD_SETCFG, argv[1]);
+ return NULL;
+ }
+
+ mrp_list_init(&action->hook);
+ action->type = ACTION_SETCFG;
+ action->id = var->id;
+ action->value = mrp_strdup(argv[2]);
+
+ if (action->value == NULL) {
+ mrp_log_error("Failed to allocate %s %s configuration action.",
+ MRP_KEYWORD_SETCFG, argv[1]);
+ mrp_free(action);
+ return NULL;
+ }
+
+ return (any_action_t *)action;
+}
+
+
+static int exec_setcfg(mrp_context_t *ctx, any_action_t *action)
+{
+ setcfg_action_t *setcfg = (setcfg_action_t *)action;
+
+ switch (setcfg->id) {
+ case CFGVAR_RESOLVER_RULES:
+ if (ctx->resolver_ruleset == NULL) {
+ ctx->resolver_ruleset = setcfg->value;
+ setcfg->value = NULL;
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Multiple resolver rulesets specified (%s, %s).",
+ ctx->resolver_ruleset, setcfg->value);
+ return FALSE;
+ }
+ break;
+ default:
+ mrp_log_error("Invalid configuration setting.");
+ }
+
+ return FALSE;
+}
+
+
+static void free_setcfg(any_action_t *action)
+{
+ setcfg_action_t *setcfg = (setcfg_action_t *)action;
+
+ if (setcfg != NULL) {
+ mrp_free(setcfg->value);
+ mrp_free(setcfg);
+ }
+}
+
+
+static any_action_t *parse_message(input_t *in, char **argv, int argc)
+{
+ message_action_t *msg;
+ action_type_t type;
+ char buf[4096], *p;
+ const char *t;
+ int i, l, n;
+
+ MRP_UNUSED(in);
+
+ if (argc < 2) {
+ mrp_log_error("%s requires at least one argument.", argv[0]);
+ return NULL;
+ }
+
+ if (!strcmp(argv[0], MRP_KEYWORD_ERROR))
+ type = ACTION_ERROR;
+ else if (!strcmp(argv[0], MRP_KEYWORD_WARNING))
+ type = ACTION_WARNING;
+ else if (!strcmp(argv[0], MRP_KEYWORD_INFO))
+ type = ACTION_INFO;
+ else
+ return NULL;
+
+ p = buf;
+ n = sizeof(buf);
+ if ((msg = mrp_allocz(sizeof(*msg))) != NULL) {
+ for (i = 1, t=""; i < argc && n > 0; i++, t=" ") {
+ l = snprintf(p, n, "%s%s", t, argv[i]);
+ p += l;
+ n -= l;
+ }
+
+ msg->type = type;
+ msg->message = mrp_strdup(buf);
+
+ if (msg->message == NULL) {
+ mrp_log_error("Failed to allocate %s config action.", argv[0]);
+ mrp_free(msg);
+ msg = NULL;
+ }
+ }
+
+ return (any_action_t *)msg;
+}
+
+
+static int exec_message(mrp_context_t *ctx, any_action_t *action)
+{
+ message_action_t *msg = (message_action_t *)action;
+
+ MRP_UNUSED(ctx);
+
+ switch (action->type) {
+ case ACTION_ERROR: mrp_log_error("%s", msg->message); exit(1);
+ case ACTION_WARNING: mrp_log_warning("%s", msg->message); return TRUE;
+ case ACTION_INFO: mrp_log_info("%s", msg->message); return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+static void free_message(any_action_t *action)
+{
+ message_action_t *msg = (message_action_t *)action;
+
+ if (msg != NULL) {
+ mrp_free(msg->message);
+ mrp_free(msg);
+ }
+}
+
+
+static int get_next_line(input_t *in, char **args, size_t size)
+{
+#define BLOCK_START(s) \
+ ((s[0] == '{' || s[0] == '[') && (s[1] == '\0' || s[1] == '\n'))
+#define BLOCK_END(s) \
+ ((s[0] == '}' || s[0] == ']') && (s[1] == '\0' || s[1] == '\n'))
+
+ char *token, *p;
+ char block[2], json[MRP_CFG_MAXLINE];
+ int narg, nest, beg;
+ int i, n, l, tot;
+
+ narg = 0;
+ nest = 0;
+ beg = -1;
+ tot = 0;
+ block[0] = block[1] = '\0';
+ while ((token = get_next_token(in)) != NULL && narg < (int)size) {
+ if (in->error)
+ return -1;
+
+ mrp_debug("read input token '%s'", token);
+
+ if (token[0] != '\n') {
+ if (BLOCK_START(token)) {
+ if (!nest) {
+ mrp_debug("collecting JSON argument");
+
+ block[0] = token[0];
+ block[1] = (block[0] == '{' ? '}' : ']');
+ nest = 1;
+ beg = narg;
+ tot = 1;
+ }
+ else {
+ if (token[0] == block[0])
+ nest++;
+ }
+ }
+
+ args[narg++] = token;
+
+ if (beg >= 0) { /* if collecting, update length */
+ tot += strlen(token) + 1;
+ if (strchr(token, ' ') || strchr(token, '\t'))
+ tot += 2; /* will need quoting */
+ }
+
+ if (BLOCK_END(token) && nest > 0) {
+ if (token[0] == block[1])
+ nest--;
+
+ if (nest == 0) {
+ mrp_debug("finished collecting JSON argument");
+
+ if (tot > (int)sizeof(json) - 1) {
+ mrp_log_error("Maximum token length exceeded.");
+ return -1;
+ }
+
+ p = json;
+ l = tot;
+ for (i = beg; i < narg; i++) {
+ if (strchr(args[i], ' ') || strchr(args[i], '\t'))
+ n = snprintf(p, l, "'%s'", args[i]);
+ else
+ n = snprintf(p, l, "%s", args[i]);
+ if (n >= l)
+ return -1;
+ p += n;
+ l -= n;
+ }
+
+ mrp_debug("collected JSON token: '%s'", json);
+
+ args[beg] = replace_tokens(in, args[beg], args[narg-1],
+ json, (int)(p - json));
+
+ if (args[beg] == NULL) {
+ mrp_log_error("Failed to replace block of tokens.");
+ return -1;
+ }
+ else
+ narg = beg + 1;
+
+ block[0] = '\0';
+ block[1] = '\0';
+ beg = -1;
+ }
+ }
+ }
+ else {
+ if (narg && *args[0] != MRP_START_COMMENT && *args[0] != '\n')
+ return narg;
+ else
+ narg = 0;
+ }
+ }
+
+ if (in->error)
+ return -1;
+
+ if (narg >= (int)size) {
+ mrp_log_error("Too many tokens on line %d of %s.",
+ in->line - 1, in->file);
+ return -1;
+ }
+ else {
+ if (*args[0] != MRP_START_COMMENT && *args[0] != '\n')
+ return narg;
+ else
+ return 0;
+ }
+}
+
+
+static inline void skip_whitespace(input_t *in)
+{
+ while ((*in->out == ' ' || *in->out == '\t') && in->out < in->in)
+ in->out++;
+}
+
+
+static inline void skip_rest_of_line(input_t *in)
+{
+ while (*in->out != '\n' && in->out < in->in)
+ in->out++;
+}
+
+
+static char *replace_tokens(input_t *in, char *first, char *last,
+ char *token, int size)
+{
+ char *beg = first;
+ char *end = last + strlen(last) + 1;
+
+ if (!(in->buf < beg && end < in->out))
+ return NULL;
+
+ if ((end - beg) < size)
+ return NULL;
+
+ strcpy(first, token);
+
+ return first;
+}
+
+
+static char *get_next_token(input_t *in)
+{
+ ssize_t len;
+ int diff, size;
+ int quote, quote_line;
+ char *p, *q;
+
+ /*
+ * Newline:
+ *
+ * If the previous token was terminated by a newline,
+ * take care of properly returning and administering
+ * the newline token here.
+ */
+
+ if (in->next_newline) {
+ in->next_newline = FALSE;
+ in->was_newline = TRUE;
+ in->line++;
+
+ return "\n";
+ }
+
+
+ /*
+ * if we just finished a line, discard all old data/tokens
+ */
+
+ if (*in->token == '\n' || in->was_newline) {
+ diff = in->out - in->buf;
+ size = in->in - in->out;
+ memmove(in->buf, in->out, size);
+ in->out -= diff;
+ in->in -= diff;
+ in->next = in->buf;
+ *in->in = '\0';
+ }
+
+ /*
+ * refill the buffer if we're empty or just flushed all tokens
+ */
+
+ if (in->token == in->buf && in->fd != -1) {
+ size = sizeof(in->buf) - 1 - (in->in - in->buf);
+ len = read(in->fd, in->in, size);
+
+ if (len < size) {
+ close(in->fd);
+ in->fd = -1;
+ }
+
+ if (len < 0) {
+ mrp_log_error("Failed to read from config file (%d: %s).",
+ errno, strerror(errno));
+ in->error = TRUE;
+ close(in->fd);
+ in->fd = -1;
+
+ return NULL;
+ }
+
+ in->in += len;
+ *in->in = '\0';
+ }
+
+ if (in->out >= in->in)
+ return NULL;
+
+ skip_whitespace(in);
+
+ quote = FALSE;
+ quote_line = 0;
+
+ p = in->out;
+ q = in->next;
+ in->token = q;
+
+ while (p < in->in) {
+ /*printf("[%c]\n", *p == '\n' ? '.' : *p);*/
+ switch (*p) {
+ /*
+ * Quoting:
+ *
+ * If we're not within a quote, mark a quote started.
+ * Otherwise if quote matches, close quoting. Otherwise
+ * copy the quoted quote verbatim.
+ */
+ case '\'':
+ case '\"':
+ in->was_newline = FALSE;
+ if (!quote) {
+ quote = *p++;
+ quote_line = in->line;
+ }
+ else {
+ if (*p == quote) {
+ quote = FALSE;
+ quote_line = 0;
+ p++;
+ *q++ = '\0';
+
+ in->out = p;
+ in->next = q;
+
+ return in->token;
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ break;
+
+ /*
+ * Whitespace:
+ *
+ * If we're quoting, copy verbatim. Otherwise mark the end
+ * of the token.
+ */
+ case ' ':
+ case '\t':
+ if (quote)
+ *q++ = *p++;
+ else {
+ p++;
+ *q++ = '\0';
+
+ in->out = p;
+ in->next = q;
+
+ return in->token;
+ }
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Escaping:
+ *
+ * If the last character in the input, copy verbatim.
+ * Otherwise if it escapes a '\n', skip both. Otherwise
+ * copy the escaped character verbatim.
+ */
+ case '\\':
+ if (p < in->in - 1) {
+ p++;
+ if (*p != '\n')
+ *q++ = *p++;
+ else {
+ p++;
+ in->line++;
+ in->out = p;
+ skip_whitespace(in);
+ p = in->out;
+ }
+ }
+ else
+ *q++ = *p++;
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Newline:
+ *
+ * We don't allow newlines to be quoted. Otherwise
+ * if the token is not the newline itself, we mark
+ * the next token to be newline and return the token
+ * it terminated.
+ */
+ case '\n':
+ if (quote) {
+ mrp_log_error("%s:%d: Unterminated quote (%c) started "
+ "on line %d.", in->file, in->line, quote,
+ quote_line);
+ in->error = TRUE;
+
+ return NULL;
+ }
+ else {
+ *q = '\0';
+ p++;
+
+ in->out = p;
+ in->next = q;
+
+ if (in->token == q) {
+ in->line++;
+ in->was_newline = TRUE;
+ return "\n";
+ }
+ else {
+ in->next_newline = TRUE;
+ return in->token;
+ }
+ }
+ break;
+
+ /*
+ * Comments:
+ *
+ * Attempt to allow and filter out partial-line comments.
+ *
+ * This has not been thoroughly thought through. Probably
+ * there are broken border-cases. The whole tokenizing loop
+ * has not been written so that it could grow the buffer and
+ * refill it even if even we run out of input and we're not
+ * sure whehter a full token has been consumed... beware.
+ * To be sure, we bail out here if it looks like we exhausted
+ * the input buffer while skipping a comment. This needs to
+ * be thought through properly.
+ */
+ case MRP_START_COMMENT:
+ skip_rest_of_line(in);
+ if (in->out == in->in) {
+ mrp_log_error("%s:%d Exhausted input buffer while skipping "
+ "a comment.", in->file, in->line);
+ in->error = TRUE;
+ return NULL;
+ }
+ else {
+ p = in->out;
+ in->line++;
+ }
+ break;
+
+ default:
+ *q++ = *p++;
+ in->was_newline = FALSE;
+ }
+ }
+
+ if (in->fd == -1) {
+ *q = '\0';
+ in->out = p;
+ in->in = q;
+
+ return in->token;
+ }
+ else {
+ mrp_log_error("Input line %d of file %s exceeds allowed length.",
+ in->line, in->file);
+ return NULL;
+ }
+}
+
+
+/*
+ * bridging to valgrind
+ */
+
+static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
+ int saved_argc, char **saved_argv, char **envp)
+{
+#define VG_ARG(a) vg_argv[vg_argc++] = a
+ char *vg_argv[MAX_ARGS + 1];
+ int vg_argc, normal_offs, i;
+
+ vg_argc = 0;
+
+ /* set valgrind binary */
+ VG_ARG(vg_path ? (char *)vg_path : "/usr/bin/valgrind");
+
+ /* add valgrind arguments */
+ for (i = vg_offs; i < argc; i++)
+ VG_ARG(argv[i]);
+
+ /* save offset to normal argument list for fallback */
+ normal_offs = vg_argc;
+
+ /* add our binary and our arguments */
+ for (i = 0; i < saved_argc; i++)
+ vg_argv[vg_argc++] = saved_argv[i];
+
+ /* terminate argument list */
+ VG_ARG(NULL);
+
+ /* try executing through valgrind */
+ mrp_log_warning("Executing through valgrind (%s)...", vg_argv[0]);
+ execve(vg_argv[0], vg_argv, envp);
+
+ /* try falling back to normal execution */
+ mrp_log_error("Executing through valgrind failed (error %d: %s), "
+ "retrying without...", errno, strerror(errno));
+ execve(vg_argv[normal_offs], vg_argv + normal_offs, envp);
+
+ /* can't do either, so just give up */
+ mrp_log_error("Fallback to normal execution failed (error %d: %s).",
+ errno, strerror(errno));
+ exit(1);
+}