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 int 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;
94 saved = mrp_log_set_mask(MRP_LOG_MASK_WARNING);
95 mrp_log_warning("***");
96 mrp_log_warning("*** Looks like we are run from the source tree.");
97 mrp_log_warning("*** Runtime defaults will be set accordingly...");
98 mrp_log_warning("***");
99 mrp_log_set_mask(saved);
104 snprintf(cfg_file, sizeof(cfg_file), "%*.*s/%s", l, l, bin, CFG);
105 snprintf(plugin_dir, sizeof(plugin_dir), "%*.*s/src/.libs", l, l, bin);
107 srs->config_file = cfg_file;
108 srs->plugin_dir = plugin_dir;
109 srs->log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
110 srs->log_target = MRP_LOG_TO_STDERR;
111 srs->foreground = TRUE;
114 srs->config_file = SRS_DEFAULT_CONFIG_FILE;
115 srs->plugin_dir = SRS_DEFAULT_PLUGIN_DIR;
116 srs->log_mask = MRP_LOG_MASK_ERROR;
117 srs->log_target = MRP_LOG_TO_STDERR;
122 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
126 const char *cfg, *plg;
129 config_set_defaults(&srs, argv0);
130 cfg = srs.config_file;
131 plg = srs.plugin_dir;
139 printf("usage: %s [options]\n\n"
140 "The possible options are:\n"
141 " -c, --config-file=PATH main configuration file to use\n"
142 " The default configuration file is '%s'.\n"
143 " -P, --plugin-dir=PATH use DIR to search for plugins\n"
144 " The default plugin directory is '%s'.\n"
145 " -L, --load-plugin=NAME load the given plugin\n"
146 " -s, --set=SETTINGS.\n"
147 " SETTINGS is of the format key1=var1[,key2=var2...]\n"
148 " -t, --log-target=TARGET log target to use\n"
149 " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
150 " -l, --log-level=LEVELS logging level to use\n"
151 " LEVELS is a comma separated list of info, error and warning\n"
152 " -v, --verbose increase logging verbosity\n"
153 " -d, --debug enable given debug configuration\n"
154 " -D, --list-debug list known debug sites\n"
155 " -f, --foreground don't daemonize\n"
156 " -h, --help show help on usage\n"
157 " -V, --valgrind[=VALGRIND-PATH] try to run under valgrind\n"
158 #ifdef SYSTEMD_ENABLED
159 " -S, --sockets=var1[,var2...] set sockets in by systemd\n"
171 static int set_passed_sockets(srs_context_t *srs, const char *variables)
173 #ifdef SYSTEMD_ENABLED
175 char key[256], val[64];
179 nfd = sd_listen_fds(0);
187 while (*b == ',' || *b == ' ' || *b == '\t')
196 if ((e = strchr(b, ',')) != NULL)
201 if (len >= sizeof(key)) {
206 strncpy(key, b, len);
209 n = snprintf(val, sizeof(val), "%d", SD_LISTEN_FDS_START + i);
211 if (n < 0 || n >= sizeof(val))
214 srs_set_config(srs, key, val);
228 static void config_load_plugins(srs_context_t *srs, char *plugins)
230 char name[PATH_MAX], *p, *n;
243 if (l > (int)sizeof(name) - 1) {
244 mrp_log_error("Plugin name '%*.*s' is too long.", l, l, p);
252 if (snprintf(name, sizeof(name), "%s", p) >= (int)sizeof(name)) {
253 mrp_log_error("Plugin name '%s' is too long.", p);
258 if (srs_create_plugin(srs, name) == NULL) {
259 mrp_log_error("Failed to load plugin '%s'.", name);
268 static void push_block(const char *block, int blen)
272 if (nblock >= MAX_DEPTH) {
273 mrp_log_error("Too deeply nested configuration block: %s.%s",
278 if (blen >= MAX_BLOCK - 1) {
279 mrp_log_error("Too long block name '%s'.", block);
283 if (prflen + 1 + blen + 1 >= sizeof(prefix)) {
284 mrp_log_error("Too long nested block name '%s.%s'.", prefix, block);
288 strncpy(blocks[nblock], block, blen);
289 blocks[nblock][blen] = '\0';
291 prefix[prflen++] = '.';
292 strncpy(prefix + prflen, block, blen);
293 prefix[prflen + blen] = '\0';
297 mrp_debug("pushed block '%*.*s', prefix now '%s'", blen, blen, block,
302 static void pop_block(void)
308 mrp_log_error("Unbalanced block open ({) and close (}).");
312 block = blocks[--nblock];
313 blen = strlen(block);
315 if (nblock > 0 && prflen < blen + 1) {
316 mrp_log_error("Internal error in nested block book-keeping.");
324 prefix[prflen] = '\0';
326 mrp_debug("popped block '%s', prefix now '%s'", block, prefix);
330 static void config_parse_settings(srs_context_t *srs, char *settings)
332 char *key, *val, *next;
334 char keybuf[128], valbuf[512];
336 while (*settings == ' ' || *settings == '\t')
341 if (!strncmp(key, "load ", 5)) {
342 config_load_plugins(srs, key + 5);
349 while (*key == ' ' || *key == '\t')
353 mrp_log_error("Invalid block closing '%s'.", settings);
361 while (key && *key) {
362 val = strchr(key, '=');
363 next = strchr(key, ';');
365 if (next != NULL && val > next)
371 vlen = next ? (size_t)(next - val) : strlen(val);
376 klen = next ? (size_t)(next - key) : strlen(key);
379 while (klen > 0 && key[klen - 1] == ' ')
381 while (vlen > 0 && val[0] == ' ') {
385 while (vlen > 0 && val[vlen - 1] == ' ')
388 if (klen + prflen >= sizeof(keybuf) || vlen >= sizeof(valbuf)) {
389 mrp_log_error("Configuration setting %*.*s = %*.*s too long.",
390 (int)klen, (int)klen, key,
391 (int)vlen, (int)vlen, val);
395 if (vlen == 1 && val[0] == '{') {
396 push_block(key, klen);
401 snprintf(keybuf, sizeof(keybuf), "%s.%*.*s", prefix,
404 snprintf(keybuf, sizeof(keybuf), "%*.*s", klen, klen, key);
405 strncpy(valbuf, val, vlen);
408 mrp_debug("setting configuration variable %s=%s", keybuf, valbuf);
409 srs_set_config(srs, keybuf, valbuf);
411 key = next ? next + 1 : NULL;
416 static void config_parse_file(srs_context_t *srs, char *path)
419 char line[1024], *p, *end;
421 fp = fopen(path, "r");
424 printf("Failed to open configuration file '%s'.\n", path);
431 while ((p = fgets(line, sizeof(line), fp)) != NULL) {
432 while (*p == ' ' || *p == '\t')
438 if ((end = strchr(p, '\n')) != NULL)
441 config_parse_settings(srs, p);
451 void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
454 # define OPTIONS "c:P:L:l:t:B:s:fvd:DhS:V"
455 struct option options[] = {
456 { "config-file" , required_argument, NULL, 'c' },
457 { "plugin-dir" , required_argument, NULL, 'P' },
458 { "load-plugin" , required_argument, NULL, 'L' },
459 { "log-level" , required_argument, NULL, 'l' },
460 { "log-target" , required_argument, NULL, 't' },
461 { "set" , required_argument, NULL, 's' },
462 { "verbose" , optional_argument, NULL, 'v' },
463 { "debug" , required_argument, NULL, 'd' },
464 { "list-debug" , no_argument , NULL, 'D' },
465 { "foreground" , no_argument , NULL, 'f' },
466 { "valgrind" , optional_argument, NULL, 'V' },
467 { "sockets" , required_argument, NULL, 'S' },
468 { "help" , no_argument , NULL, 'h' },
473 # define SAVE_ARG(a) do { \
474 if (saved_argc >= MAX_ARGS) \
475 print_usage(argv[0], EINVAL, \
476 "too many command line arguments"); \
478 saved_argv[saved_argc++] = a; \
480 # define SAVE_OPT(o) SAVE_ARG(o)
481 # define SAVE_OPTARG(o, a) SAVE_ARG(o); SAVE_ARG(a)
482 char *saved_argv[MAX_ARGS];
487 config_set_defaults(srs, argv[0]);
488 mrp_log_set_mask(srs->log_mask);
489 mrp_log_set_target(srs->log_target);
492 saved_argv[saved_argc++] = argv[0];
496 while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
499 SAVE_OPTARG("-c", optarg);
500 srs->config_file = optarg;
501 config_parse_file(srs, optarg);
505 SAVE_OPTARG("-P", optarg);
506 srs->plugin_dir = optarg;
510 SAVE_OPTARG("-L", optarg);
511 if (srs_create_plugin(srs, optarg) == NULL) {
512 mrp_log_error("Failed to load plugin '%s'.", optarg);
521 mrp_log_set_mask(srs->log_mask);
525 SAVE_OPTARG("-l", optarg);
526 srs->log_mask = mrp_log_parse_levels(optarg);
527 if (srs->log_mask < 0)
528 print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
530 mrp_log_set_mask(srs->log_mask);
534 SAVE_OPTARG("-t", optarg);
535 srs->log_target = optarg;
539 SAVE_OPTARG("-s", optarg);
542 config_parse_settings(srs, optarg);
548 SAVE_OPTARG("-d", optarg);
549 srs->log_mask |= MRP_LOG_MASK_DEBUG;
550 mrp_debug_set_config(optarg);
551 mrp_debug_enable(TRUE);
556 printf("Known debug sites:\n");
557 mrp_debug_dump_sites(stdout, 4);
563 srs->foreground = TRUE;
567 valgrind(optarg, argc, argv, optind, saved_argc, saved_argv, envp);
570 #ifdef SYSTEMD_ENABLED
572 SAVE_OPTARG("-S", optarg);
573 set_passed_sockets(srs, optarg);
583 print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
588 print_usage(argv[0], -1, "");
596 * configuration setting processing
598 * Format of a configuration entry is
599 * setting: <key> = <value> | <key> | load <plugin>
600 * entry: setting | setting ; entry
603 static srs_cfg_t *find_config(srs_cfg_t *settings, const char *key)
605 if (settings != NULL) {
606 while (settings->key != NULL) {
607 if (!strcmp(settings->key, key))
618 const char *srs_get_string_config(srs_cfg_t *settings, const char *key,
621 srs_cfg_t *cfg = find_config(settings, key);
633 int srs_get_bool_config(srs_cfg_t *settings, const char *key, int defval)
635 srs_cfg_t *cfg = find_config(settings, key);
640 if (!strcasecmp(cfg->value, "true"))
642 else if (!strcasecmp(cfg->value, "false"))
645 mrp_log_error("Value '%s' for key '%s' is not a boolean.",
646 cfg->value, cfg->key);
654 int32_t srs_get_int32_config(srs_cfg_t *settings, const char *key,
657 srs_cfg_t *cfg = find_config(settings, key);
664 val = (int32_t)strtol(cfg->value, &end, 0);
669 mrp_log_error("Value '%s' for key '%s' is not an int32.",
670 cfg->value, cfg->key);
679 uint32_t srs_get_uint32_config(srs_cfg_t *settings, const char *key,
682 srs_cfg_t *cfg = find_config(settings, key);
689 val = (uint32_t)strtoul(cfg->value, &end, 0);
694 mrp_log_error("Value '%s' for key '%s' is not an uint32.",
695 cfg->value, cfg->key);
704 int srs_collect_config(srs_cfg_t *settings, const char *prefix,
705 srs_cfg_t **matching)
709 size_t osize, nsize, l;
711 if (settings == NULL)
716 while (settings->key != NULL) {
717 if (!strncmp(settings->key, prefix, l)) {
718 osize = sizeof(*m) * n;
719 nsize = sizeof(*m) * (n + 1);
721 if (!mrp_reallocz(m, osize, nsize))
724 m[n].key = mrp_strdup(settings->key);
725 m[n].value = mrp_strdup(settings->value);
727 if (m[n].key == NULL || m[n].value == NULL) {
740 osize = sizeof(*m) * n;
741 nsize = sizeof(*m) * (n + 1);
743 if (!mrp_reallocz(m, osize, nsize))
754 mrp_free(m[n].value);
764 void srs_free_config(srs_cfg_t *settings)
768 if (settings != NULL) {
769 for (s = settings; s->key != NULL; s++) {
779 void srs_set_config(srs_context_t *srs, const char *key, const char *value)
782 size_t osize, nsize, diff;
784 var = find_config(srs->settings, key);
787 diff = srs->nsetting == 0 ? 2 : 1;
788 osize = sizeof(*srs->settings) * srs->nsetting;
789 nsize = sizeof(*srs->settings) * (srs->nsetting + diff);
791 if (!mrp_reallocz(srs->settings, osize, nsize))
794 var = srs->settings + srs->nsetting++;
797 mrp_log_warning("Overwriting configuration setting '%s = %s'",
798 var->key, var->value);
799 mrp_log_warning("with new setting '%s = %s'", key, value);
802 mrp_free(var->value);
803 var->key = var->value = NULL;
806 var->key = mrp_strdup(key);
807 var->value = mrp_strdup(value);
809 if (var->key == NULL || var->value == NULL) {
811 mrp_log_error("Failed to allocate configuration variable %s=%s.",
819 * bridging to valgrind
822 static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
823 int saved_argc, char **saved_argv, char **envp)
825 #define VG_ARG(a) vg_argv[vg_argc++] = a
826 char *vg_argv[MAX_ARGS + 1];
827 int vg_argc, normal_offs, i;
831 /* set valgrind binary */
832 VG_ARG(vg_path ? (char *)vg_path : "/usr/bin/valgrind");
834 /* add valgrind arguments */
835 for (i = vg_offs; i < argc; i++)
838 /* save offset to normal argument list for fallback */
839 normal_offs = vg_argc;
841 /* add our binary and our arguments */
842 for (i = 0; i < saved_argc; i++)
843 vg_argv[vg_argc++] = saved_argv[i];
845 /* terminate argument list */
848 /* try executing through valgrind */
849 mrp_log_warning("Executing through valgrind (%s)...", vg_argv[0]);
850 execve(vg_argv[0], vg_argv, envp);
852 /* try falling back to normal execution */
853 mrp_log_error("Executing through valgrind failed (error %d: %s), "
854 "retrying without...", errno, strerror(errno));
855 execve(vg_argv[normal_offs], vg_argv + normal_offs, envp);
857 /* can't do either, so just give up */
858 mrp_log_error("Fallback to normal execution failed (error %d: %s).",
859 errno, strerror(errno));