[Libguestfs] [libnbd PATCH v2 04/12] api: Add nbd_set_request_meta_context()

Eric Blake
Wed, 31 Aug 2022 14:39:20 -0000

Add a new control knob nbd_set_request_meta_context(), modeled after
the existing nbd_set_request_structured_replies(), to make it possible
to skip the NBD_OPT_SET_META_CONTEXT half of the two-command sequence
currently performed in nbd_opt_go() and nbd_opt_info().  Also add a
counterpart nbd_get_request_meta_context() for symmetry.

A later patch will then add the ability for the user to manually
invoke nbd_opt_set_meta_context() at a time of their choosing during
option negotiation; but even without that patch, this new API has some
demonstrable effects by itself:

- skipping meta contexts but not structured replies lets us test
  additional corner cases of servers (for example, while trying to
  write my unit tests, I quickly found out that with structured
  replies negotiated, current nbdkit ALWAYS emulates and advertises
  the base:allocation context rather than consulting .can_extents as a
  way to suppress it on a per-export basis, even when a corresponding
  .open would fail. A future nbdkit may make .can_extents tri-state to
  make it possible to do structured reads but not extents; however,
  the current nbdkit behavior appears to comply with the NBD spec,
  which allows but does not require NBD_OPT_SET_META_CONTEXT to pay
  attention to the export name)

- back-to-back nbd_opt_info() and nbd_opt_go() was performing a
  redundant SET_META_CONTEXT; with this new API, we can get by with
  less network traffic during negotiation

- nbd_opt_info() has to be client-side stateful (you check things like
  nbd_get_size after the fact), but based on the name, the fact that
  it was also server-side stateful was surprising.  Skipping
  SET_META_CONTEXT during nbd_opt_info(), and instead using
  stateless[1] nbd_opt_list_meta_context(), avoids messing with server
  state and can also be more convenient in getting supported context
  names by callback instead of lots of post-process
  nbd_can_meta_context() calls

Things to note in the patch: the choice of when to change h->meta_valid
is moved around.  Marking contexts invalid is no longer a side effect
of clearing h->exportsize (that is, once set_request_meta_context is
false, nbd_opt_go() and nbd_opt_info() should inherit what contexts
are previously negotiated); it is now an explicit action when changing
the export name[2], starting an actual NBD_OPT_SET_META_CONTEXT
request, or upon failure of NBD_OPT_GO/INFO.

The testsuite changes added here depend on the new API; therefore,
there is no benefit to separating the C change to a separate patch (if
split and you rearranged the series, it would fail to compile).
However, for ease of review, porting the test to its counterparts in
other languages is split out.

[1] nbd_opt_list_meta_context() is slightly improved here, but has
other client-side state effects that are left for a later patch to
minimize the size of this one
[2] nbd_set_export_name() should also reset size, but I'm leaving that
for a later patch to minimize this one
---
 lib/internal.h                               |   1 +
 generator/API.ml                             |  71 ++++++-
 generator/states-newstyle-opt-go.c           |   1 +
 generator/states-newstyle-opt-meta-context.c |  19 +-
 lib/flags.c                                  |   4 +-
 lib/handle.c                                 |  17 ++
 tests/opt-info.c                             |  67 +++++-
 tests/opt-set-meta                           | 210 +++++++++++++++++++
 8 files changed, 369 insertions(+), 21 deletions(-)
 create mode 100755 tests/opt-set-meta

diff --git a/lib/internal.h b/lib/internal.h
index 8aaff15..9d329f0 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -111,6 +111,7 @@ struct nbd_handle {

   /* Desired metadata contexts. */
   bool request_sr;
+  bool request_meta;
   string_vector request_meta_contexts;

   /* Allowed in URIs, see lib/uri.c. */
diff --git a/generator/API.ml b/generator/API.ml
index 62e2d54..adafac6 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -747,6 +747,46 @@   "get_structured_replies_negotiated", {
                 Link "get_protocol"];
   };

