[Libguestfs] [PATCH nbdkit v2 1/4] sh: Implement inline scripts.

Richard W.M. Jones rjones at redhat.com
Sat Dec 15 11:01:14 UTC 2018


This implements a readonly disk reading as zeroes:

nbdkit sh - <<'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 | 20 ++++++++++++
 plugins/sh/sh.c                 | 55 ++++++++++++++++++++++++++++++++-
 2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod
index 371de5f..2765841 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 - <<'EOF'
+ ... shell script ...
+ EOF
+
 =head1 DESCRIPTION
 
 C<nbdkit-sh-plugin> allows you to write plugins for L<nbdkit(1)> using
@@ -26,6 +30,22 @@ 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 - <<'EOF'
+   case "$1" in
+     get_size) echo 1M ;;
+     pread) dd if=/dev/zero count=$3 iflag=count_bytes ;;
+     *) exit 2 ;;
+   esac
+ EOF
+
+By default the inline script runs under F</bin/sh>.  You can add a
+shebang (C<#!>) to use other scripting languages.
+
 =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..4ca394f 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.  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




More information about the Libguestfs mailing list