[Libguestfs] [PATCH libnbd v2 3/3] golang: Add straightforward bindings, without callbacks.

Richard W.M. Jones rjones at redhat.com
Tue Mar 17 21:20:13 UTC 2020


---
 generator/GoLang.ml                           | 336 +++++++++++++++++-
 .../libnbd/libnbd_200_connect_command_test.go |  35 ++
 2 files changed, 367 insertions(+), 4 deletions(-)

diff --git a/generator/GoLang.ml b/generator/GoLang.ml
index 1b8f20b..e5fb97c 100644
--- a/generator/GoLang.ml
+++ b/generator/GoLang.ml
@@ -23,6 +23,304 @@ open Printf
 open API
 open Utils
 
+let go_name_of_arg = function
+  | Bool n -> n
+  | BytesIn (n, len) -> n
+  | BytesOut (n, len) -> n
+  | BytesPersistIn (n, len) -> n
+  | BytesPersistOut (n, len) -> n
+  | Closure { cbname } -> cbname
+  | Enum (n, _) -> n
+  | Fd n -> n
+  | Flags (n, _) -> n
+  | Int n -> n
+  | Int64 n -> n
+  | Path n -> n
+  | SockAddrAndLen (n, len) -> n
+  | String n -> n
+  | StringList n -> n
+  | UInt n -> n
+  | UInt32 n -> n
+  | UInt64 n -> n
+
+let go_arg_type_to_string = function
+  | Bool _ -> "bool"
+  | BytesIn _ -> "*string/*XXX*/"
+  | BytesPersistIn _ -> "*string/*XXX*/"
+  | BytesOut _ -> "*string/*XXX*/"
+  | BytesPersistOut _ -> "*string/*XXX*/"
+  | Closure { cbargs } -> "func (/*XXX*/)"
+  | Enum _ -> "uint32"
+  | Fd _ -> "int/*XXX*/"
+  | Flags _ -> "uint32"
+  | Int _ -> "int"
+  | Int64 _ -> "int64"
+  | Path _ -> "string"
+  | SockAddrAndLen _ -> "string"
+  | String _ -> "string"
+  | StringList _ -> "[]string"
+  | UInt _ -> "uint"
+  | UInt32 _ -> "uint32"
+  | UInt64 _ -> "uint64"
+
+let go_name_of_optarg = function
+  | OClosure { cbname } -> String.capitalize_ascii cbname
+  | OFlags (n, _) -> String.capitalize_ascii n
+
+(* For consistency every method will return (type, error) where
+ * type is defined by this function.  However some functions
+ * never really have a type (RErr) and others never really have
+ * an error (RUInt).
+ *)
+let go_ret_type = function
+  | RBool -> "bool"
+  | RStaticString -> "*string"
+  | RErr -> "int" (* always 0 *)
+  | RFd -> "int/*XXX*/"
+  | RInt -> "int"
+  | RInt64 -> "int64"
+  | RCookie -> "int64"
+  | RString -> "*string"
+  | RUInt -> "uint"
+
+let go_ret_error = function
+  | RBool -> "false"
+  | RStaticString -> "nil"
+  | RErr -> "0"
+  | RFd -> "0/*XXX*/"
+  | RInt -> "0"
+  | RInt64 -> "0"
+  | RCookie -> "0"
+  | RString -> "nil"
+  | RUInt -> "0"
+
+let go_ret_c_errcode = function
+  | RBool -> Some "-1"
+  | RStaticString -> Some "nil"
+  | RErr -> Some "-1"
+  | RFd -> Some "-1/*XXX*/"
+  | RInt -> Some "-1"
+  | RInt64 -> Some "-1"
+  | RCookie -> Some "-1"
+  | RString -> Some "nil"
+  | RUInt -> None
+
+(* We need a wrapper around every function (except Close) to
+ * handle errors because cgo calls are sequence points and
+ * could result in us being rescheduled on another thread,
+ * but libnbd error handling means we must call nbd_get_error
+ * etc from the same thread.
+ *)
+let print_wrapper (name, { args; optargs; ret }) =
+  let ret_c_type = C.type_of_ret ret and errcode = C.errcode_of_ret ret in
+  pr "static %s\n" ret_c_type;
+  pr "_nbd_%s_wrapper (struct error *err,\n" name;
+  pr "        ";
+  C.print_arg_list ~wrap:true ~handle:true ~parens:false args optargs;
+  pr ")\n";
+  pr "{\n";
+  pr "  %s ret;\n" ret_c_type;
+  pr "\n";
+  pr "  ret = nbd_%s " name;
+  C.print_arg_list ~wrap:true ~handle:true ~types:false args optargs;
+  pr ";\n";
+  (match errcode with
+   | None -> ()
+   | Some errcode ->
+      pr "  if (ret == %s)\n" errcode;
+      pr "    save_error (err);\n";
+  );
+  pr "  return ret;\n";
+  pr "}\n";
+  pr "\n"
+
+let print_binding (name, { args; optargs; ret; shortdesc }) =
+  let uname = String.capitalize_ascii name in
+
+  (* Tedious method of passing optional arguments in golang. *)
+  if optargs <> [] then (
+    pr "/* Struct carrying optional arguments for %s. */\n" uname;
+    pr "type Optargs_%s struct {\n" uname;
+    List.iter (
+      fun optarg ->
+        let fname = go_name_of_optarg optarg in
+        pr "  /* %s field is ignored unless %s_is_set == true. */\n"
+          fname fname;
+        pr "  %s_is_set bool\n" fname;
+        pr "  %s " fname;
+        (match optarg with
+         | OClosure { cbargs } -> pr "func (/*XXX*/)"
+         | OFlags _ -> pr "uint32"
+        );
+        pr "\n"
+    ) optargs;
+    pr "}\n";
+    pr "\n";
+  );
+
+  (* Define the golang function which calls the C wrapper. *)
+  pr "/* %s: %s */\n" uname shortdesc;
+  pr "func (h *Libnbd) %s (" uname;
+  let comma = ref false in
+  List.iter (
+    fun arg ->
+      if !comma then pr ", ";
+      comma := true;
+      pr "%s %s" (go_name_of_arg arg) (go_arg_type_to_string arg)
+  ) args;
+  if optargs <> [] then (
+    if !comma then pr ", ";
+    comma := true;
+    pr "optargs *Optargs_%s" uname
+  );
+  pr ") (%s, error)" (go_ret_type ret);
+  pr " {\n";
+  pr "    if h.h == nil {\n";
+  pr "        return %s, closed_handle_error (\"%s\")\n"
+    (go_ret_error ret) name;
+  pr "    }\n";
+  pr "\n";
+  pr "    c_err := C.struct_error{}\n";
+  List.iter (
+    function
+    | Bool n ->
+       pr "    c_%s := C.bool (%s)\n" n n
+    | BytesIn (n, len) ->
+       pr "    var c_%s unsafe.Pointer /* XXX */\n" n;
+       pr "    var c_%s C.size_t /* XXX */\n" len;
+    | BytesOut (n, len) ->
+       pr "    var c_%s unsafe.Pointer /* XXX */\n" n;
+       pr "    var c_%s C.size_t /* XXX */\n" len;
+    | BytesPersistIn (n, len) ->
+       pr "    var c_%s unsafe.Pointer /* XXX */\n" n;
+       pr "    var c_%s C.size_t /* XXX */\n" len;
+    | BytesPersistOut (n, len) ->
+       pr "    var c_%s unsafe.Pointer /* XXX */\n" n;
+       pr "    var c_%s C.size_t /* XXX */\n" len;
+    | Closure { cbname } ->
+       pr "    c_%s := C.nbd_%s_callback{} /* XXX */\n" cbname cbname
+    | Enum (n, _) ->
+       pr "    c_%s := C.int (%s)\n" n n
+    | Fd n ->
+       pr "    c_%s := C.int (%s) /* XXX */\n" n n
+    | Flags (n, _) ->
+       pr "    c_%s := C.uint32_t (%s)\n" n n
+    | Int n ->
+       pr "    c_%s := C.int (%s)\n" n n
+    | Int64 n ->
+       pr "    c_%s := C.int64_t (%s)\n" n n
+    | Path n ->
+       pr "    c_%s := C.CString (%s)\n" n n;
+       pr "    defer C.free (unsafe.Pointer (c_%s))\n" n
+    | SockAddrAndLen (n, len) ->
+       pr "    panic (\"SockAddrAndLen not supported\")\n";
+       pr "    var c_%s *C.struct_sockaddr\n" n;
+       pr "    var c_%s C.uint\n" len
+    | String n ->
+       pr "    c_%s := C.CString (%s)\n" n n;
+       pr "    defer C.free (unsafe.Pointer (c_%s))\n" n
+    | StringList n ->
+       pr "    c_%s := arg_string_list (%s)\n" n n;
+       pr "    defer free_string_list (c_%s)\n" n
+    | UInt n ->
+       pr "    c_%s := C.uint (%s)\n" n n
+    | UInt32 n ->
+       pr "    c_%s := C.uint32_t (%s)\n" n n
+    | UInt64 n ->
+       pr "    c_%s := C.uint64_t (%s)\n" n n
+  ) args;
+  if optargs <> [] then (
+    List.iter (
+      function
+      | OClosure { cbname } ->
+         pr "    c_%s := C.nbd_%s_callback{} /* XXX */\n" cbname cbname
+      | OFlags (n, _) ->
+         pr "    var c_%s C.uint32_t\n" n
+    ) optargs;
+    pr "    if optargs != nil {\n";
+    List.iter (
+      fun optarg ->
+         pr "        if optargs.%s_is_set {\n" (go_name_of_optarg optarg);
+         (match optarg with
+          | OClosure { cbname } -> () (* XXX *)
+          | OFlags (n, _) ->
+             pr "            c_%s = C.uint32_t (optargs.%s)\n"
+               n (go_name_of_optarg optarg);
+         );
+         pr "        }\n";
+    ) optargs;
+    pr "    }\n";
+  );
+  pr "\n";
+  pr "    ret := C._nbd_%s_wrapper (&c_err, h.h" name;
+  List.iter (
+    function
+    | Bool n -> pr ", c_%s" n
+    | BytesIn (n, len) -> pr ", c_%s, c_%s" n len
+    | BytesOut (n, len) ->  pr ", c_%s, c_%s" n len
+    | BytesPersistIn (n, len) ->  pr ", c_%s, c_%s" n len
+    | BytesPersistOut (n, len) ->  pr ", c_%s, c_%s" n len
+    | Closure { cbname } ->  pr ", c_%s" cbname
+    | Enum (n, _) -> pr ", c_%s" n
+    | Fd n -> pr ", c_%s" n
+    | Flags (n, _) -> pr ", c_%s" n
+    | Int n -> pr ", c_%s" n
+    | Int64 n -> pr ", c_%s" n
+    | Path n -> pr ", c_%s" n
+    | SockAddrAndLen (n, len) -> pr ", c_%s, c_%s" n len
+    | String n -> pr ", c_%s" n
+    | StringList n -> pr ", c_%s" n
+    | UInt n -> pr ", c_%s" n
+    | UInt32 n -> pr ", c_%s" n
+    | UInt64 n -> pr ", c_%s" n
+  ) args;
+  List.iter (
+    function
+    | OClosure { cbname} -> pr ", c_%s" cbname
+    | OFlags (n, _) -> pr ", c_%s" n
+  ) optargs;
+  pr ")\n";
+  let errcode = go_ret_c_errcode ret in
+  (match errcode with
+   | None -> ()
+   | Some errcode ->
+      pr "    if ret == %s {\n" errcode;
+      pr "        err := get_error (\"%s\", c_err)\n" name;
+      pr "        C.free_error (&c_err)\n";
+      pr "        return %s, err\n" (go_ret_error ret);
+      pr "    }\n";
+  );
+  (match ret with
+   | RBool ->
+      pr "    r := int (ret)\n";
+      pr "    if r != 0 { return true, nil } else { return false, nil }\n"
+   | RStaticString ->
+      pr "    /* ret is statically allocated, do not free it. */\n";
+      pr "    r := C.GoString (ret);\n";
+      pr "    return &r, nil\n"
+   | RErr ->
+      pr "    /* Note that the return value 0 has no meaning here.\n";
+      pr "     * It is only returned for consistency.\n";
+      pr "     */\n";
+      pr "    return 0, nil\n"
+   | RFd ->
+      pr "    return int/*XXX*/ (ret), nil\n"
+   | RInt ->
+      pr "    return int (ret), nil\n"
+   | RInt64 ->
+      pr "    return int64 (ret), nil\n"
+   | RCookie ->
+      pr "    return int64 (ret), nil\n"
+   | RString ->
+      pr "    r := C.GoString (ret)\n";
+      pr "    C.free (unsafe.Pointer (ret))\n";
+      pr "    return &r, nil\n"
+   | RUInt ->
+      pr "    return uint (ret), nil\n"
+  );
+  pr "}\n";
+  pr "\n"
+
 let generate_golang_libnbd_go () =
   generate_header CStyle;
 
