plugins: Wire up ocaml plugin support for NBD_INFO_INIT_STATE
[nbdkit/ericb.git] / wrapper.c
blob6aef81a1acb7828f90ac1f4624384acd2ea1ffcf
1 /* nbdkit
2 * Copyright (C) 2017-2019 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 "options.h"
75 #include "utils.h"
77 /* Construct an array of parameters passed through to real nbdkit. */
78 static const char **cmd;
79 static size_t len;
81 /* Plugins written in scripting languages (only Perl right now) need
82 * to be rewritten on the command line in a different way from plugins
83 * written in C. So we have to list those here.
85 static bool
86 is_perl_plugin (const char *name)
88 return strcmp (name, "example4") == 0 || strcmp (name, "tar") == 0;
91 static void
92 passthru (const char *s)
94 cmd = realloc (cmd, (len+1) * sizeof (const char *));
95 if (cmd == NULL)
96 abort ();
97 cmd[len] = s;
98 ++len;
101 static void __attribute__((format (printf, 1, 2)))
102 passthru_format (const char *fs, ...)
104 va_list args;
105 char *str;
107 va_start (args, fs);
108 if (vasprintf (&str, fs, args) == -1)
109 abort ();
110 va_end (args);
111 passthru (str);
114 static void
115 end_passthru (void)
117 passthru (NULL);
120 static void
121 print_command (void)
123 size_t i;
125 if (len > 0)
126 shell_quote (cmd[0], stderr);
127 for (i = 1; i < len && cmd[i] != NULL; ++i) {
128 fputc (' ', stderr);
129 shell_quote (cmd[i], stderr);
131 fputc ('\n', stderr);
135 main (int argc, char *argv[])
137 bool verbose = false;
138 char *s;
139 time_t t;
140 unsigned tu;
141 char ts[32];
142 int r;
144 /* If NBDKIT_VALGRIND=1 is set in the environment, then we run the
145 * program under valgrind. This is used by the tests. Similarly if
146 * NBDKIT_GDB=1 is set, we run the program under GDB, useful during
147 * development.
149 s = getenv ("NBDKIT_VALGRIND");
150 if (s && strcmp (s, "1") == 0) {
151 passthru (VALGRIND);
152 passthru ("--vgdb=no");
153 passthru ("--leak-check=full");
154 passthru ("--show-leak-kinds=all");
155 passthru ("--error-exitcode=119");
156 passthru_format ("--suppressions=%s/valgrind/suppressions", builddir);
157 passthru ("--trace-children=no");
158 passthru ("--run-libc-freeres=no");
159 passthru ("--num-callers=100");
160 /* This is a temporary workaround until RHBZ#1662656 is fixed: */
161 passthru ("--read-inline-info=no");
163 else {
164 s = getenv ("NBDKIT_GDB");
165 if (s && strcmp (s, "1") == 0) {
166 passthru ("gdb");
167 passthru ("--args");
171 /* Needed for plugins written in OCaml. */
172 s = getenv ("LD_LIBRARY_PATH");
173 if (s)
174 r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s);
175 else
176 r = asprintf (&s, "%s/plugins/ocaml/.libs", builddir);
177 if (r < 0) {
178 perror ("asprintf");
179 exit (EXIT_FAILURE);
181 setenv ("LD_LIBRARY_PATH", s, 1);
182 free (s);
183 s = getenv ("LIBRARY_PATH");
184 if (s)
185 r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s);
186 else
187 r = asprintf (&s, "%s/plugins/ocaml/.libs", builddir);
188 if (r < 0) {
189 perror ("asprintf");
190 exit (EXIT_FAILURE);
192 setenv ("LIBRARY_PATH", s, 1);
193 free (s);
195 /* Absolute path of the real nbdkit command. */
196 passthru_format ("%s/server/nbdkit", builddir);
198 /* Option parsing. We don't really parse options here. We are only
199 * interested in which options have arguments and which need
200 * rewriting.
202 for (;;) {
203 int c;
204 int long_index = -1;
205 bool is_long_option;
207 c = getopt_long (argc, argv, short_options, long_options, &long_index);
208 if (c == -1)
209 break;
211 if (c == '?') /* getopt prints an error */
212 exit (EXIT_FAILURE);
214 /* long_index is only set if it's an actual long option. */
215 is_long_option = long_index >= 0;
217 /* Verbose is special because we will print the final command. */
218 if (c == 'v') {
219 verbose = true;
220 if (is_long_option)
221 passthru ("--verbose");
222 else
223 passthru ("-v");
225 /* Filters can be rewritten if they are a short name. */
226 else if (c == FILTER_OPTION) {
227 if (is_short_name (optarg))
228 passthru_format ("--filter=%s/filters/%s/.libs/nbdkit-%s-filter.so",
229 builddir, optarg, optarg);
230 else
231 passthru_format ("--filter=%s", optarg);
233 /* Any long option. */
234 else if (is_long_option) {
235 if (optarg) /* Long option which takes an argument. */
236 passthru_format ("--%s=%s", long_options[long_index].name, optarg);
237 else /* Long option which takes no argument. */
238 passthru_format ("--%s", long_options[long_index].name);
240 /* Any short option. */
241 else {
242 passthru_format ("-%c", c);
243 if (optarg)
244 passthru (optarg);
248 /* Are there any non-option arguments? */
249 if (optind < argc) {
250 /* Ensure any further parameters can never be parsed as options by
251 * real nbdkit.
253 passthru ("--");
255 /* The first non-option argument is the plugin name. If it is a
256 * short name then rewrite it.
258 if (is_short_name (argv[optind])) {
259 /* Special plugins written in Perl. */
260 if (is_perl_plugin (argv[optind])) {
261 passthru_format ("%s/plugins/perl/.libs/nbdkit-perl-plugin.so",
262 builddir);
263 passthru_format ("%s/plugins/%s/nbdkit-%s-plugin",
264 builddir, argv[optind], argv[optind]);
266 else {
267 passthru_format ("%s/plugins/%s/.libs/nbdkit-%s-plugin.so",
268 builddir, argv[optind], argv[optind]);
270 ++optind;
273 /* Everything else is passed through without rewriting. */
274 while (optind < argc) {
275 passthru (argv[optind]);
276 ++optind;
280 end_passthru ();
281 if (verbose)
282 print_command ();
284 /* This is a cheap way to find some use-after-free and uninitialized
285 * read problems when using glibc, and doesn't affect normal
286 * operation or other libc. We don't overwrite existing values so
287 * this can be disabled or overridden at runtime.
289 setenv ("MALLOC_CHECK_", "1", 0);
290 time (&t);
291 tu = t;
292 tu %= 255;
293 tu++;
294 snprintf (ts, sizeof ts, "%u", tu);
295 setenv ("MALLOC_PERTURB_", ts, 0);
297 /* Run the final command. */
298 execvp (cmd[0], (char **) cmd);
299 perror (cmd[0]);
300 exit (EXIT_FAILURE);