[Libguestfs] [PATCH libnbd 3/9] generator: Add Enum type for enumerated types / unions.

Richard W.M. Jones
2019-08-10T13:02:42Z

Previously nbd_set_tls had an integer argument which was 0 for
disable, 1 for allow and 2 for require.  This commit adds a proper
enumerated type to describe this, defining LIBNBD_TLS_DISABLE = 0,
LIBNBD_TLS_ALLOW = 1 and LIBNBD_TLS_REQUIRE = 2.  (Note the C API
doesn't change).

In C the enumerated type is still defined and passed as an int (not as
an enum).  While we could define an enum type for this, there are ABI
stability problems inherent in enums in C.

In OCaml this is implemented as a variant type.

There is no equivalent for returning an enum (eg. for nbd_get_tls).
We should add that later.  It won't affect the C API but would change
the OCaml API.
---
 TODO                |   2 -
 generator/generator | 108 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 104 insertions(+), 6 deletions(-)

diff --git a/TODO b/TODO
index 65c95ee..8e067c0 100644
--- a/TODO
+++ b/TODO
@@ -35,8 +35,6 @@ Suggested API improvements:
   - nbd_connect_command: allow passing char **env

   TLS:
-  - nbd_set_tls: either remove optiona (1/2) interface, or
-    define symbols for it
   - should be individual APIs for setting each TLS file
     (set_tls_certificates can continue to exist)
   - TLS should have a way to pass in PIN or password to
diff --git a/generator/generator b/generator/generator
index 5823686..73b8b79 100755
--- a/generator/generator
+++ b/generator/generator
@@ -855,6 +855,7 @@ and arg  | BytesPersistIn of string * string (* same as
above, but buffer persists *)
 | BytesPersistOut of string * string
 | Closure of closure       (* function pointer + void *opaque *)
+| Enum of string * enum    (* enum/union type, int in C *)
 | Int of string            (* small int *)
 | Int64 of string          (* 64 bit signed int *)
 | Path of string           (* filename or path *)
@@ -890,6 +891,10 @@ and cbarg  | CBString of string       (* like String *)
 | CBUInt of string         (* like UInt *)
 | CBUInt64 of string       (* like UInt64 *)
+and enum = {
+  enum_prefix : string;    (* prefix of each enum variant *)
+  enums : (string * int) list (* enum names and their values in C *)
+}
 and flags = {
   flag_prefix : string;    (* prefix of each flag name *)
   flags : (string * int) list (* flag names and their values in C *)
@@ -910,6 +915,17 @@ let non_blocking_test_call_description = "\n
 This call does not block, because it returns data that is saved in
 the handle from the NBD protocol handshake."

+(* Enums. *)
+let tls_enum = {
+  enum_prefix = "TLS";
+  enums = [
+    "DISABLE", 0;
+    "ALLOW",   1;
+    "REQUIRE", 2;
+  ]
+}
+let all_enums = [ tls_enum ]
+
 (* Flags. *)
 let cmd_flags = {
   flag_prefix = "CMD_FLAG";
@@ -1020,7 +1036,7 @@ Get the export name associated with the handle.";

   "set_tls", {
     default_call with
-    args = [Int "tls"]; ret = RErr;
+    args = [Enum ("tls", tls_enum)]; ret = RErr;
     permitted_states = [ Created ];
     shortdesc = "enable or require TLS (authentication and
encryption)";
     longdesc = "\
@@ -1029,12 +1045,12 @@ NBD server.  The possible settings are:

 =over 4

-=item C<tls=0>
+=item C<LIBNBD_TLS_DISABLE>

 Disable TLS.  (The default setting, unless using C<nbd_connect_uri> with
 a URI that requires TLS)

-=item C<tls=1>
+=item C<LIBNBD_TLS_ALLOW>

 Enable TLS if possible.  In some cases this will fall back
 to an unencrypted and/or unauthenticated connection if
@@ -1042,7 +1058,7 @@ TLS could not be established.  However some servers will
 drop the connection if TLS fails so fallback may not be
 possible.

-=item C<tls=2>
+=item C<LIBNBD_TLS_REQUIRE>

 Require an encrypted and authenticated TLS connection.
 Always fail to connect if the connection is not encrypted
@@ -3307,6 +3323,7 @@ let rec name_of_arg = function
 | BytesPersistOut (n, len) -> [n; len]
 | Closure { cbname } ->
    [ sprintf "%s_callback" cbname; sprintf "%s_user_data"
cbname ]
+| Enum (n, _) -> [n]
 | Int n -> [n]
 | Int64 n -> [n]
 | Path n -> [n]
@@ -3351,6 +3368,9 @@ let rec print_arg_list ?(handle = false) ?(types = true)
args optargs           pr ", ";
          if types then pr "void *";
          pr "%s_user_data" cbname
+      | Enum (n, _) ->
+         if types then pr "int ";
+         pr "%s" n
       | Int n ->
          if types then pr "int ";
          pr "%s" n
@@ -3512,6 +3532,15 @@ let generate_include_libnbd_h ()    pr "\n";
   pr "struct nbd_handle;\n";
   pr "\n";
+  List.iter (
+    fun { enum_prefix; enums } ->
+      List.iter (
+        fun (enum, i) ->
+          let enum = sprintf "LIBNBD_%s_%s" enum_prefix enum in
+          pr "#define %-40s %d\n" enum i
+      ) enums;
+      pr "\n"
+  ) all_enums;
   List.iter (
     fun { flag_prefix; flags } ->
       List.iter (
@@ -3701,6 +3730,7 @@ let generate_lib_api_c ()        | BytesOut (n, count)
       | BytesPersistOut (n, count) -> pr " %s=<buf> %s=%%zu"
n count
       | Closure { cbname } -> pr " %s=<fun>" cbname
+      | Enum (n, _) -> pr " %s=%%d" n
       | Int n -> pr " %s=%%d" n
       | Int64 n -> pr " %s=%%\" PRIi64 \"" n
       | SockAddrAndLen (n, len) -> pr " %s=<sockaddr>
%s=%%d" n len
@@ -3724,6 +3754,7 @@ let generate_lib_api_c ()        | BytesOut (_, count)
       | BytesPersistOut (_, count) -> pr ", %s" count
       | Closure { cbname } -> ()
+      | Enum (n, _) -> pr ", %s" n
       | Int n -> pr ", %s" n
       | Int64 n -> pr ", %s" n
       | SockAddrAndLen (_, len) -> pr ", (int) %s" len
@@ -4206,6 +4237,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }         pr "  struct py_aio_buffer *%s_buf;\n" n
     | Closure { cbname } ->
        pr "  PyObject *%s_user_data;\n" cbname
+    | Enum (n, _) -> pr "  int %s;\n" n
     | Int n -> pr "  int %s;\n" n
     | Int64 n ->
        pr "  int64_t %s_i64;\n" n;
@@ -4248,6 +4280,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }      | BytesOut (_, count) -> pr " \"n\""
     | BytesPersistOut (_, count) -> pr " \"O\""
     | Closure _ -> pr " \"O\""
+    | Enum _ -> pr " \"i\""
     | Int n -> pr " \"i\""
     | Int64 n -> pr " \"L\""
     | Path n -> pr " \"O&\""
@@ -4272,6 +4305,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }      | BytesPersistOut (n, _) -> pr ", &%s" n
     | BytesOut (_, count) -> pr ", &%s" count
     | Closure { cbname } -> pr ", &%s_user_data" cbname
+    | Enum (n, _) -> pr ", &%s" n
     | Int n -> pr ", &%s" n
     | Int64 n -> pr ", &%s" n
     | Path n -> pr ", PyUnicode_FSConverter, &py_%s" n
@@ -4306,6 +4340,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }             pr "                     \"callback
parameter %s is not callable\");\n" cbname;
            pr "    return NULL;\n";
            pr "  }\n"
+    | Enum _ -> ()
     | Int _ -> ()
     | Int64 n -> pr "  %s_i64 = %s;\n" n n
     | Path n ->
@@ -4338,6 +4373,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }      | Closure { cbname } ->
        pr ", %s_%s_wrapper" name cbname;
        pr ", %s_user_data" cbname
