vddk: advise user on obscure thumbprint mismatch error condition
[nbdkit.git] / wrapper.c
bloba034356ee085e9929860abdd029068dc5e54ac30
1 /* nbdkit
2 * Copyright (C) 2017-2020 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 /*------------------------------------------------------------
34 * This is not nbdkit. This is a wrapper which lets you run nbdkit
35 * from the source directory without installing nbdkit.
37 * You can use either:
39 * ./nbdkit file [arg=value] [arg=value] ...
41 * or:
43 * /path/to/nbdkit file [arg=value] [arg=value] ...
45 * or you can set $PATH to include the nbdkit source directory and run
46 * the bare "nbdkit" command without supplying the full path.
48 * The wrapper modifies the bare plugin name (eg. "file") to be the
49 * full path to the locally compiled plugin. If you don't use this
50 * program and run server/nbdkit directly then it will pick up the
51 * globally installed plugins which is usually not what you want.
53 * This program is also used to run the tests (make check).
55 * You can enable valgrind by setting NBDKIT_VALGRIND=1 (this
56 * is mainly used by the internal tests).
58 * You can enable debugging by setting NBDKIT_GDB=1
59 *------------------------------------------------------------
62 #include <config.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <stdbool.h>
67 #include <stdarg.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <getopt.h>
71 #include <limits.h>
72 #include <time.h>
74 #include "const-string-vector.h"
75 #include "options.h"
76 #include "windows-compat.h"
77 #include "utils.h"
79 /* Plugins written in scripting languages need to be rewritten on the
80 * command line in a different way from plugins written in C. So we
81 * have to list those here, and return the language plugin needed to
82 * run them.
84 static bool
85 is_script_plugin (const char *name, const char **language)
87 if (strcmp (name, "example4") == 0) {
88 *language = "perl";
89 return true;
91 if (strcmp (name, "S3") == 0) {
92 *language = "python";
93 return true;
95 return false;
98 /* Construct an array of parameters passed through to real nbdkit. */
99 static const_string_vector cmd;
101 static void
102 passthru (const char *s)
104 if (const_string_vector_append (&cmd, s) == -1)
105 abort ();
108 static void __attribute__((format (printf, 1, 2)))
109 passthru_format (const char *fs, ...)
111 va_list args;
112 char *str;
114 va_start (args, fs);
115 if (vasprintf (&str, fs, args) == -1)
116 abort ();
117 va_end (args);
118 passthru (str);
121 static void
122 end_passthru (void)
124 passthru (NULL);
127 static void
128 print_command (void)
130 size_t i;
132 if (cmd.len > 0)
133 shell_quote (cmd.ptr[0], stderr);
134 for (i = 1; i < cmd.len && cmd.ptr[i] != NULL; ++i) {
135 fputc (' ', stderr);
136 shell_quote (cmd.ptr[i], stderr);
138 fputc ('\n', stderr);
141 #ifdef WIN32
142 /* Windows behaviour of _spawnvp is completely insane:
143 * https://stackoverflow.com/questions/4146980/how-to-avoid-space-splitting-and-quote-removal-with-spawnvp
144 * See also glib sources where it's described as "really weird".
146 static const char *
147 quote_string_for_spawn (const char *str)
149 size_t i, len, pb;
150 char *p, *ret = (char *) str;
152 if (*str == 0 || strchr (str, ' ') || strchr (str, '\t')) {
153 len = strlen (str);
155 p = ret = malloc (2 + len*2 + 1);
156 if (ret == NULL) {
157 perror ("malloc");
158 exit (EXIT_FAILURE);
161 *p++ = '"';
162 pb = 0;
163 for (i = 0; i < len; ++i) {
164 switch (str[i]) {
165 case '"':
166 *p++ = '\\';
167 for (; pb > 0; --pb)
168 *p++ = '\\';
169 *p++ = '"';
170 break;
171 case '\\':
172 pb++;
173 *p++ = str[i];
174 break;
175 default:
176 pb = 0;
177 *p++ = str[i];
180 for (; pb > 0; --pb)
181 *p++ = '\\';
182 *p++ = '"';
183 *p++ = '\0';
186 /* We never free these strings. */
187 return ret;
189 #endif /* WIN32 */
192 main (int argc, char *argv[])
194 bool verbose = false;
195 char *s;
196 int r;
198 #ifndef WIN32
199 /* If NBDKIT_VALGRIND=1 is set in the environment, then we run the
200 * program under valgrind. This is used by the tests. Similarly if
201 * NBDKIT_GDB=1 is set, we run the program under GDB, useful during
202 * development.
204 s = getenv ("NBDKIT_VALGRIND");
205 if (s && strcmp (s, "1") == 0) {
206 passthru (VALGRIND);
207 passthru ("--vgdb=no");
208 passthru ("--leak-check=full");
209 passthru ("--show-leak-kinds=all");
210 passthru ("--error-exitcode=119");
211 passthru_format ("--suppressions=%s/valgrind/suppressions", builddir);
212 passthru ("--trace-children=no");
213 passthru ("--run-libc-freeres=no");
214 passthru ("--num-callers=100");
215 /* This is a temporary workaround until RHBZ#1662656 is fixed: */
216 passthru ("--read-inline-info=no");
218 /* Don't invoke malloc debugging when we are valgrinding because
219 * it duplicates work done by valgrind and might even hide issues.
221 * Originally this was a workaround for:
222 * https://sourceware.org/bugzilla/show_bug.cgi?id=28256
224 unsetenv ("GLIBC_TUNABLES");
226 else {
227 s = getenv ("NBDKIT_GDB");
228 if (s && strcmp (s, "1") == 0) {
229 passthru ("gdb");
230 passthru ("--args");
233 #endif
235 /* Needed for plugins written in OCaml. */
236 #ifndef WIN32
237 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
238 #else
239 #define LD_LIBRARY_PATH "PATH"
240 #endif
241 s = getenv (LD_LIBRARY_PATH);
242 if (s)
243 r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s);
244 else
245 r = asprintf (&s, "%s/plugins/ocaml/.libs", builddir);
246 if (r < 0) {
247 perror ("asprintf");
248 exit (EXIT_FAILURE);
250 setenv (LD_LIBRARY_PATH, s, 1);
251 free (s);
252 s = getenv ("LIBRARY_PATH");
253 if (s)
254 r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s);
255 else
256 r = asprintf (&s, "%s/plugins/ocaml/.libs", builddir);
257 if (r < 0) {
258 perror ("asprintf");
259 exit (EXIT_FAILURE);
261 setenv ("LIBRARY_PATH", s, 1);
262 free (s);
264 /* Absolute path of the real nbdkit command. */
265 passthru_format ("%s/server/nbdkit" EXEEXT, builddir);
267 /* Option parsing. We don't really parse options here. We are only
268 * interested in which options have arguments and which need
269 * rewriting.
271 for (;;) {
272 int c;
273 int long_index = -1;
274 bool is_long_option;
276 c = getopt_long (argc, argv, short_options, long_options, &long_index);
277 if (c == -1)
278 break;
280 if (c == '?') /* getopt prints an error */
281 exit (EXIT_FAILURE);
283 /* long_index is only set if it's an actual long option. */
284 is_long_option = long_index >= 0;
286 /* Verbose is special because we will print the final command. */
287 if (c == 'v') {
288 verbose = true;
289 if (is_long_option)
290 passthru ("--verbose");
291 else
292 passthru ("-v");
294 /* Filters can be rewritten if they are a short name. */
295 else if (c == FILTER_OPTION) {
296 if (is_short_name (optarg))
297 passthru_format ("--filter="
298 "%s/filters/%s/.libs/nbdkit-%s-filter." SOEXT,
299 builddir, optarg, optarg);
300 else
301 passthru_format ("--filter=%s", optarg);
303 /* Any long option. */
304 else if (is_long_option) {
305 if (optarg) /* Long option which takes an argument. */
306 passthru_format ("--%s=%s", long_options[long_index].name, optarg);
307 else /* Long option which takes no argument. */
308 passthru_format ("--%s", long_options[long_index].name);
310 /* Any short option. */
311 else {
312 passthru_format ("-%c", c);
313 if (optarg)
314 passthru (optarg);
318 /* Are there any non-option arguments? */
319 if (optind < argc) {
320 /* Ensure any further parameters can never be parsed as options by
321 * real nbdkit.
323 passthru ("--");
325 /* The first non-option argument is the plugin name. If it is a
326 * short name then rewrite it.
328 if (is_short_name (argv[optind])) {
329 const char *language;
331 /* Plugins written in scripting languages. */
332 if (is_script_plugin (argv[optind], &language)) {
333 passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin." SOEXT,
334 builddir, language, language);
335 passthru_format ("%s/plugins/%s/nbdkit-%s-plugin",
336 builddir, argv[optind], argv[optind]);
338 /* Otherwise normal plugins written in C or other languages that
339 * compile to .so files.
341 else {
342 passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin." SOEXT,
343 builddir, argv[optind], argv[optind]);
345 ++optind;
348 /* Everything else is passed through without rewriting. */
349 while (optind < argc) {
350 passthru (argv[optind]);
351 ++optind;
355 end_passthru ();
356 if (verbose)
357 print_command ();
359 /* Run the final command. */
360 #ifndef WIN32
361 execvp (cmd.ptr[0], (char **) cmd.ptr);
362 perror (cmd.ptr[0]);
363 exit (EXIT_FAILURE);
364 #else /* WIN32 */
365 size_t i;
366 for (i = 1; cmd.ptr[i] != NULL; ++i)
367 cmd.ptr[i] = quote_string_for_spawn (cmd.ptr[i]);
368 r = _spawnvp (_P_WAIT, cmd.ptr[0], cmd.ptr);
369 if (r == -1) {
370 perror (cmd.ptr[0]);
371 exit (EXIT_FAILURE);
373 exit (r == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
374 #endif /* WIN32 */