[Libguestfs] [nbdkit PATCH v3 04/14] sh, eval: Implement .default_export

Eric Blake eblake at redhat.com
Mon Sep 21 16:23:48 UTC 2020


Use the recently added nbdkit_strndup_intern to make this possible.
Testsuite coverage is added in both tls-fallback as well as a cleanup
to test-eval-exports.sh.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 plugins/eval/nbdkit-eval-plugin.pod |  4 +++
 plugins/sh/nbdkit-sh-plugin.pod     | 20 +++++++++++--
 plugins/sh/methods.h                |  1 +
 plugins/eval/eval.c                 |  2 ++
 plugins/sh/methods.c                | 45 ++++++++++++++++++++++++++++-
 plugins/sh/sh.c                     |  1 +
 plugins/sh/example.sh               |  2 +-
 tests/test-eval-exports.sh          | 42 +++++++++++++++++++--------
 tests/test-export-info.sh           |  7 ++---
 tests/test-tls-fallback.sh          | 11 ++++---
 10 files changed, 111 insertions(+), 24 deletions(-)

diff --git a/plugins/eval/nbdkit-eval-plugin.pod b/plugins/eval/nbdkit-eval-plugin.pod
index 7126c6de..eb3c7f5a 100644
--- a/plugins/eval/nbdkit-eval-plugin.pod
+++ b/plugins/eval/nbdkit-eval-plugin.pod
@@ -96,6 +96,8 @@ features):

 =item B<config_complete=>SCRIPT

+=item B<default_export=>SCRIPT
+
 =item B<dump_plugin=>SCRIPT

 =item B<extents=>SCRIPT
@@ -110,6 +112,8 @@ features):

 =item B<list_exports=>SCRIPT

+=item B<default_export=>SCRIPT
+
 =item B<open=>SCRIPT

 =item B<pread=>SCRIPT
diff --git a/plugins/sh/nbdkit-sh-plugin.pod b/plugins/sh/nbdkit-sh-plugin.pod
index f63114d4..f3a6147c 100644
--- a/plugins/sh/nbdkit-sh-plugin.pod
+++ b/plugins/sh/nbdkit-sh-plugin.pod
@@ -315,8 +315,24 @@ export name or description, although this could be possible under a
 new mode supporting escape sequences.

 This method is I<not> required; if it is absent, the list of exports
-advertised by nbdkit will be the single export with the empty string
-as a name and no description.
+advertised by nbdkit will be the single name result of C<default_export>
+and no description.
+
+=item C<default_export>
+
+ /path/to/script default_export <readonly> <tls>
+
+The C<readonly> and C<tls> parameters will be C<true> or C<false>.
+
+On success this should print a name on stdout to use in place of the
+default export C<"">, then exit with code C<0>.  For convenience, the
+output can be any of the list forms recognized by C<list_exports>, in
+which case the first listed export name is used, and where an empty
+list uses C<"">.  Given the current set of recognized export lists, it
+is not possible for the resulting name to include a newline.
+
+This method is I<not> required; if it is absent, the default export
+name will be the empty string, C<"">.

 =item C<open>

diff --git a/plugins/sh/methods.h b/plugins/sh/methods.h
index 69017fa4..3fd4ef42 100644
--- a/plugins/sh/methods.h
+++ b/plugins/sh/methods.h
@@ -46,6 +46,7 @@ extern int sh_after_fork (void);
 extern int sh_preconnect (int readonly);
 extern int sh_list_exports (int readonly, int default_only,
                             struct nbdkit_exports *exports);
+extern const char *sh_default_export (int readonly, int is_tls);
 extern void *sh_open (int readonly);
 extern void sh_close (void *handle);
 extern int64_t sh_get_size (void *handle);