+  "set_request_meta_context", {
+    default_call with
+    args = [Bool "request"]; ret = RErr;
+    permitted_states = [ Created; Negotiating ];
+    shortdesc = "control whether connect automatically requests meta
contexts";
+    longdesc = "\
+This function controls whether the act of connecting to an export
+(all C<nbd_connect_*> calls when L<nbd_set_opt_mode(3)> is false,
+or L<nbd_opt_go(3)> and L<nbd_opt_info(3)> when option mode is
+enabled) will also try to issue NBD_OPT_SET_META_CONTEXT when
+the server supports structured replies and any contexts were
+registered by L<nbd_add_meta_context(3)>.  The default setting
+is true; however the extra step of negotiating meta contexts is
+not always desirable: performing both info and go on the same
+export works without needing to re-negotiate contexts on the
+second call; and even when using just L<nbd_opt_info(3)>, it
+can be faster to collect the server's results by relying on the
+callback function passed to L<nbd_opt_list_meta_context(3)> than
+a series of post-process calls to L<nbd_can_meta_context(3)>.
+
+Note that this control has no effect if the server does not
+negotiate structured replies, or if the client did not request
+any contexts via L<nbd_add_meta_context(3)>.  Setting this
+control to false may cause L<nbd_block_status(3)> to fail.";
+    see_also = [Link "set_opt_mode"; Link "opt_go"; Link
"opt_info";
+                Link "opt_list_meta_context";
+                Link "get_structured_replies_negotiated";
+                Link "get_request_meta_context"; Link
"can_meta_context"];
+  };
+
+  "get_request_meta_context", {
+    default_call with
+    args = []; ret = RBool;
+    permitted_states = [];
+    shortdesc = "see if connect automatically requests meta
contexts";
+    longdesc = "\
+Return the state of the automatic meta context request flag on this
handle.";
+    see_also = [Link "set_request_meta_context"];
+  };
+
   "set_handshake_flags", {
     default_call with
     args = [ Flags ("flags", handshake_flags) ]; ret = RErr;
@@ -994,12 +1034,17 @@   "opt_go", {
 or L<nbd_connect_uri(3)>.  This can only be used if
 L<nbd_set_opt_mode(3)> enabled option mode.

+By default, libnbd will automatically request all meta contexts
+registered by L<nbd_add_meta_context(3)> as part of this call; but
+this can be suppressed with L<nbd_set_request_meta_context(3)>.
+
 If this fails, the server may still be in negotiation, where it is
 possible to attempt another option such as a different export name;
 although older servers will instead have killed the connection.";
     example = Some "examples/list-exports.c";
     see_also = [Link "set_opt_mode"; Link "aio_opt_go";
Link "opt_abort";
-                Link "set_export_name"; Link "connect_uri";
Link "opt_info"];
+                Link "set_export_name"; Link "connect_uri";
Link "opt_info";
+                Link "add_meta_context"; Link
"set_request_meta_context"];
   };

   "opt_abort", {
@@ -1068,16 +1113,23 @@   "opt_info", {
 L<nbd_set_opt_mode(3)> enabled option mode.

 If successful, functions like L<nbd_is_read_only(3)> and
-L<nbd_get_size(3)> will report details about that export.  In
-general, if L<nbd_opt_go(3)> is called next, that call will
-likely succeed with the details remaining the same, although this
-is not guaranteed by all servers.
+L<nbd_get_size(3)> will report details about that export.  If
+L<nbd_set_request_meta_context(3)> is set (the default) and
+structured replies were negotiated, it is also valid to use
+L<nbd_can_meta_context(3)> after this call.  However, it may be
+more efficient to clear that setting and manually utilize
+L<nbd_opt_list_meta_context(3)> with its callback approach, for
+learning which contexts an export supports.  In general, if
+L<nbd_opt_go(3)> is called next, that call will likely succeed
+with the details remaining the same, although this is not
+guaranteed by all servers.

 Not all servers understand this request, and even when it is
 understood, the server might fail the request even when a
 corresponding L<nbd_opt_go(3)> would succeed.";
     see_also = [Link "set_opt_mode"; Link "aio_opt_info";
Link "opt_go";
-                Link "set_export_name"];
+                Link "set_export_name"; Link
"set_request_meta_context";
+                Link "opt_list_meta_context"];
   };

   "opt_list_meta_context", {
@@ -1797,7 +1849,8 @@   "can_meta_context", {
 ^ non_blocking_test_call_description;
     see_also = [SectionLink "Flag calls"; Link "opt_info";
                 Link "add_meta_context";
-                Link "block_status"; Link
"aio_block_status"];
+                Link "block_status"; Link
"aio_block_status";
+                Link "set_request_meta_context"];
   };

   "get_protocol", {
@@ -3246,6 +3299,10 @@ let first_version    "set_request_block_size",
(1, 12);
   "get_request_block_size", (1, 12);

+  (* Added in 1.13.x development cycle, will be stable and supported in 1.14.
*)
+  "set_request_meta_context", (1, 14);
+  "get_request_meta_context", (1, 14);
+
   (* These calls are proposed for a future version of libnbd, but
    * have not been added to any released version so far.
   "get_tls_certificates", (1, ??);
diff --git a/generator/states-newstyle-opt-go.c
b/generator/states-newstyle-opt-go.c
index b7354ae..1ca5f09 100644
--- a/generator/states-newstyle-opt-go.c
+++ b/generator/states-newstyle-opt-go.c
@@ -269,6 +269,7 @@ STATE_MACHINE {
                  reply);
     }
     nbd_internal_reset_size_and_flags (h);
+    h->meta_valid = false;
     err = nbd_get_errno () ? : ENOTSUP;
     break;
   case NBD_REP_ACK:
diff --git a/generator/states-newstyle-opt-meta-context.c
b/generator/states-newstyle-opt-meta-context.c
index 5c65454..35d3cbc 100644
--- a/generator/states-newstyle-opt-meta-context.c
+++ b/generator/states-newstyle-opt-meta-context.c
@@ -29,9 +29,6 @@ STATE_MACHINE {
    */
   assert (h->gflags & LIBNBD_HANDSHAKE_FLAG_FIXED_NEWSTYLE);
   nbd_internal_reset_size_and_flags (h);
-  for (i = 0; i < h->meta_contexts.len; ++i)
-    free (h->meta_contexts.ptr[i].name);
-  meta_vector_reset (&h->meta_contexts);
   if (h->opt_current == NBD_OPT_LIST_META_CONTEXT) {
     assert (h->opt_mode);
     assert (CALLBACK_IS_NOT_NULL (h->opt_cb.fn.context));
@@ -40,13 +37,19 @@ STATE_MACHINE {
   else {
     assert (CALLBACK_IS_NULL (h->opt_cb.fn.context));
     opt = NBD_OPT_SET_META_CONTEXT;
-    if (!h->structured_replies || h->request_meta_contexts.len == 0) {
-      SET_NEXT_STATE (%^OPT_GO.START);
-      return 0;
+    if (h->request_meta) {
+      for (i = 0; i < h->meta_contexts.len; ++i)
+        free (h->meta_contexts.ptr[i].name);
+      meta_vector_reset (&h->meta_contexts);
+      h->meta_valid = false;
     }
   }
-
-  assert (!h->meta_valid);
+  if (opt != h->opt_current &&
+      (!h->request_meta || !h->structured_replies ||
+       h->request_meta_contexts.len == 0)) {
+    SET_NEXT_STATE (%^OPT_GO.START);
+    return 0;
+  }

   /* Calculate the length of the option request data. */
   len = 4 /* exportname len */ + strlen (h->export_name) + 4 /* nr queries
*/;
diff --git a/lib/flags.c b/lib/flags.c
index 91efc1a..c8c68ea 100644
--- a/lib/flags.c
+++ b/lib/flags.c
@@ -37,7 +37,6 @@ nbd_internal_reset_size_and_flags (struct nbd_handle *h)

   h->exportsize = 0;
   h->eflags = 0;
-  h->meta_valid = false;
   h->block_minimum = 0;
   h->block_preferred = 0;
   h->block_maximum = 0;
@@ -73,7 +72,8 @@ nbd_internal_set_size_and_flags (struct nbd_handle *h,
     eflags &= ~NBD_FLAG_SEND_FAST_ZERO;
   }

-  if (!h->structured_replies || h->request_meta_contexts.len == 0) {
+  if (h->request_meta &&
+      (!h->structured_replies || h->request_meta_contexts.len == 0)) {
     assert (h->meta_contexts.len == 0);
     h->meta_valid = true;
   }
diff --git a/lib/handle.c b/lib/handle.c
index 03f45a4..4b373f5 100644
--- a/lib/handle.c
+++ b/lib/handle.c
@@ -64,6 +64,7 @@ nbd_create (void)
   h->unique = 1;
   h->tls_verify_peer = true;
   h->request_sr = true;
+  h->request_meta = true;
   h->request_block_size = true;
   h->pread_initialize = true;

@@ -232,6 +233,7 @@ nbd_unlocked_set_export_name (struct nbd_handle *h, const
char *export_name)

   free (h->export_name);
   h->export_name = new_name;
+  h->meta_valid = false;
   return 0;
 }

@@ -391,6 +393,21 @@ nbd_unlocked_get_request_structured_replies (struct
nbd_handle *h)
   return h->request_sr;
 }

+int
+nbd_unlocked_set_request_meta_context (struct nbd_handle *h,
+                                       bool request)
+{
+  h->request_meta = request;
+  return 0;
+}
+
+/* NB: may_set_error = false. */
+int
+nbd_unlocked_get_request_meta_context (struct nbd_handle *h)
+{
+  return h->request_meta;
+}
+
 int
 nbd_unlocked_get_structured_replies_negotiated (struct nbd_handle *h)
 {
diff --git a/tests/opt-info.c b/tests/opt-info.c
index b9739a5..26de0ee 100644
--- a/tests/opt-info.c
+++ b/tests/opt-info.c
@@ -1,5 +1,5 @@
 /* NBD client library in userspace
- * Copyright (C) 2013-2020 Red Hat Inc.
+ * Copyright (C) 2013-2022 Red Hat Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -102,11 +102,15 @@ main (int argc, char *argv[])
     exit (EXIT_FAILURE);
   }

-  /* info for a different export */
+  /* info for a different export, with automatic meta_context disabled */
   if (nbd_set_export_name (nbd, "b") == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
   }
+  if (nbd_set_request_meta_context (nbd, 0) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
   if (nbd_opt_info (nbd) == -1) {
     fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
@@ -119,8 +123,12 @@ main (int argc, char *argv[])
     fprintf (stderr, "expecting read-write export, got %" PRId64
"\n", r);
     exit (EXIT_FAILURE);
   }
-  if ((r = nbd_can_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION)) != 1) {
-    fprintf (stderr, "expecting can_meta_context true, got %" PRId64
"\n", r);
+  if (nbd_can_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION) != -1) {
+    fprintf (stderr, "expecting error for can_meta_context\n");
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_set_request_meta_context (nbd, 1) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
     exit (EXIT_FAILURE);
   }

@@ -189,8 +197,59 @@ main (int argc, char *argv[])
     fprintf (stderr, "expecting size of 4, got %" PRId64
"\n", r);
     exit (EXIT_FAILURE);
   }
+  if ((r = nbd_can_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION)) != 1) {
+    fprintf (stderr, "expecting can_meta_context true, got %" PRId64
"\n", r);
+    exit (EXIT_FAILURE);
+  }

   nbd_shutdown (nbd, 0);
   nbd_close (nbd);
+
+  /* Another connection. This time, check that SET_META triggered by opt_info
+   * persists through nbd_opt_go with set_request_meta_context disabled. */
+  nbd = nbd_create ();
+  if (nbd == NULL ||
+      nbd_set_opt_mode (nbd, true) == -1 ||
+      nbd_connect_command (nbd, args) == -1 ||
+      nbd_add_meta_context (nbd, "x-unexpected:bogus") == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+
+  if (nbd_can_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION) != -1) {
+    fprintf (stderr, "expecting error for can_meta_context\n");
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_opt_info (nbd) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if ((r = nbd_can_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION)) != 0) {
+    fprintf (stderr, "expecting can_meta_context false, got %" PRId64
"\n", r);
+
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_set_request_meta_context (nbd, 0) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  /* Adding to the request list now won't matter */
+  if (nbd_add_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION) != 0) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_opt_go (nbd) == -1) {
+    fprintf (stderr, "%s\n", nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if ((r = nbd_can_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION)) != 0) {
+    fprintf (stderr, "expecting can_meta_context false, got %" PRId64
"\n", r);
+
+    exit (EXIT_FAILURE);
+  }
+
+  nbd_shutdown (nbd, 0);
+  nbd_close (nbd);
+
   exit (EXIT_SUCCESS);
 }
diff --git a/tests/opt-set-meta b/tests/opt-set-meta
new file mode 100755
index 0000000..6c7b044
--- /dev/null
+++ b/tests/opt-set-meta
@@ -0,0 +1,210 @@
+#! /bin/sh
+
+# opt-set-meta - temporary wrapper script for .libs/opt-set-meta
+# Generated by libtool (GNU libtool) 2.4.6
+#
+# The opt-set-meta program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null
2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=""
+
+# This environment variable determines our operation mode.
+if test "$libtool_install_magic" = "%%%MAGIC variable%%%";
then
+  # install mode needs the following variables:
+  generated_by_libtool_version='2.4.6'
+  notinst_deplibs=' ../lib/libnbd.la'
+else
+  # When we are sourced in execute mode, $file and $ECHO are already set.
+  if test "$libtool_execute_magic" != "%%%MAGIC
variable%%%"; then
+    file="$0"
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+    ECHO="printf %s\\n"
+  fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string --lt-
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's ../libtool value, followed by no.
+lt_option_debug+func_parse_lt_options ()
+{
+  lt_script_arg0=$0
+  shift
+  for lt_opt
+  do
+    case "$lt_opt" in
+    --lt-debug) lt_option_debug=1 ;;
+    --lt-dump-script)
+        lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e
's/^X//' -e 's%/[^/]*$%%'`
+        test "X$lt_dump_D" = "X$lt_script_arg0" &&
lt_dump_D=.
+        lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e
's/^X//' -e 's%^.*/%%'`
+        cat "$lt_dump_D/$lt_dump_F"
+        exit 0
+      ;;
+    --lt-*)
+        $ECHO "Unrecognized --lt- option: '$lt_opt'"
1>&2
+        exit 1
+      ;;
+    esac
+  done
+
+  # Print the debug banner immediately:
+  if test -n "$lt_option_debug"; then
+    echo "opt-set-meta:opt-set-meta:$LINENO: libtool wrapper (GNU libtool)
2.4.6" 1>&2
+  fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+  lt_dump_args_N=1;
+  for lt_arg
+  do
+    $ECHO "opt-set-meta:opt-set-meta:$LINENO: newargv[$lt_dump_args_N]:
$lt_arg"
+    lt_dump_args_N=`expr $lt_dump_args_N + 1`
+  done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+
+      if test -n "$lt_option_debug"; then
+        $ECHO "opt-set-meta:opt-set-meta:$LINENO: newargv[0]:
$progdir/$program" 1>&2
+        func_lt_dump_args ${1+"$@"} 1>&2
+      fi
+      exec "$progdir/$program" ${1+"$@"}
+
+      $ECHO "$0: cannot exec $program $*" 1>&2
+      exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from $@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+  case " $* " in
+  *\ --lt-*)
+    for lt_wr_arg
+    do
+      case $lt_wr_arg in
+      --lt-*) ;;
+      *) set x "$@" "$lt_wr_arg"; shift;;
+      esac
+      shift
+    done ;;
+  esac
+  func_exec_program_core ${1+"$@"}
+}
+
+  # Parse options
+  func_parse_lt_options "$0" ${1+"$@"}
+
+  # Find the directory that this script lives in.
+  thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+  test "x$thisdir" = "x$file" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
+  while test -n "$file"; do
+    destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
+
+    # If there was a directory component, then change thisdir.
+    if test "x$destdir" != "x$file"; then
+      case "$destdir" in
+      [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
+      *) thisdir="$thisdir/$destdir" ;;
+      esac
+    fi
+
+    file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
+    file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*->
//p'`
+  done
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+  if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
+    # special case for '.'
+    if test "$thisdir" = "."; then
+      thisdir=`pwd`
+    fi
+    # remove .libs from thisdir
+    case "$thisdir" in
+    *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed
's%[\\/][^\\/]*$%%'` ;;
+    .libs )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=`cd "$thisdir" && pwd`
+  test -n "$absdir" && thisdir="$absdir"
+
+  program='opt-set-meta'
+  progdir="$thisdir/.libs"
+
+
+  if test -f "$progdir/$program"; then
+    # Add our own library path to LD_LIBRARY_PATH
+    LD_LIBRARY_PATH="/home/eblake/libnbd/lib/.libs:$LD_LIBRARY_PATH"
+
+    # Some systems cannot cope with colon-terminated LD_LIBRARY_PATH
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    LD_LIBRARY_PATH=`$ECHO "$LD_LIBRARY_PATH" | /usr/bin/sed
's/::*$//'`
+
+    export LD_LIBRARY_PATH
+
+    if test "$libtool_execute_magic" != "%%%MAGIC
variable%%%"; then
+      # Run the actual program with our arguments.
+      func_exec_program ${1+"$@"}
+    fi
+  else
+    # The program doesn't exist.
+    $ECHO "$0: error: '$progdir/$program' does not exist"
1>&2
+    $ECHO "This script is just a wrapper for $program." 1>&2
+    $ECHO "See the libtool documentation for more information."
1>&2
+    exit 1
+  fi
+fi
--
2.37.2