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