+    | Enum (n, _) -> pr ", %s" n
     | Int n -> pr ", %s" n
     | Int64 n -> pr ", %s_i64" n
     | Path n -> pr ", %s" n
@@ -4373,6 +4409,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }      | BytesIn _
     | BytesPersistIn _ | BytesPersistOut _
     | Closure _
+    | Enum _
     | Int _
     | Int64 _
     | Path _
@@ -4413,6 +4450,7 @@ let print_python_binding name { args; optargs; ret;
may_set_error }      | BytesIn (n, _) -> pr "  PyBuffer_Release
(&%s);\n" n
     | BytesPersistIn _ | BytesOut _ | BytesPersistOut _ -> ()
     | Closure _ -> ()
+    | Enum _ -> ()
     | Int _ -> ()
     | Int64 _ -> ()
     | Path n ->
@@ -4505,6 +4543,15 @@ Error.__str__ = _str

 ";

+  List.iter (
+    fun { enum_prefix; enums } ->
+      List.iter (
+        fun (enum, i) ->
+          let enum = sprintf "%s_%s" enum_prefix enum in
+          pr "%-30s = %d\n" enum i
+      ) enums;
+      pr "\n"
+  ) all_enums;
   List.iter (
     fun { flag_prefix; flags } ->
       List.iter (
@@ -4565,6 +4612,7 @@ class NBD (object):
           | BytesPersistOut (n, _) -> n
           | BytesOut (_, count) -> count
           | Closure { cbname } -> cbname
+          | Enum (n, _) -> n
           | Int n -> n
           | Int64 n -> n
           | Path n -> n
@@ -4640,6 +4688,7 @@ and ocaml_arg_to_string = function
   | BytesPersistOut _ -> "Buffer.t"
   | Closure { cbargs } ->
      sprintf "(%s)" (ocaml_closuredecl_to_string cbargs)
+  | Enum (_, { enum_prefix }) -> sprintf "%s.t" enum_prefix
   | Int _ -> "int"
   | Int64 _ -> "int64"
   | Path _ -> "string"
@@ -4686,6 +4735,7 @@ let ocaml_name_of_arg = function
   | BytesPersistIn (n, len) -> n
   | BytesPersistOut (n, len) -> n
   | Closure { cbname } -> cbname
+  | Enum (n, _) -> n
   | Int n -> n
   | Int64 n -> n
   | Path n -> n
@@ -4727,6 +4777,17 @@ exception Closed of string

 ";

+  List.iter (
+    fun { enum_prefix; enums } ->
+      pr "module %s : sig\n" enum_prefix;
+      pr "  type t =\n";
+      List.iter (
+        fun (enum, _) ->
+          pr "  | %s\n" enum
+      ) enums;
+      pr "end\n";
+      pr "\n"
+  ) all_enums;
   List.iter (
     fun { flag_prefix; flags } ->
       pr "module %s : sig\n" flag_prefix;
@@ -4819,6 +4880,17 @@ let ()
 ";

+  List.iter (
+    fun { enum_prefix; enums } ->
+      pr "module %s = struct\n" enum_prefix;
+      pr "  type t =\n";
+      List.iter (
+        fun (enum, _) ->
+          pr "  | %s\n" enum
+      ) enums;
+      pr "end\n";
+      pr "\n"
+  ) all_enums;
   List.iter (
     fun { flag_prefix; flags } ->
       pr "module %s = struct\n" flag_prefix;
@@ -4874,6 +4946,30 @@ external close : t -> unit =
\"nbd_internal_ocaml_nbd_close\"
       pr "\"nbd_internal_ocaml_nbd_%s\"\n" name
   ) handle_calls

+let print_ocaml_enum_val { enum_prefix; enums } +  pr "/* Convert OCaml
%s.t to int. */\n" enum_prefix;
+  pr "static int\n";
+  pr "%s_val (value v)\n" enum_prefix;
+  pr "{\n";
+  pr "  CAMLparam1 (v);\n";
+  pr "  int i, r = 0;\n";
+  pr "\n";
+  pr "  i = Int_val (Field (v, 0));\n";
+  pr "  /* i is the index of the enum in the type\n";
+  pr "   * (eg. i = 0 => enum = %s.%s).\n" enum_prefix (fst
(List.hd enums));
+  pr "   * Convert it to the C representation.\n";
+  pr "   */\n";
+  pr "  switch (i) {\n";
+  List.iteri (
+    fun i (enum, _) ->
+      pr "  case %d: r = LIBNBD_%s_%s; break;\n" i enum_prefix enum
+  ) enums;
+  pr "  }\n";
+  pr "\n";
+  pr "  CAMLreturnT (int, r);\n";
+  pr "}\n";
+  pr "\n"
+
 let print_ocaml_flag_val { flag_prefix; flags }    pr "/* Convert OCaml
%s.t list to uint32_t bitmask. */\n" flag_prefix;
   pr "static uint32_t\n";
@@ -5097,6 +5193,8 @@ let print_ocaml_binding (name, { args; optargs; ret })
pr "  *%s_user_data = %sv;\n" cbname cbname;
        pr "  caml_register_generational_global_root
(%s_user_data);\n" cbname;
        pr "  const void *%s_callback = %s_%s_wrapper;\n" cbname name
cbname
+    | Enum (n, { enum_prefix }) ->
+       pr "  int %s = %s_val (%sv);\n" n enum_prefix n
     | Int n ->
        pr "  int %s = Int_val (%sv);\n" n n
     | Int64 n ->
@@ -5154,6 +5252,7 @@ let print_ocaml_binding (name, { args; optargs; ret })
| BytesOut _
     | BytesPersistOut _
     | Closure _
+    | Enum _
     | Int _
     | Int64 _
     | Path _
@@ -5209,6 +5308,7 @@ let generate_ocaml_nbd_c ()    pr "#pragma GCC
diagnostic ignored \"-Wmissing-prototypes\"\n";
   pr "\n";

+  List.iter print_ocaml_enum_val all_enums;
   List.iter print_ocaml_flag_val all_flags;
   List.iter print_ocaml_binding handle_calls
 end
--
2.22.0