[Libguestfs] [libnbd PATCH v2 03/13] api: Add nbd_is_state_negotiating for new state

Eric Blake
Fri, 14 Aug 2020 22:00:22 -0000

This patch prepares for a change to export list extraction by adding a
new state in the state machine, NEGOTIATING.  At present, it is not
actually possible to enter this state.  But upcoming patches will
allow the user to opt-in to fine-grained control over negotiation
during the connecting phase.  Since the handshake phase is synchronous
(the client sends a full command, then waits for the full server
response, with no interleaving or out-of-order traffic), the idea is
that we can drop out of connecting over to Negotiating any time we are
waiting for the user to determine the next client command to send, and
then back to connecting until that option is handled; where unlike the
data phase, we won't need a cookie.  Other upcoming patches will then
adjust existing API that make sense to call while in this state, as
well as adding new API to give the user control over when to actually
use this state.
---
 lib/internal.h             |  1 +
 generator/API.ml           | 17 +++++++++++++++++
 generator/API.mli          |  1 +
 generator/C.ml             |  4 +++-
 generator/state_machine.ml |  7 +++++++
 lib/connect.c              |  5 +++--
 lib/is-state.c             | 14 +++++++++++++-
 7 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/lib/internal.h b/lib/internal.h
index aa7e9a2..186d677 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -410,6 +410,7 @@ extern int nbd_internal_set_block_size (struct nbd_handle
*h, uint32_t min,
 /* is-state.c */
 extern bool nbd_internal_is_state_created (enum state state);
 extern bool nbd_internal_is_state_connecting (enum state state);
+extern bool nbd_internal_is_state_negotiating (enum state state);
 extern bool nbd_internal_is_state_ready (enum state state);
 extern bool nbd_internal_is_state_processing (enum state state);
 extern bool nbd_internal_is_state_dead (enum state state);
diff --git a/generator/API.ml b/generator/API.ml
index 0c8ef30..0990d48 100644
--- a/generator/API.ml
+++ b/generator/API.ml
@@ -90,6 +90,7 @@ and flags = {
 and permitted_state  | Created
 | Connecting
+| Negotiating
 | Connected
 | Closed | Dead
 and link @@ -2136,6 +2137,21 @@ issue commands (see
L<nbd_aio_is_ready(3)>).";
     see_also = [Link "aio_is_ready"];
   };

+  "aio_is_negotiating", {
+    default_call with
+    args = []; ret = RBool; is_locked = false; may_set_error = false;
+    shortdesc = "check if connection is ready to send handshake
option";
+    longdesc = "\
+Return true if this connection is ready to start another option
+negotiation command while handshaking with the server.  An option
+command will move back to the connecting state (see
+L<nbd_aio_is_connecting(3)>).  Note that this state cannot be
+reached unless requested by nbd_set_opt_mode, and even then
+it only works with newstyle servers; an oldstyle server will skip
+straight to L<nbd_aio_is_ready(3)>.";
+    see_also = [Link "aio_is_connecting"; Link
"aio_is_ready"];
+  };
+
   "aio_is_ready", {
     default_call with
     args = []; ret = RBool; is_locked = false; may_set_error = false;
@@ -2444,6 +2460,7 @@ let first_version = [
   "get_full_info", (1, 4);
   "get_canonical_export_name", (1, 4);
   "get_export_description", (1, 4);
+  "aio_is_negotiating", (1, 4);

   (* These calls are proposed for a future version of libnbd, but
    * have not been added to any released version so far.
diff --git a/generator/API.mli b/generator/API.mli
index 3dbd1ff..712e837 100644
--- a/generator/API.mli
+++ b/generator/API.mli
@@ -102,6 +102,7 @@ and flags = {
 and permitted_state  | Created                  (** can be called in the START
state *)
 | Connecting               (** can be called when connecting/handshaking *)
+| Negotiating              (** can be called when handshaking in opt_mode *)
 | Connected                (** when connected and READY or processing, but
                                not including CLOSED or DEAD *)
 | Closed | Dead            (** can be called when the handle is
diff --git a/generator/C.ml b/generator/C.ml
index afb8142..6b65f6e 100644
--- a/generator/C.ml
+++ b/generator/C.ml
@@ -397,7 +397,8 @@ let permitted_state_text permitted_states           function
          | Created -> "newly created"
          | Connecting -> "connecting"
-         | Connected -> "connected and finished handshaking with the
server"
+         | Negotiating -> "negotiating"
+         | Connected -> "connected with the server"
          | Closed -> "shut down"
          | Dead -> "dead"
        ) permitted_states
@@ -421,6 +422,7 @@ let generate_lib_api_c ()            function
           | Created -> "nbd_internal_is_state_created (state)"
           | Connecting -> "nbd_internal_is_state_connecting
(state)"
+          | Negotiating -> "nbd_internal_is_state_negotiating
(state)"
           | Connected -> "nbd_internal_is_state_ready (state) ||
nbd_internal_is_state_processing (state)"
           | Closed -> "nbd_internal_is_state_closed (state)"
           | Dead -> "nbd_internal_is_state_dead (state)"
diff --git a/generator/state_machine.ml b/generator/state_machine.ml
index 144a5f8..8525520 100644
--- a/generator/state_machine.ml
+++ b/generator/state_machine.ml
@@ -100,6 +100,13 @@ let rec state_machine = [
   Group ("OLDSTYLE", oldstyle_state_machine);
   Group ("NEWSTYLE", newstyle_state_machine);

+  State {
+    default_state with
+    name = "NEGOTIATING";
+    comment = "Connection is ready to negotiate an NBD option";
+    external_events = [ CmdIssue, "NEWSTYLE.START" ];
+  };
+
   State {
     default_state with
     name = "READY";
diff --git a/lib/connect.c b/lib/connect.c
index 92ed6b1..7e42b14 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -1,5 +1,5 @@
 /* NBD client library in userspace
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 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
@@ -40,7 +40,8 @@
 static int
 error_unless_ready (struct nbd_handle *h)
 {
-  if (nbd_internal_is_state_ready (get_next_state (h)))
+  if (nbd_internal_is_state_ready (get_next_state (h)) ||
+      nbd_internal_is_state_negotiating (get_next_state (h)))
     return 0;

   /* Why did it fail? */
diff --git a/lib/is-state.c b/lib/is-state.c
index 1a85c7a..e019e53 100644
--- a/lib/is-state.c
+++ b/lib/is-state.c
@@ -1,5 +1,5 @@
 /* NBD client library in userspace
- * Copyright (C) 2013-2019 Red Hat Inc.
+ * Copyright (C) 2013-2020 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
@@ -58,6 +58,12 @@ nbd_internal_is_state_connecting (enum state state)
   return is_connecting_group (group);
 }

+bool
+nbd_internal_is_state_negotiating (enum state state)
+{
+  return state == STATE_NEGOTIATING;
+}
+
 bool
 nbd_internal_is_state_ready (enum state state)
 {
@@ -120,6 +126,12 @@ nbd_unlocked_aio_is_connecting (struct nbd_handle *h)
   return nbd_internal_is_state_connecting (get_public_state (h));
 }

+int
+nbd_unlocked_aio_is_negotiating (struct nbd_handle *h)
+{
+  return nbd_internal_is_state_negotiating (get_public_state (h));
+}
+
 int
 nbd_unlocked_aio_is_ready (struct nbd_handle *h)
 {
--
2.28.0