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
44 #define NBDKIT_API_VERSION 2
46 #include <nbdkit-plugin.h>
54 static char *magic_config_key
;
56 /* This is called back by methods.c to get the current script name (if
57 * set). The method parameter is ignored by nbdkit-sh-plugin.
60 get_script (const char *method
)
74 const char *method
= "unload";
76 /* Run the unload method. Ignore all errors. */
78 const char *args
[] = { script
, method
, NULL
};
85 free (magic_config_key
);
88 /* This implements the "inline script" feature. Read stdin into a
89 * temporary file and return the name of the file which the caller
90 * must free. For convenience we put the temporary file into tmpdir
91 * but that's an implementation detail.
96 const char scriptname
[] = "inline-script.sh";
97 char *filename
= NULL
;
98 CLEANUP_FREE
char *cmd
= NULL
;
100 if (!nbdkit_stdio_safe ()) {
101 nbdkit_error ("inline script is incompatible with -s");
105 if (asprintf (&filename
, "%s/%s", tmpdir
, scriptname
) == -1) {
106 nbdkit_error ("asprintf: %m");
110 /* Safe because both the tmpdir and script name are controlled by us
111 * and don't contain characters that need quoting.
113 if (asprintf (&cmd
, "cat > %s", filename
) == -1) {
114 nbdkit_error ("asprintf: %m");
118 if (system (cmd
) != 0) {
119 nbdkit_error ("sh: failed to copy inline script to temporary file");
123 if (chmod (filename
, 0500) == -1) {
124 nbdkit_error ("chmod: %s: %m", filename
);
136 sh_config (const char *key
, const char *value
)
139 /* The first parameter MUST be "script". */
140 if (strcmp (key
, "script") != 0) {
141 nbdkit_error ("the first parameter must be script=/path/to/script");
145 /* If the script name is not "-" then it's expected to be a
146 * filename, otherwise it's an inline script which must be read
147 * into a temporary file. Either way we want an absolute path.
149 if (strcmp (value
, "-") != 0)
150 script
= nbdkit_realpath (value
);
152 script
= inline_script ();
156 /* Call the load method. */
157 const char *args
[] = { script
, "load", NULL
};
158 switch (call (args
)) {
167 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
175 /* Call the magic_config_key method if it exists. */
176 const char *args2
[] = { script
, "magic_config_key", NULL
};
177 CLEANUP_FREE_STRING string s
= empty_vector
;
178 switch (call_read (&s
, args2
)) {
180 if (s
.len
> 0 && s
.ptr
[s
.len
-1] == '\n')
181 s
.ptr
[s
.len
-1] = '\0';
182 magic_config_key
= strdup (s
.ptr
);
183 if (magic_config_key
== NULL
) {
184 nbdkit_error ("strdup: %m");
196 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
197 script
, "magic_config_key");
205 /* If the script sets a magic_config_key then it's possible that
206 * we will be called here with key == "script" (which is the
207 * plugin.magic_config_key). If that happens then swap in the
208 * script magic_config_key as the key. However if the script
209 * didn't define a magic_config_key then it's an error, emulating
210 * the behaviour of the core server.
212 if (strcmp (key
, "script") == 0) {
213 if (magic_config_key
)
214 key
= magic_config_key
;
216 nbdkit_error ("%s: expecting key=value on the command line but got: "
223 const char *args
[] = { script
, "config", key
, value
, NULL
};
224 switch (call (args
)) {
229 /* Emulate what core nbdkit does if a config callback is NULL. */
230 nbdkit_error ("%s: this plugin does not need command line configuration",
238 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
251 sh_config_complete (void)
253 const char *args
[] = { script
, "config_complete", NULL
};
256 nbdkit_error ("missing script parameter");
260 switch (call (args
)) {
269 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
270 script
, "config_complete");
278 #define sh_config_help \
279 "script=<FILENAME> (required) The shell script to run.\n" \
280 "[other arguments may be used by the plugin that you load]"
282 /* See also the comments in call.c:call3() */
283 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
285 static struct nbdkit_plugin plugin
= {
287 .version
= PACKAGE_VERSION
,
291 .dump_plugin
= sh_dump_plugin
,
294 .config_complete
= sh_config_complete
,
295 .config_help
= sh_config_help
,
296 .magic_config_key
= "script",
297 .thread_model
= sh_thread_model
,
298 .get_ready
= sh_get_ready
,
299 .after_fork
= sh_after_fork
,
301 .preconnect
= sh_preconnect
,
302 .list_exports
= sh_list_exports
,
303 .default_export
= sh_default_export
,
307 .export_description
= sh_export_description
,
308 .get_size
= sh_get_size
,
309 .block_size
= sh_block_size
,
310 .can_write
= sh_can_write
,
311 .can_flush
= sh_can_flush
,
312 .is_rotational
= sh_is_rotational
,
313 .can_trim
= sh_can_trim
,
314 .can_zero
= sh_can_zero
,
315 .can_extents
= sh_can_extents
,
316 .can_fua
= sh_can_fua
,
317 .can_multi_conn
= sh_can_multi_conn
,
318 .can_cache
= sh_can_cache
,
319 .can_fast_zero
= sh_can_fast_zero
,
326 .extents
= sh_extents
,
329 .errno_is_preserved
= 1,
332 NBDKIT_REGISTER_PLUGIN (plugin
)