[Libguestfs] [libnbd PATCH 1/3] tests: Test behavior of nbd_shutdown during opt mode

Eric Blake
2023-08-29T22:20:41Z

When we added nbd_set_opt_mode (v1.4), we did not do anything special
to nbd_shutdown().  As a result, clients that use opt mode when
available, but which want to gracefully close a socket rather than
just forcefully disconnect via nbd_close(), have to take care to check
the current state and then decide between nbd_opt_abort or
nbd_shutdown (if they call both, one of the two will give an error
message about being used from the wrong state).  Add some unit test
coverage of this prior to enhancing the API to make a clean shutdown
easier for clients.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 tests/Makefile.am         |   5 ++
 tests/shutdown-opt-mode.c | 124 ++++++++++++++++++++++++++++++++++++++
 .gitignore                |   1 +
 3 files changed, 130 insertions(+)
 create mode 100644 tests/shutdown-opt-mode.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 52fadd9c..d2e9baee 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -189,6 +189,7 @@ check_PROGRAMS += \
 	errors-server-zerosize \
 	server-death \
 	shutdown-flags \
+	shutdown-opt-mode \
 	get-size \
 	read-only-flag \
 	read-write-flag \
@@ -263,6 +264,7 @@ TESTS += \
 	errors-server-zerosize \
 	server-death \
 	shutdown-flags \
+	shutdown-opt-mode \
 	get-size \
 	read-only-flag \
 	read-write-flag \
@@ -401,6 +403,9 @@ server_death_LDADD = $(top_builddir)/lib/libnbd.la
 shutdown_flags_SOURCES = shutdown-flags.c
 shutdown_flags_LDADD = $(top_builddir)/lib/libnbd.la

+shutdown_opt_mode_SOURCES = shutdown-opt-mode.c
+shutdown_opt_mode_LDADD = $(top_builddir)/lib/libnbd.la
+
 get_size_SOURCES = get-size.c
 get_size_LDADD = $(top_builddir)/lib/libnbd.la

diff --git a/tests/shutdown-opt-mode.c b/tests/shutdown-opt-mode.c
new file mode 100644
index 00000000..34386220
--- /dev/null
+++ b/tests/shutdown-opt-mode.c
@@ -0,0 +1,124 @@
+/* NBD client library in userspace
+ * Copyright Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Test shutdown in relation to opt mode.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <libnbd.h>
+
+static const char *progname;
+
+int
+main (int argc, char *argv[])
+{
+  struct nbd_handle *nbd;
+  const char *cmd_old[] = { "nbdkit", "--oldstyle",
"-s", "--exit-with-parent",
+                            "memory", "size=2m", NULL };
+  const char *cmd_new[] = { "nbdkit", "-s",
"--exit-with-parent",
+                            "memory", "size=2m", NULL };
+
+  progname = argv[0];
+
+  /* Part 1: Request opt mode. With oldstyle, it is not possible. */
+  nbd = nbd_create ();
+  if (nbd == NULL) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_set_opt_mode (nbd, true) == -1) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_connect_command (nbd, (char **)cmd_old) == -1) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_aio_is_ready (nbd) != 1) {
+    fprintf (stderr, "%s: unexpected state\n", progname);
+    exit (EXIT_FAILURE);
+  }
+
+  /* opt_abort fails, because we aren't in option negotiation. */
+  if (nbd_opt_abort (nbd) != -1) {
+    fprintf (stderr, "%s: unexpected success of nbd_opt_abort\n",
progname);
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_get_errno () != EINVAL) {
+    fprintf (stderr, "%s: test failed: unexpected errno: %s\n",
+             progname, strerror (nbd_get_errno ()));
+    exit (EXIT_FAILURE);
+  }
+  /* Shutdown will succeed, since opt mode was not possible. */
+  if (nbd_shutdown (nbd, 0) == -1) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  nbd_close (nbd);
+
+  /* Part 2: Request opt mode. With newstyle, it succeeds. */
+  nbd = nbd_create ();
+  if (nbd == NULL) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_set_opt_mode (nbd, true) == -1) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_connect_command (nbd, (char **)cmd_new) == -1) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_aio_is_negotiating (nbd) != 1) {
+    fprintf (stderr, "%s: unexpected state\n", progname);
+    exit (EXIT_FAILURE);
+  }
+
+  /* Shutdown fails, because we did not pass flag. */
+  if (nbd_shutdown (nbd, 0) != -1) {
+    fprintf (stderr, "%s: test failed: nbd_shutdown unexpectedly
worked\n",
+             progname);
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_get_errno () != EINVAL) {
+    fprintf (stderr, "%s: test failed: unexpected errno: %s\n",
+             progname, strerror (nbd_get_errno ()));
+    exit (EXIT_FAILURE);
+  }
+  /* But we can manually call nbd_opt_abort, which closes gracefully. */
+  if (nbd_opt_abort (nbd) == -1) {
+    fprintf (stderr, "%s: %s\n", progname, nbd_get_error ());
+    exit (EXIT_FAILURE);
+  }
+  if (nbd_aio_is_closed (nbd) != 1) {
+    fprintf (stderr, "%s: unexpected state\n", progname);
+    exit (EXIT_FAILURE);
+  }
+  nbd_close (nbd);
+
+  /* Part 3: TODO: add flag to nbd_shutdown */
+  exit (EXIT_SUCCESS);
+}
diff --git a/.gitignore b/.gitignore
index 866d745a..bb2cdf99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -268,6 +268,7 @@ Makefile.in
 /tests/read-write-flag
 /tests/server-death
 /tests/shutdown-flags
+/tests/shutdown-opt-mode
 /tests/socket-activation-name
 /tests/synch-parallel
 /tests/synch-parallel-tls
--
2.41.0