fbb701b93594c7d467b10a321488377b962db3b3
[profile/ivi/speech-recognition.git] / src / daemon / config.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <stdarg.h>
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39
40 #define _GNU_SOURCE
41 #include <getopt.h>
42
43 #include "srs/config.h"
44
45 #ifdef SYSTEMD_ENABLED
46 #    include <systemd/sd-daemon.h>
47 #endif
48
49 #include <murphy/common/mm.h>
50 #include <murphy/common/log.h>
51
52 #include "srs/daemon/context.h"
53 #include "srs/daemon/plugin.h"
54 #include "srs/daemon/config.h"
55
56 #ifndef PATH_MAX
57 #    define PATH_MAX 1024
58 #endif
59 #define MAX_ARGS 64
60
61 #define MAX_DEPTH   16
62 #define MAX_BLOCK   64
63 #define MAX_PREFIX 128
64
65 static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
66                      int saved_argc, char **saved_argv, char **envp);
67
68 static srs_cfg_t *find_config(srs_cfg_t *settings, const char *key);
69
70
71 static int  nblock = 0;
72 static int  prflen = 0;
73 static char blocks[MAX_DEPTH][MAX_BLOCK];
74 static char prefix[MAX_PREFIX];
75
76
77 /*
78  * command line processing
79  */
80
81 static void config_set_defaults(srs_context_t *srs, const char *bin)
82 {
83 #define CFG "speech-recognition.conf"
84     static char cfg_file[PATH_MAX], plugin_dir[PATH_MAX];
85     char *e;
86     int   l;
87
88     if ((e = strstr(bin, "/src/srs-daemon")) != NULL ||
89         (e = strstr(bin, "/src/.libs/lt-srs-daemon")) != NULL) {
90         static int     warned = 0;
91         mrp_log_mask_t saved;
92
93         if (!warned) {
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);
100             warned = 1;
101         }
102
103         l = e - bin;
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);
106
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;
112     }
113     else {
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;
118     }
119 }
120
121
122 static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
123 {
124     va_list        ap;
125     srs_context_t  srs;
126     const char    *cfg, *plg;
127
128     mrp_clear(&srs);
129     config_set_defaults(&srs, argv0);
130     cfg = srs.config_file;
131     plg = srs.plugin_dir;
132
133     if (fmt && *fmt) {
134         va_start(ap, fmt);
135         vprintf(fmt, ap);
136         va_end(ap);
137     }
138
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"
160 #endif
161 ,
162            argv0, cfg, plg);
163
164     if (exit_code < 0)
165         return;
166     else
167         exit(exit_code);
168 }
169
170
171 static int set_passed_sockets(srs_context_t *srs, const char *variables)
172 {
173 #ifdef SYSTEMD_ENABLED
174     const char *b, *e;
175     char        key[256], val[64];
176     int         nfd, i, n;
177     size_t      len;
178
179     nfd = sd_listen_fds(0);
180
181     if (nfd <= 0)
182         return nfd;
183
184     i = 0;
185     b = variables;
186     while (b && *b) {
187         while (*b == ',' || *b == ' ' || *b == '\t')
188             b++;
189
190         if (!*b)
191             return 0;
192
193         if (i >= nfd)
194             return 0;
195
196         if ((e = strchr(b, ',')) != NULL)
197             len = e - b;
198         else
199             len = strlen(b);
200
201         if (len >= sizeof(key)) {
202             errno = EOVERFLOW;
203             return -1;
204         }
205
206         strncpy(key, b, len);
207         key[len] = '\0';
208
209         n = snprintf(val, sizeof(val), "%d", SD_LISTEN_FDS_START + i);
210
211         if (n < 0 || n >= sizeof(val))
212             return -1;
213
214         srs_set_config(srs, key, val);
215
216         b = e;
217         i++;
218     }
219
220     return 0;
221 #else
222     errno = EOPNOTSUPP;
223     return -1;
224 #endif
225 }
226
227
228 static void config_load_plugins(srs_context_t *srs, char *plugins)
229 {
230     char name[PATH_MAX], *p, *n;
231     int  l;
232
233     p = plugins;
234     while (p && *p) {
235         while (*p == ' ')
236             p++;
237
238         n = strchr(p, ' ');
239
240         if (n != NULL) {
241             l = n - p;
242
243             if (l > (int)sizeof(name) - 1) {
244                 mrp_log_error("Plugin name '%*.*s' is too long.", l, l, p);
245                 exit(1);
246             }
247
248             strncpy(name, p, l);
249             name[l] = '\0';
250         }
251         else {
252             if (snprintf(name, sizeof(name), "%s", p) >= (int)sizeof(name)) {
253                 mrp_log_error("Plugin name '%s' is too long.", p);
254                 exit(1);
255             }
256         }
257
258         if (srs_create_plugin(srs, name) == NULL) {
259             mrp_log_error("Failed to load plugin '%s'.", name);
260             exit(1);
261         }
262
263         p = n ? n : NULL;
264     }
265 }
266
267
268 static void push_block(const char *block, int blen)
269 {
270     int plen;
271
272     if (nblock >= MAX_DEPTH) {
273         mrp_log_error("Too deeply nested configuration block: %s.%s",
274                       prefix, block);
275         exit(1);
276     }
277
278     if (blen >= MAX_BLOCK - 1) {
279         mrp_log_error("Too long block name '%s'.", block);
280         exit(1);
281     }
282
283     if (prflen + 1 + blen + 1 >= sizeof(prefix)) {
284         mrp_log_error("Too long nested block name '%s.%s'.", prefix, block);
285         exit(1);
286     }
287
288     strncpy(blocks[nblock], block, blen);
289     blocks[nblock][blen] = '\0';
290     if (nblock > 0)
291         prefix[prflen++] = '.';
292     strncpy(prefix + prflen, block, blen);
293     prefix[prflen + blen] = '\0';
294     nblock++;
295     prflen += blen;
296
297     mrp_debug("pushed block '%*.*s', prefix now '%s'", blen, blen, block,
298               prefix);
299 }
300
301
302 static void pop_block(void)
303 {
304     char *block;
305     int   blen;
306
307     if (nblock <= 0) {
308         mrp_log_error("Unbalanced block open ({) and close (}).");
309         exit(1);
310     }
311
312     block = blocks[--nblock];
313     blen  = strlen(block);
314
315     if (nblock > 0 && prflen < blen + 1) {
316         mrp_log_error("Internal error in nested block book-keeping.");
317         exit(1);
318     }
319
320     if (nblock > 0)
321         prflen -= blen + 1;
322     else
323         prflen = 0;
324     prefix[prflen] = '\0';
325
326     mrp_debug("popped block '%s', prefix now '%s'", block, prefix);
327 }
328
329
330 static void config_parse_settings(srs_context_t *srs, char *settings)
331 {
332     char   *key, *val, *next;
333     size_t  klen, vlen;
334     char    keybuf[128], valbuf[512];
335
336     while (*settings == ' ' || *settings == '\t')
337         settings++;
338
339     key = settings;
340
341     if (!strncmp(key, "load ", 5)) {
342         config_load_plugins(srs, key + 5);
343         return;
344     }
345
346     if (*key == '}') {
347         key++;
348
349         while (*key == ' ' || *key == '\t')
350             key++;
351
352         if (*key != '\0') {
353             mrp_log_error("Invalid block closing '%s'.", settings);
354             exit(1);
355         }
356
357         pop_block();
358         return;
359     }
360
361     while (key && *key) {
362         val  = strchr(key, '=');
363         next = strchr(key, ';');
364
365         if (next != NULL && val > next)
366             val = NULL;
367
368         if (val != NULL) {
369             klen = val - key;
370             val++;
371             vlen = next ? (size_t)(next - val) : strlen(val);
372         }
373         else {
374             val  = "true";
375             vlen = 4;
376             klen = next ? (size_t)(next - key) : strlen(key);
377         }
378
379         while (klen > 0 && key[klen - 1] == ' ')
380             klen--;
381         while (vlen > 0 && val[0] == ' ') {
382             val++;
383             vlen--;
384         }
385         while (vlen > 0 && val[vlen - 1] == ' ')
386             vlen--;
387
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);
392             exit(1);
393         }
394
395         if (vlen == 1 && val[0] == '{') {
396             push_block(key, klen);
397             return;
398         }
399
400         if (nblock > 0)
401             snprintf(keybuf, sizeof(keybuf), "%s.%*.*s", prefix,
402                      klen, klen, key);
403         else
404             snprintf(keybuf, sizeof(keybuf), "%*.*s", klen, klen, key);
405         strncpy(valbuf, val, vlen);
406         valbuf[vlen] = '\0';
407
408         mrp_debug("setting configuration variable %s=%s", keybuf, valbuf);
409         srs_set_config(srs, keybuf, valbuf);
410
411         key = next ? next + 1 : NULL;
412     }
413 }
414
415
416 static void config_parse_file(srs_context_t *srs, char *path)
417 {
418     FILE *fp;
419     char  line[1024], *p, *end;
420
421     fp = fopen(path, "r");
422
423     if (fp == NULL) {
424         printf("Failed to open configuration file '%s'.\n", path);
425         exit(1);
426     }
427
428     nblock = 0;
429     prflen = 0;
430
431     while ((p = fgets(line, sizeof(line), fp)) != NULL) {
432         while (*p == ' ' || *p == '\t')
433             p++;
434
435         if (*p == '#')
436             continue;
437
438         if ((end = strchr(p, '\n')) != NULL)
439             *end = '\0';
440
441         config_parse_settings(srs, p);
442     }
443
444     nblock = 0;
445     prflen = 0;
446
447     fclose(fp);
448 }
449
450
451 void config_parse_cmdline(srs_context_t *srs, int argc, char **argv,
452                           char **envp)
453 {
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' },
469         { NULL, 0, NULL, 0 }
470     };
471
472
473 #   define SAVE_ARG(a) do {                                     \
474         if (saved_argc >= MAX_ARGS)                             \
475             print_usage(argv[0], EINVAL,                        \
476                         "too many command line arguments");     \
477         else                                                    \
478             saved_argv[saved_argc++] = a;                       \
479     } while (0)
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];
483     int   saved_argc;
484
485     int   opt, help;
486
487     config_set_defaults(srs, argv[0]);
488     mrp_log_set_mask(srs->log_mask);
489     mrp_log_set_target(srs->log_target);
490
491     saved_argc = 0;
492     saved_argv[saved_argc++] = argv[0];
493
494     help = FALSE;
495
496     while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
497         switch (opt) {
498         case 'c':
499             SAVE_OPTARG("-c", optarg);
500             srs->config_file = optarg;
501             config_parse_file(srs, optarg);
502             break;
503
504         case 'P':
505             SAVE_OPTARG("-P", optarg);
506             srs->plugin_dir = optarg;
507             break;
508
509         case 'L':
510             SAVE_OPTARG("-L", optarg);
511             if (srs_create_plugin(srs, optarg) == NULL) {
512                 mrp_log_error("Failed to load plugin '%s'.", optarg);
513                 exit(1);
514             }
515             break;
516
517         case 'v':
518             SAVE_OPT("-v");
519             srs->log_mask <<= 1;
520             srs->log_mask  |= 1;
521             mrp_log_set_mask(srs->log_mask);
522             break;
523
524         case 'l':
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);
529             else
530                 mrp_log_set_mask(srs->log_mask);
531             break;
532
533         case 't':
534             SAVE_OPTARG("-t", optarg);
535             srs->log_target = optarg;
536             break;
537
538         case 's':
539             SAVE_OPTARG("-s", optarg);
540             nblock = 0;
541             prflen = 0;
542             config_parse_settings(srs, optarg);
543             nblock = 0;
544             prflen = 0;
545             break;
546
547         case 'd':
548             SAVE_OPTARG("-d", optarg);
549             srs->log_mask |= MRP_LOG_MASK_DEBUG;
550             mrp_debug_set_config(optarg);
551             mrp_debug_enable(TRUE);
552             break;
553
554         case 'D':
555             SAVE_OPT("-D");
556             printf("Known debug sites:\n");
557             mrp_debug_dump_sites(stdout, 4);
558             exit(0);
559             break;
560
561         case 'f':
562             SAVE_OPT("-f");
563             srs->foreground = TRUE;
564             break;
565
566         case 'V':
567             valgrind(optarg, argc, argv, optind, saved_argc, saved_argv, envp);
568             break;
569
570 #ifdef SYSTEMD_ENABLED
571         case 'S':
572             SAVE_OPTARG("-S", optarg);
573             set_passed_sockets(srs, optarg);
574             break;
575 #endif
576
577         case 'h':
578             SAVE_OPT("-h");
579             help++;
580             break;
581
582         default:
583             print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
584         }
585     }
586
587     if (help) {
588         print_usage(argv[0], -1, "");
589         exit(0);
590     }
591
592 }
593
594
595 /*
596  * configuration setting processing
597  *
598  * Format of a configuration entry is
599  *    setting: <key> = <value> | <key> | load <plugin>
600  *    entry: setting | setting ; entry
601  */
602
603 static srs_cfg_t *find_config(srs_cfg_t *settings, const char *key)
604 {
605     if (settings != NULL) {
606         while (settings->key != NULL) {
607             if (!strcmp(settings->key, key))
608                 return settings;
609             else
610                 settings++;
611         }
612     }
613
614     return NULL;
615 }
616
617
618 const char *srs_get_string_config(srs_cfg_t *settings, const char *key,
619                                   const char *defval)
620 {
621     srs_cfg_t *cfg = find_config(settings, key);
622
623     if (cfg != NULL) {
624         cfg->used = TRUE;
625
626         return cfg->value;
627     }
628     else
629         return defval;
630 }
631
632
633 int srs_get_bool_config(srs_cfg_t *settings, const char *key, int defval)
634 {
635     srs_cfg_t *cfg = find_config(settings, key);
636
637     if (cfg != NULL) {
638         cfg->used = TRUE;
639
640         if (!strcasecmp(cfg->value, "true"))
641             return TRUE;
642         else if (!strcasecmp(cfg->value, "false"))
643             return FALSE;
644
645         mrp_log_error("Value '%s' for key '%s' is not a boolean.",
646                       cfg->value, cfg->key);
647         exit(1);
648     }
649
650     return defval;
651 }
652
653
654 int32_t srs_get_int32_config(srs_cfg_t *settings, const char *key,
655                              int32_t defval)
656 {
657     srs_cfg_t *cfg = find_config(settings, key);
658     int32_t    val;
659     char      *end;
660
661     if (cfg != NULL) {
662         cfg->used = TRUE;
663
664         val = (int32_t)strtol(cfg->value, &end, 0);
665
666         if (end && !*end)
667             return val;
668         else {
669             mrp_log_error("Value '%s' for key '%s' is not an int32.",
670                           cfg->value, cfg->key);
671             exit(1);
672         }
673     }
674
675     return defval;
676 }
677
678
679 uint32_t srs_get_uint32_config(srs_cfg_t *settings, const char *key,
680                                uint32_t defval)
681 {
682     srs_cfg_t *cfg = find_config(settings, key);
683     uint32_t   val;
684     char      *end;
685
686     if (cfg != NULL) {
687         cfg->used = TRUE;
688
689         val = (uint32_t)strtoul(cfg->value, &end, 0);
690
691         if (end && !*end)
692             return val;
693         else {
694             mrp_log_error("Value '%s' for key '%s' is not an uint32.",
695                           cfg->value, cfg->key);
696             exit(1);
697         }
698     }
699
700     return defval;
701 }
702
703
704 int srs_collect_config(srs_cfg_t *settings, const char *prefix,
705                        srs_cfg_t **matching)
706 {
707     srs_cfg_t *m = NULL;
708     int        n = 0;
709     size_t     osize, nsize, l;
710
711     if (settings == NULL)
712         goto out;
713
714     l = strlen(prefix);
715
716     while (settings->key != NULL) {
717         if (!strncmp(settings->key, prefix, l)) {
718             osize = sizeof(*m) *  n;
719             nsize = sizeof(*m) * (n + 1);
720
721             if (!mrp_reallocz(m, osize, nsize))
722                 goto fail;
723
724             m[n].key   = mrp_strdup(settings->key);
725             m[n].value = mrp_strdup(settings->value);
726
727             if (m[n].key == NULL || m[n].value == NULL) {
728                 n++;
729                 goto fail;
730             }
731
732             n++;
733         }
734
735         settings++;
736     }
737
738  out:
739     if (m != NULL) {
740         osize = sizeof(*m) *  n;
741         nsize = sizeof(*m) * (n + 1);
742
743         if (!mrp_reallocz(m, osize, nsize))
744             goto fail;
745     }
746
747     *matching = m;
748
749     return n;
750
751  fail:
752     while (n >= 0) {
753         mrp_free(m[n].key);
754         mrp_free(m[n].value);
755     }
756
757     mrp_free(m);
758
759     *matching = NULL;
760     return -1;
761 }
762
763
764 void srs_free_config(srs_cfg_t *settings)
765 {
766     srs_cfg_t *s;
767
768     if (settings != NULL) {
769         for (s = settings; s->key != NULL; s++) {
770             mrp_free(s->key);
771             mrp_free(s->value);
772         }
773
774         mrp_free(settings);
775     }
776 }
777
778
779 void srs_set_config(srs_context_t *srs, const char *key, const char *value)
780 {
781     srs_cfg_t *var;
782     size_t     osize, nsize, diff;
783
784     var = find_config(srs->settings, key);
785
786     if (var == NULL) {
787         diff  = srs->nsetting == 0 ? 2 : 1;
788         osize = sizeof(*srs->settings) *  srs->nsetting;
789         nsize = sizeof(*srs->settings) * (srs->nsetting + diff);
790
791         if (!mrp_reallocz(srs->settings, osize, nsize))
792             goto nomem;
793
794         var = srs->settings + srs->nsetting++;
795     }
796     else {
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);
800
801         mrp_free(var->key);
802         mrp_free(var->value);
803         var->key = var->value = NULL;
804     }
805
806     var->key   = mrp_strdup(key);
807     var->value = mrp_strdup(value);
808
809     if (var->key == NULL || var->value == NULL) {
810     nomem:
811         mrp_log_error("Failed to allocate configuration variable %s=%s.",
812                       key, value);
813         exit(1);
814     }
815 }
816
817
818 /*
819  * bridging to valgrind
820  */
821
822 static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
823                      int saved_argc, char **saved_argv, char **envp)
824 {
825 #define VG_ARG(a) vg_argv[vg_argc++] = a
826     char *vg_argv[MAX_ARGS + 1];
827     int   vg_argc, normal_offs, i;
828
829     vg_argc = 0;
830
831     /* set valgrind binary */
832     VG_ARG(vg_path ? (char *)vg_path : "/usr/bin/valgrind");
833
834     /* add valgrind arguments */
835     for (i = vg_offs; i < argc; i++)
836         VG_ARG(argv[i]);
837
838     /* save offset to normal argument list for fallback */
839     normal_offs = vg_argc;
840
841     /* add our binary and our arguments */
842     for (i = 0; i < saved_argc; i++)
843         vg_argv[vg_argc++] = saved_argv[i];
844
845     /* terminate argument list */
846     VG_ARG(NULL);
847
848     /* try executing through valgrind */
849     mrp_log_warning("Executing through valgrind (%s)...", vg_argv[0]);
850     execve(vg_argv[0], vg_argv, envp);
851
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);
856
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));
860     exit(1);
861 }