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
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
33 /*------------------------------------------------------------
34 * This is not nbdkit. This is a wrapper which lets you run nbdkit
35 * from the source directory without installing nbdkit.
39 * ./nbdkit file [arg=value] [arg=value] ...
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 *------------------------------------------------------------
74 #include "const-string-vector.h"
76 #include "windows-compat.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
85 is_script_plugin (const char *name
, const char **language
)
87 if (strcmp (name
, "example4") == 0) {
91 if (strcmp (name
, "S3") == 0) {
98 /* Construct an array of parameters passed through to real nbdkit. */
99 static const_string_vector cmd
;
102 passthru (const char *s
)
104 if (const_string_vector_append (&cmd
, s
) == -1)
108 static void __attribute__ ((format (printf
, 1, 2)))
109 passthru_format (const char *fs
, ...)
115 if (vasprintf (&str
, fs
, args
) == -1)
133 shell_quote (cmd
.ptr
[0], stderr
);
134 for (i
= 1; i
< cmd
.len
&& cmd
.ptr
[i
] != NULL
; ++i
) {
136 shell_quote (cmd
.ptr
[i
], stderr
);
138 fputc ('\n', stderr
);
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".
147 quote_string_for_spawn (const char *str
)
150 char *p
, *ret
= (char *) str
;
152 if (*str
== 0 || strchr (str
, ' ') || strchr (str
, '\t')) {
155 p
= ret
= malloc (2 + len
*2 + 1);
163 for (i
= 0; i
< len
; ++i
) {
186 /* We never free these strings. */
192 main (int argc
, char *argv
[])
194 bool verbose
= false;
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
204 s
= getenv ("NBDKIT_VALGRIND");
205 if (s
&& strcmp (s
, "1") == 0) {
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");
227 s
= getenv ("NBDKIT_GDB");
228 if (s
&& strcmp (s
, "1") == 0) {
235 /* Needed for plugins written in OCaml. */
237 #define LD_LIBRARY_PATH "PATH"
238 #elif defined (__APPLE__)
239 #define LD_LIBRARY_PATH "DYLD_LIBRARY_PATH"
241 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
243 s
= getenv (LD_LIBRARY_PATH
);
245 r
= asprintf (&s
, "%s/plugins/ocaml/.libs:%s", builddir
, s
);
247 r
= asprintf (&s
, "%s/plugins/ocaml/.libs", builddir
);
252 setenv (LD_LIBRARY_PATH
, s
, 1);
254 s
= getenv ("LIBRARY_PATH");
256 r
= asprintf (&s
, "%s/plugins/ocaml/.libs:%s", builddir
, s
);
258 r
= asprintf (&s
, "%s/plugins/ocaml/.libs", builddir
);
263 setenv ("LIBRARY_PATH", s
, 1);
266 /* Absolute path of the real nbdkit command. */
267 passthru_format ("%s/server/nbdkit" EXEEXT
, builddir
);
269 /* Option parsing. We don't really parse options here. We are only
270 * interested in which options have arguments and which need
278 c
= getopt_long (argc
, argv
, short_options
, long_options
, &long_index
);
282 if (c
== '?') /* getopt prints an error */
285 /* long_index is only set if it's an actual long option. */
286 is_long_option
= long_index
>= 0;
288 /* Verbose is special because we will print the final command. */
292 passthru ("--verbose");
296 /* Filters can be rewritten if they are a short name. */
297 else if (c
== FILTER_OPTION
) {
298 if (is_short_name (optarg
))
299 passthru_format ("--filter="
300 "%s/filters/%s/.libs/nbdkit-%s-filter." SOEXT
,
301 builddir
, optarg
, optarg
);
303 passthru_format ("--filter=%s", optarg
);
305 /* Any long option. */
306 else if (is_long_option
) {
307 if (optarg
) /* Long option which takes an argument. */
308 passthru_format ("--%s=%s", long_options
[long_index
].name
, optarg
);
309 else /* Long option which takes no argument. */
310 passthru_format ("--%s", long_options
[long_index
].name
);
312 /* Any short option. */
314 passthru_format ("-%c", c
);
320 /* Are there any non-option arguments? */
322 /* Ensure any further parameters can never be parsed as options by
327 /* The first non-option argument is the plugin name. If it is a
328 * short name then rewrite it.
330 if (is_short_name (argv
[optind
])) {
331 const char *language
;
333 /* Plugins written in scripting languages. */
334 if (is_script_plugin (argv
[optind
], &language
)) {
335 passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin." SOEXT
,
336 builddir
, language
, language
);
337 passthru_format ("%s/plugins/%s/nbdkit-%s-plugin",
338 builddir
, argv
[optind
], argv
[optind
]);
340 /* Otherwise normal plugins written in C or other languages that
341 * compile to .so files.
344 passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin." SOEXT
,
345 builddir
, argv
[optind
], argv
[optind
]);
350 /* Everything else is passed through without rewriting. */
351 while (optind
< argc
) {
352 passthru (argv
[optind
]);
361 /* Run the final command. */
363 execvp (cmd
.ptr
[0], (char **) cmd
.ptr
);
368 for (i
= 1; cmd
.ptr
[i
] != NULL
; ++i
)
369 cmd
.ptr
[i
] = quote_string_for_spawn (cmd
.ptr
[i
]);
370 r
= _spawnvp (_P_WAIT
, cmd
.ptr
[0], cmd
.ptr
);
375 exit (r
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
);