This implements something like a readonly 1MB disk reading as zeroes: nbdkit sh script=- <<'EOF' case "$1" in get_size) echo 1M ;; pread) dd if=/dev/zero count=$3 iflag=count_bytes ;; *) exit 2 ;; esac EOF Use of "-" is analogous to reading passwords from stdin. --- plugins/sh/nbdkit-sh-plugin.pod | 17 ++++++++++ plugins/sh/sh.c | 55 ++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod index 371de5f..0644c36 100644 --- a/plugins/sh/nbdkit-sh-plugin.pod +++ b/plugins/sh/nbdkit-sh-plugin.pod @@ -6,6 +6,10 @@ nbdkit-sh-plugin - nbdkit shell, script or executable plugin nbdkit sh /path/to/script [arguments...] + nbdkit sh script=- [arguments...] <<'EOF' + ... shell script here ... + EOF + =head1 DESCRIPTION C<nbdkit-sh-plugin> allows you to write plugins for L<nbdkit(1)> using @@ -26,6 +30,19 @@ like this: You may have to add further C<key=value> arguments to the command line. +=head2 Inline shell scripts + +It is also possible to write a shell script plugin "inline" using C<-> +as the name of the script, like this: + + nbdkit sh script=- <<'EOF' + case "$1" in + get_size) echo 1M ;; + pread) dd if=/dev/zero count=$3 iflag=count_bytes ;; + *) exit 2 ;; + esac + EOF + =head1 WRITING AN NBDKIT SH PLUGIN For an example plugin written in Bash, see: diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c index 368f1eb..fcd839b 100644 --- a/plugins/sh/sh.c +++ b/plugins/sh/sh.c @@ -39,6 +39,7 @@ #include <string.h> #include <unistd.h> #include <errno.h> +#include <sys/stat.h> #include <nbdkit-plugin.h> @@ -115,6 +116,50 @@ sh_dump_plugin (void) } } +/* This implements the "inline script" feature (script=-). Read stdin + * into a temporary file and return the name of the file which the + * caller must free. For convenience we put the temporary file into + * tmpdir but that's an implementation detail. + */ +static char * +inline_script (void) +{ + const char scriptname[] = "inline-script.sh"; + char *filename = NULL; + char *cmd = NULL; + + if (asprintf (&filename, "%s/%s", tmpdir, scriptname) == -1) { + nbdkit_error ("asprintf: %m"); + goto err; + } + + /* Safe because both the tmpdir and script name are controlled by us + * and don't contain characters that need quoting. + */ + if (asprintf (&cmd, "cat > %s", filename) == -1) { + nbdkit_error ("asprintf: %m"); + goto err; + } + + if (system (cmd) != 0) { + nbdkit_error ("sh: failed to copy inline script to temporary file"); + goto err; + } + + if (chmod (filename, 0500) == -1) { + nbdkit_error ("chmod: %s: %m", filename); + goto err; + } + + free (cmd); + return filename; + + err: + free (filename); + free (cmd); + return NULL; +} + static int sh_config (const char *key, const char *value) { @@ -124,7 +169,15 @@ sh_config (const char *key, const char *value) nbdkit_error ("the first parameter must be script=/path/to/script"); return -1; } - script = nbdkit_realpath (value); + + /* If the script name is not "-" then it's expected to be a + * filename, otherwise it's an inline script which must be read + * into a temporary file. Either way we want an absolute path. + */ + if (strcmp (value, "-") != 0) + script = nbdkit_realpath (value); + else + script = inline_script (); if (script == NULL) return -1; -- 2.19.2