Use the recently added nbdkit_string_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(a)redhat.com>
---
plugins/eval/nbdkit-eval-plugin.pod | 2 ++
plugins/sh/nbdkit-sh-plugin.pod | 20 +++++++++--
plugins/sh/methods.h | 1 +
plugins/eval/eval.c | 2 ++
plugins/sh/methods.c | 54 +++++++++++++++++++++++++++--
plugins/sh/sh.c | 1 +
plugins/sh/example.sh | 2 +-
tests/test-eval-exports.sh | 35 ++++++++++++-------
tests/test-tls-fallback.sh | 11 +++---
9 files changed, 107 insertions(+), 21 deletions(-)
diff --git a/plugins/eval/nbdkit-eval-plugin.pod b/plugins/eval/nbdkit-eval-plugin.pod
index 7126c6de..eed97f11 100644
--- a/plugins/eval/nbdkit-eval-plugin.pod
+++ b/plugins/eval/nbdkit-eval-plugin.pod
@@ -110,6 +110,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 07d90b57..b827b658 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 07fb3e48..a2631b39 100644
--- a/plugins/sh/methods.c
+++ b/plugins/sh/methods.c
@@ -241,7 +241,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) {
@@ -310,13 +312,18 @@ sh_list_exports (int readonly, int default_only,
default_only ? "true" : "false", NULL };
CLEANUP_FREE char *s = NULL;
size_t slen;
+ const char *def;
switch (call_read (&s, &slen, args)) {
case OK:
return parse_exports (script, s, slen, exports);
case MISSING:
- return nbdkit_add_export (exports, "", NULL);
+ /* Match what is done for a C plugin. */
+ def = sh_default_export (readonly, nbdkit_is_tls ());
+ if (!def)
+ return 0;
+ return nbdkit_add_export (exports, def, NULL);
case ERROR:
return -1;
@@ -331,6 +338,49 @@ 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;
+ CLEANUP_FREE char *name = NULL;
+
+ 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;
+ name = strndup (p, n - p);
+ return nbdkit_string_intern (name);
+
+ case MISSING:
+ return nbdkit_string_intern ("");
+
+ 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..14b4c2ce 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,21 @@ 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 -u nbd+unix://\?socket=$sock -c 'quit()' && 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-tls-fallback.sh b/tests/test-tls-fallback.sh
index cf54a606..94449f31 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