2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include <sys/types.h>
43 #include "srs/config.h"
45 #ifdef SYSTEMD_ENABLED
46 # include <systemd/sd-daemon.h>
49 #include <murphy/common/mm.h>
50 #include <murphy/common/log.h>
52 #include "srs/daemon/context.h"
53 #include "srs/daemon/plugin.h"
54 #include "srs/daemon/config.h"
57 # define PATH_MAX 1024
63 #define MAX_PREFIX 128
65 static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
66 int saved_argc, char **saved_argv, char **envp);
68 static srs_cfg_t *find_config(srs_cfg_t *settings, const char *key);
71 static int nblock = 0;
72 static size_t prflen = 0;
73 static char blocks[MAX_DEPTH][MAX_BLOCK];
74 static char prefix[MAX_PREFIX];
78 * command line processing
81 static void config_set_defaults(srs_context_t *srs, const char *bin)
83 #define CFG "speech-recognition.conf"
84 static char cfg_file[PATH_MAX], plugin_dir[PATH_MAX];
88 if ((e = strstr(bin, "/src/srs-daemon")) != NULL ||
89 (e = strstr(bin, "/src/.libs/lt-srs-daemon")) != NULL) {
90 static int warned = 0;
93 mrp_log_mask_t saved = mrp_log_set_mask(MRP_LOG_MASK_WARNING);
94 mrp_log_warning("***");
95 mrp_log_warning("*** Looks like we are run from the source tree.");
96 mrp_log_warning("*** Runtime defaults will be set accordingly...");
97 mrp_log_warning("***");
98 mrp_log_set_mask(saved);
103 snprintf(cfg_file, sizeof(cfg_file), "%*.*s/%s", l, l, bin, CFG);
104 snprintf(plugin_dir, sizeof(plugin_dir), "%*.*s/src/.libs", l, l, bin);
106 srs->config_file = cfg_file;
107 srs->plugin_dir = plugin_dir;
108 srs->log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
109 srs->log_target = MRP_LOG_TO_STDERR;
110 srs->foreground = TRUE;
113 srs->config_file = SRS_DEFAULT_CONFIG_FILE;
114 srs->plugin_dir = SRS_DEFAULT_PLUGIN_DIR;
115 srs->log_mask = MRP_LOG_MASK_ERROR;
116 srs->log_target = MRP_LOG_TO_STDERR;
121 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
125 const char *cfg, *plg;
128 config_set_defaults(&srs, argv0);
129 cfg = srs.config_file;
130 plg = srs.plugin_dir;
138 printf("usage: %s [options]\n\n"
139 "The possible options are:\n"
140 " -c, --config-file=PATH main configuration file to use\n"
141 " The default configuration file is '%s'.\n"
142 " -P, --plugin-dir=PATH use DIR to search for plugins\n"
143 " The default plugin directory is '%s'.\n"
144 " -L, --load-plugin=NAME load the given plugin\n"
145 " -s, --set=SETTINGS.\n"
146 " SETTINGS is of the format key1=var1[,key2=var2...]\n"
147 " -t, --log-target=TARGET log target to use\n"
148 " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
149 " -l, --log-level=LEVELS logging level to use\n"
150 " LEVELS is a comma separated list of info, error and warning\n"
151 " -v, --verbose increase logging verbosity\n"
152 " -d, --debug enable given debug configuration\n"
153 " -D, --list-debug list known debug sites\n"
154 " -f, --foreground don't daemonize\n"
155 " -h, --help show help on usage\n"
156 " -V, --valgrind[=VALGRIND-PATH] try to run under valgrind\n"
157 #ifdef SYSTEMD_ENABLED
158 " -S, --sockets=var1[,var2...] set sockets in by systemd\n"
170 #ifdef SYSTEMD_ENABLED
171 static int set_passed_sockets(srs_context_t *srs, const char *variables)
174 char key[256], val[64];
178 nfd = sd_listen_fds(0);
186 while (*b == ',' || *b == ' ' || *b == '\t')
195 if ((e = strchr(b, ',')) != NULL)
200 if (len >= sizeof(key)) {
205 strncpy(key, b, len);
208 n = snprintf(val, sizeof(val), "%d", SD_LISTEN_FDS_START + i);
210 if (n < 0 || n >= (int)sizeof(val))
213 srs_set_config(srs, key, val);
224 static void config_load_plugins(srs_context_t *srs, char *plugins)
226 char name[PATH_MAX], *p, *n;
239 if (l > (int)sizeof(name) - 1) {
240 mrp_log_error("Plugin name '%*.*s' is too long.", l, l, p);
248 if (snprintf(name, sizeof(name), "%s", p) >= (int)sizeof(name)) {
249 mrp_log_error("Plugin name '%s' is too long.", p);
254 if (srs_create_plugin(srs, name) == NULL) {
255 mrp_log_error("Failed to load plugin '%s'.", name);
264 static void push_block(const char *block, int blen)
266 if (nblock >= MAX_DEPTH) {
267 mrp_log_error("Too deeply nested configuration block: %s.%s",
272 if (blen >= MAX_BLOCK - 1) {
273 mrp_log_error("Too long block name '%s'.", block);
277 if (prflen + 1 + blen + 1 >= sizeof(prefix)) {
278 mrp_log_error("Too long nested block name '%s.%s'.", prefix, block);
282 strncpy(blocks[nblock], block, blen);
283 blocks[nblock][blen] = '\0';
285 prefix[prflen++] = '.';
286 strncpy(prefix + prflen, block, blen);
287 prefix[prflen + blen] = '\0';
291 mrp_debug("pushed block '%*.*s', prefix now '%s'", blen, blen, block,
296 static void pop_block(void)
302 mrp_log_error("Unbalanced block open ({) and close (}).");
306 block = blocks[--nblock];
307 blen = strlen(block);
309 if (nblock > 0 && prflen < blen + 1) {
310 mrp_log_error("Internal error in nested block book-keeping.");
318 prefix[prflen] = '\0';
320 mrp_debug("popped block '%s', prefix now '%s'", block, prefix);
324 static void config_parse_settings(srs_context_t *srs, char *settings)
326 char *key, *val, *next;
328 char keybuf[128], valbuf[512];
330 while (*settings == ' ' || *settings == '\t')
335 if (!strncmp(key, "load ", 5)) {
336 config_load_plugins(srs, key + 5);
343 while (*key == ' ' || *key == '\t')
347 mrp_log_error("Invalid block closing '%s'.", settings);
355 while (key && *key) {
356 val = strchr(key, '=');
357 next = strchr(key, ';');
359 if (next != NULL && val > next)
365 vlen = next ? (size_t)(next - val) : strlen(val);
370 klen = next ? (size_t)(next - key) : strlen(key);
373 while (klen > 0 && key[klen - 1] == ' ')
375 while (vlen > 0 && val[0] == ' ') {
379 while (vlen > 0 && val[vlen - 1] == ' ')
382 if (klen + prflen >= sizeof(keybuf) || vlen >= sizeof(valbuf)) {
383 mrp_log_error("Configuration setting %*.*s = %*.*s too long.",
384 (int)klen, (int)klen, key,
385 (int)vlen, (int)vlen, val);
389 if (vlen == 1 && val[0] == '{') {
390 push_block(key, klen);
395 snprintf(keybuf, sizeof(keybuf), "%s.%*.*s", prefix,
396 (int)klen, (int)klen, key);
398 snprintf(keybuf, sizeof(keybuf), "%*.*s",
399 (int)klen, (int)klen, key);
400 strncpy(valbuf, val, vlen);
403 mrp_debug("setting configuration variable %s=%s", keybuf, valbuf);
404 srs_set_config(srs, keybuf, valbuf);
406 key = next ? next + 1 : NULL;
411 static void config_parse_file(srs_context_t *srs, char *path)
414 char line[1024], *p, *end;
416 fp = fopen(path, "r");
419 printf("Failed to open configuration file '%s'.\n", path);
426 while ((p = fgets(line, sizeof(line), fp)) != NULL) {
427 while (*p == ' ' || *p == '\t')
433 if ((end = strchr(p, '\n')) != NULL)
436 config_parse_settings(srs, p);
446 void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
449 # define OPTIONS "c:P:L:l:t:B:s:fvd:hS:V"
450 struct option options[] = {
451 { "config-file" , required_argument, NULL, 'c' },
452 { "plugin-dir" , required_argument, NULL, 'P' },
453 { "load-plugin" , required_argument, NULL, 'L' },
454 { "log-level" , required_argument, NULL, 'l' },
455 { "log-target" , required_argument, NULL, 't' },
456 { "set" , required_argument, NULL, 's' },
457 { "verbose" , optional_argument, NULL, 'v' },
458 { "debug" , required_argument, NULL, 'd' },
459 { "foreground" , no_argument , NULL, 'f' },
460 { "valgrind" , optional_argument, NULL, 'V' },
461 #ifdef SYSTEMD_ENABLED
462 { "sockets" , required_argument, NULL, 'S' },
464 { "help" , no_argument , NULL, 'h' },
469 # define SAVE_ARG(a) do { \
470 if (saved_argc >= MAX_ARGS) \
471 print_usage(argv[0], EINVAL, \
472 "too many command line arguments"); \
474 saved_argv[saved_argc++] = a; \
476 # define SAVE_OPT(o) SAVE_ARG(o)
477 # define SAVE_OPTARG(o, a) SAVE_ARG(o); SAVE_ARG(a)
478 char *saved_argv[MAX_ARGS];
483 config_set_defaults(srs, argv[0]);
484 mrp_log_set_mask(srs->log_mask);
485 mrp_log_set_target(srs->log_target);
488 saved_argv[saved_argc++] = argv[0];
492 while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
495 SAVE_OPTARG("-c", optarg);
496 srs->config_file = optarg;
497 config_parse_file(srs, optarg);
501 SAVE_OPTARG("-P", optarg);
502 srs->plugin_dir = optarg;
506 SAVE_OPTARG("-L", optarg);
507 if (srs_create_plugin(srs, optarg) == NULL) {
508 mrp_log_error("Failed to load plugin '%s'.", optarg);
517 mrp_log_set_mask(srs->log_mask);
521 SAVE_OPTARG("-l", optarg);
522 srs->log_mask = mrp_log_parse_levels(optarg);
523 if (srs->log_mask < 0)
524 print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
526 mrp_log_set_mask(srs->log_mask);
530 SAVE_OPTARG("-t", optarg);
531 srs->log_target = optarg;
535 SAVE_OPTARG("-s", optarg);
538 config_parse_settings(srs, optarg);
544 SAVE_OPTARG("-d", optarg);
545 srs->log_mask |= MRP_LOG_MASK_DEBUG;
546 mrp_debug_set_config(optarg);
547 mrp_debug_enable(TRUE);
552 srs->foreground = TRUE;
556 valgrind(optarg, argc, argv, optind, saved_argc, saved_argv, envp);
559 #ifdef SYSTEMD_ENABLED
561 SAVE_OPTARG("-S", optarg);
562 set_passed_sockets(srs, optarg);
572 print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
577 print_usage(argv[0], -1, "");
585 * configuration setting processing
587 * Format of a configuration entry is
588 * setting: <key> = <value> | <key> | load <plugin>
589 * entry: setting | setting ; entry
592 static srs_cfg_t *find_config(srs_cfg_t *settings, const char *key)
594 if (settings != NULL) {
595 while (settings->key != NULL) {
596 if (!strcmp(settings->key, key))
607 const char *srs_config_get_string(srs_cfg_t *settings, const char *key,
610 srs_cfg_t *cfg = find_config(settings, key);
622 int srs_config_get_bool(srs_cfg_t *settings, const char *key, int defval)
624 srs_cfg_t *cfg = find_config(settings, key);
629 if (!strcasecmp(cfg->value, "true"))
631 else if (!strcasecmp(cfg->value, "false"))
634 mrp_log_error("Value '%s' for key '%s' is not a boolean.",
635 cfg->value, cfg->key);
643 int32_t srs_config_get_int32(srs_cfg_t *settings, const char *key,
646 srs_cfg_t *cfg = find_config(settings, key);
653 val = (int32_t)strtol(cfg->value, &end, 0);
658 mrp_log_error("Value '%s' for key '%s' is not an int32.",
659 cfg->value, cfg->key);
668 uint32_t srs_config_get_uint32(srs_cfg_t *settings, const char *key,
671 srs_cfg_t *cfg = find_config(settings, key);
678 val = (uint32_t)strtoul(cfg->value, &end, 0);
683 mrp_log_error("Value '%s' for key '%s' is not an uint32.",
684 cfg->value, cfg->key);
693 int srs_config_collect(srs_cfg_t *settings, const char *prefix,
694 srs_cfg_t **matching)
698 size_t osize, nsize, l;
700 if (settings == NULL)
705 while (settings->key != NULL) {
706 if (!strncmp(settings->key, prefix, l)) {
707 osize = sizeof(*m) * n;
708 nsize = sizeof(*m) * (n + 1);
710 if (!mrp_reallocz(m, osize, nsize))
713 m[n].key = mrp_strdup(settings->key);
714 m[n].value = mrp_strdup(settings->value);
716 if (m[n].key == NULL || m[n].value == NULL) {
729 osize = sizeof(*m) * n;
730 nsize = sizeof(*m) * (n + 1);
732 if (!mrp_reallocz(m, osize, nsize))
743 mrp_free(m[n].value);
753 void srs_config_free(srs_cfg_t *settings)
757 if (settings != NULL) {
758 for (s = settings; s->key != NULL; s++) {
768 void srs_set_config(srs_context_t *srs, const char *key, const char *value)
771 size_t osize, nsize, diff;
773 var = find_config(srs->settings, key);
776 diff = srs->nsetting == 0 ? 2 : 1;
777 osize = sizeof(*srs->settings) * srs->nsetting;
778 nsize = sizeof(*srs->settings) * (srs->nsetting + diff);
780 if (!mrp_reallocz(srs->settings, osize, nsize))
783 var = srs->settings + srs->nsetting++;
786 mrp_log_warning("Overwriting configuration setting '%s = %s'",
787 var->key, var->value);
788 mrp_log_warning("with new setting '%s = %s'", key, value);
791 mrp_free(var->value);
792 var->key = var->value = NULL;
795 var->key = mrp_strdup(key);
796 var->value = mrp_strdup(value);
798 if (var->key == NULL || var->value == NULL) {
800 mrp_log_error("Failed to allocate configuration variable %s=%s.",
808 * bridging to valgrind
811 static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
812 int saved_argc, char **saved_argv, char **envp)
814 #define VG_ARG(a) vg_argv[vg_argc++] = a
815 char *vg_argv[MAX_ARGS + 1];
816 int vg_argc, normal_offs, i;
820 /* set valgrind binary */
821 VG_ARG(vg_path ? (char *)vg_path : "/usr/bin/valgrind");
823 /* add valgrind arguments */
824 for (i = vg_offs; i < argc; i++)
827 /* save offset to normal argument list for fallback */
828 normal_offs = vg_argc;
830 /* add our binary and our arguments */
831 for (i = 0; i < saved_argc; i++)
832 vg_argv[vg_argc++] = saved_argv[i];
834 /* terminate argument list */
837 /* try executing through valgrind */
838 mrp_log_warning("Executing through valgrind (%s)...", vg_argv[0]);
839 execve(vg_argv[0], vg_argv, envp);
841 /* try falling back to normal execution */
842 mrp_log_error("Executing through valgrind failed (error %d: %s), "
843 "retrying without...", errno, strerror(errno));
844 execve(vg_argv[normal_offs], vg_argv + normal_offs, envp);
846 /* can't do either, so just give up */
847 mrp_log_error("Fallback to normal execution failed (error %d: %s).",
848 errno, strerror(errno));