@@ -62,14 +360,20 @@ free_error (struct error *err)
 static struct nbd_handle *
 _nbd_create_wrapper (struct error *err)
 {
-  struct nbd_handle *r;
+  struct nbd_handle *ret;
 
-  r = nbd_create ();
-  if (r == NULL)
+  ret = nbd_create ();
+  if (ret == NULL)
     save_error (err);
-  return r;
+  return ret;
 }
 
+";
+
+  (* Wrappers. *)
+  List.iter print_wrapper handle_calls;
+
+  pr "\
 // There must be no blank line between end comment and import!
 // https://github.com/golang/go/issues/9733
 */
@@ -79,6 +383,7 @@ import (
     \"fmt\"
     \"runtime\"
     \"syscall\"
+    \"unsafe\"
 )
 
 /* Handle. */
@@ -147,4 +452,27 @@ func (h *Libnbd) Close () *LibnbdError {
     return nil
 }
 
+/* Functions for translating between NULL-terminated lists of
+ * C strings and golang []string.
+ */
+func arg_string_list (xs []string) **C.char {
+    r := make ([]*C.char, 1 + len (xs))
+    for i, x := range xs {
+        r[i] = C.CString (x)
+    }
+    r[len (xs)] = nil
+    return &r[0]
+}
+
+func free_string_list (argv **C.char) {
+    for *argv != nil {
+        //C.free (*argv)
+        argv = (**C.char) (unsafe.Pointer (uintptr (unsafe.Pointer (argv)) +
+                                           unsafe.Sizeof (*argv)))
+    }
+}
+
 ";
+
+  (* Bindings. *)
+  List.iter print_binding handle_calls;
diff --git a/golang/src/libguestfs.org/libnbd/libnbd_200_connect_command_test.go b/golang/src/libguestfs.org/libnbd/libnbd_200_connect_command_test.go
new file mode 100644
index 0000000..9faf244
--- /dev/null
+++ b/golang/src/libguestfs.org/libnbd/libnbd_200_connect_command_test.go
@@ -0,0 +1,35 @@
+/* libnbd golang tests
+ * Copyright (C) 2013-2020 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+package libnbd
+
+import "testing"
+
+func Test200ConnectCommand (t *testing.T) {
+	h, err := Create ()
+	if err != nil {
+		t.Errorf ("could not create handle: %s", err)
+	}
+        _, err = h.Connect_command ([]string{
+            "nbdkit", "-s", "--exit-with-parent", "-v", "null",
+        })
+        if err != nil {
+		t.Errorf ("could not connect: %s", err)
+        }
+	h.Close ()
+}
-- 
2.25.0




More information about the Libguestfs mailing list