diff --git a/plugins/eval/eval.c b/plugins/eval/eval.c
index 2bd5e79f..a123734e 100644
--- a/plugins/eval/eval.c
+++ b/plugins/eval/eval.c
@@ -68,6 +68,7 @@ static const char *known_methods[] = {
   "close",
   "config",
   "config_complete",
+  "default_export",
   "dump_plugin",
   "extents",
   "flush",
@@ -395,6 +396,7 @@ static struct nbdkit_plugin plugin = {

   .preconnect        = sh_preconnect,
   .list_exports      = sh_list_exports,
+  .default_export    = sh_default_export,
   .open              = sh_open,
   .close             = sh_close,

diff --git a/plugins/sh/methods.c b/plugins/sh/methods.c
index 23e4f084..15e0d053 100644
--- a/plugins/sh/methods.c
+++ b/plugins/sh/methods.c
@@ -240,7 +240,9 @@ parse_exports (const char *script,
 {
   const char *n, *d, *p, *q;

-  /* The first line determines how to parse the rest of s */
+  /* The first line determines how to parse the rest of s.  Keep
+   * sh_default_export in sync with this.
+   */
   if ((p = skip_prefix (s, "INTERLEAVED\n")) != NULL) {
     n = p;
     while ((d = strchr (n, '\n')) != NULL) {
@@ -330,6 +332,47 @@ sh_list_exports (int readonly, int default_only,
   }
 }

+const char *
+sh_default_export (int readonly, int is_tls)
+{
+  const char *method = "default_export";
+  const char *script = get_script (method);
+  const char *args[] = { script, method, readonly ? "true" : "false",
+                         is_tls ? "true" : "false", NULL };
+  CLEANUP_FREE char *s = NULL;
+  size_t slen;
+  const char *p, *n;
+
+  switch (call_read (&s, &slen, args)) {
+  case OK:
+    /* The first line determines how to parse the rest of s.  For now,
+     * all export modes treat the next line as the first export.
+     */
+    if ((p = skip_prefix (s, "INTERLEAVED\n")) != NULL ||
+        (p = skip_prefix (s, "NAMES+DESCRIPTIONS\n")) != NULL ||
+        (p = skip_prefix (s, "NAMES\n")) != NULL)
+      ;
+    else
+      p = s;
+    n = strchr (p, '\n') ?: s + slen;
+    return nbdkit_strndup_intern (p, n - p);
+
+  case MISSING:
+    return "";
+
+  case ERROR:
+    return NULL;
+
+  case RET_FALSE:
+    nbdkit_error ("%s: %s method returned unexpected code (3/false)",
+                  script, method);
+    errno = EIO;
+    return NULL;
+
+  default: abort ();
+  }
+}
+
 void *
 sh_open (int readonly)
 {
diff --git a/plugins/sh/sh.c b/plugins/sh/sh.c
index 374888a4..4fcc2a5a 100644
--- a/plugins/sh/sh.c
+++ b/plugins/sh/sh.c
@@ -301,6 +301,7 @@ static struct nbdkit_plugin plugin = {

   .preconnect        = sh_preconnect,
   .list_exports      = sh_list_exports,
+  .default_export    = sh_default_export,
   .open              = sh_open,
   .close             = sh_close,

diff --git a/plugins/sh/example.sh b/plugins/sh/example.sh
index 4f547db0..e0e1b773 100755
--- a/plugins/sh/example.sh
+++ b/plugins/sh/example.sh
@@ -85,7 +85,7 @@ case "$1" in
         echo parallel
         ;;

-    list_exports)
+    list_exports | default_export)
         # The following lists the names of all files in the current
         # directory that do not contain whitespace, backslash, or single
         # quotes.  No description accompanies the export names.
diff --git a/tests/test-eval-exports.sh b/tests/test-eval-exports.sh
index 7c946378..c56f5ac4 100755
--- a/tests/test-eval-exports.sh
+++ b/tests/test-eval-exports.sh
@@ -37,8 +37,8 @@ source ./functions.sh
 set -e
 set -x

-# requires nbdinfo --version # nbdinfo 1.3.9 was broken, so check this instead:
-requires nbdkit -U - memory 1 --run 'nbdinfo --size --json "$uri"'
+requires nbdinfo --version
+requires nbdsh -c 'print(h.set_full_info)'
 requires jq --version

 sock=$(mktemp -u)
@@ -57,20 +57,19 @@ diff -u <(jq -c \
           '[.exports[] | [."export-name", .description, ."export-size"]]' \
           eval-exports.out) <(printf %s\\n '[["",null,0]]')

-# Start a long-running server with .list_exports set to varying contents
+# Start a long-running server with .list_exports and .default_export
+# set to varying contents
 start_nbdkit -P eval-exports.pid -U $sock eval get_size='echo "$2"|wc -c' \
-    open='echo "$3"' list_exports="cat '$PWD/eval-exports.list'"
+    open='echo "$3"' list_exports="cat '$PWD/eval-exports.list'" \
+    default_export="cat '$PWD/eval-exports.list'"

 # do_nbdkit EXPNAME EXPOUT
 do_nbdkit ()
 {
     # Check how the default export name is handled
-    # nbdinfo currently makes multiple connections, so we can't use the
-    # long-running server for validating default export name.
-    # XXX FIXME: requires .default_export in eval
-    : || nbdkit -U - -v eval list_exports="cat '$PWD/eval-exports.list'" \
-      open='[ "$3" = "'"$1"'" ] || { echo EINVAL wrong export >&2; exit 1; }' \
-      get_size='echo 0' --run 'nbdsh -u "$uri" -c "exit()"'
+    nbdinfo --no-content nbd+unix://\?socket=$sock >eval-exports.out
+    diff -u <(sed -n 's/export="\(.*\)":/\1/p' eval-exports.out) \
+         <(printf %s\\n "$1")
     # Check what exports are listed
     nbdinfo --list --json nbd+unix://\?socket=$sock >eval-exports.out
     cat eval-exports.out
@@ -79,9 +78,28 @@ do_nbdkit ()
               eval-exports.out) <(printf %s\\n "$2")
 }

-# With no file, .list_exports fails, but connecting works
+# With no file, .list_exports and .default_export both fail, preventing
+# connection to the default export, but not other exports
 nbdinfo --list --json nbd+unix://\?socket=$sock && fail=1
-nbdsh -u nbd+unix://\?socket=$sock -c 'quit()'
+nbdsh -c '
+import os
+try:
+  h.connect_uri("nbd+unix://?socket='"$sock"'")
+  exit(1)
+except nbd.Error:
+  pass
+' || fail=1
+nbdsh -u nbd+unix:///name\?socket=$sock -c 'quit()'
+
+# Setting .default_export but not .list_exports advertises the canonical name
+nbdkit -U - eval default_export='echo hello' get_size='echo 0' \
+       --run 'nbdinfo --list "$uri"' >eval-exports.out
+diff -u <(grep '^export=' eval-exports.out) <(echo 'export="hello":')
+
+# Failing .default_export without .list_exports results in an empty list
+nbdkit -U - eval default_export='echo ENOENT >&2; exit 1' get_size='echo 0' \
+       --run 'nbdinfo --list "$uri"' >eval-exports.out
+diff -u <(grep '^export=' eval-exports.out) /dev/null

 # Various spellings of empty lists, producing 0 exports
 for fmt in '' 'NAMES\n' 'INTERLEAVED\n' 'NAMES+DESCRIPTIONS\n'; do
diff --git a/tests/test-export-info.sh b/tests/test-export-info.sh
index f94b69e6..3b61979f 100755
--- a/tests/test-export-info.sh
+++ b/tests/test-export-info.sh
@@ -43,7 +43,7 @@ rm -f $files
 cleanup_fn rm -f $files

 # Create an nbdkit sh plugin for checking NBD_INFO replies to NBD_OPT_GO.
-# XXX Update when .default_export and .export_description are implemented in sh
+# XXX Update when .export_description is implemented in sh
 start_nbdkit -P export-info.pid -U $sock \
              sh - <<'EOF'
 case "$1" in
@@ -102,11 +102,10 @@ assert h.get_canonical_export_name() == "a"

 h.set_export_name("")
 h.opt_info()
-# XXX Adjust once default export works
-assert h.get_canonical_export_name() == ""
+assert h.get_canonical_export_name() == "hello"

 h.opt_go()
-assert h.get_canonical_export_name() == ""
+assert h.get_canonical_export_name() == "hello"

 h.shutdown()
 '
diff --git a/tests/test-tls-fallback.sh b/tests/test-tls-fallback.sh
index fe3b84de..f3c4b14b 100755
--- a/tests/test-tls-fallback.sh
+++ b/tests/test-tls-fallback.sh
@@ -60,12 +60,15 @@ cleanup_fn rm -f $files
 # Run dual-mode server
 start_nbdkit -P $pid -U $sock --tls=on --tls-psk=keys.psk \
     --filter=tls-fallback sh - tlsreadme=$'dummy\n' <<\EOF
+check () {
+ if test "$1" != true; then
+   echo 'EINVAL unexpected tls mode' 2>&1; exit 1
+ fi
+}
 case $1 in
   list_exports) echo NAMES; echo hello; echo world ;;
-  open) if test "$4" != true; then
-      echo 'EINVAL unexpected tls mode' 2>&1; exit 1
-    fi
-    echo $3 ;;
+  default_export) check "$3"; echo hello ;;
+  open) check "$4"; echo $3 ;;
   get_size) echo "$2" | wc -c ;;
   pread) echo "$2" | dd skip=$4 count=$3 iflag=skip_bytes,count_bytes ;;
   can_write | can_trim) exit 0 ;;
-- 
2.28.0




More information about the Libguestfs mailing list