[Libguestfs] [PATCH libnbd] python: Implement nbd.aio_connect for AF_UNIX

Richard W.M. Jones rjones at redhat.com
Wed Jun 30 19:22:19 UTC 2021


This call previously just called abort().  Implement it for Unix
domain sockets (the easy case).  Implementing it for AF_INET and
AF_INET6 is more complicated so that is left as a to-do.

Note also that implementing this fully for Python is a bit pointless.
It would be easier for a Python program to call
nbd.aio_connect_tcp(host, port) instead of calling
nbd.aio_connect((host, port)).  Both cases would do hostname lookups
but the former is already implemented.
---
 generator/Python.ml               | 17 +++++----
 python/Makefile.am                |  6 ++-
 python/python-aio-connect-unix.sh | 36 ++++++++++++++++++
 python/utils.c                    | 63 +++++++++++++++++++++++++++++++
 4 files changed, 114 insertions(+), 8 deletions(-)

diff --git a/generator/Python.ml b/generator/Python.ml
index 25f92d7..bcfd4bf 100644
--- a/generator/Python.ml
+++ b/generator/Python.ml
@@ -41,6 +41,8 @@ struct py_aio_buffer {
 
 extern char **nbd_internal_py_get_string_list (PyObject *);
 extern void nbd_internal_py_free_string_list (char **);
+extern int nbd_internal_py_get_sockaddr (PyObject *,
+    struct sockaddr_storage *, socklen_t *);
 extern struct py_aio_buffer *nbd_internal_py_get_aio_buffer (PyObject *);
 
 static inline struct nbd_handle *
@@ -300,10 +302,9 @@ let print_python_binding name { args; optargs; ret; may_set_error } =
     | SizeT n ->
        pr "  Py_ssize_t %s;\n" n
     | SockAddrAndLen (n, _) ->
-       pr "  /* XXX Complicated - Python uses a tuple of different\n";
-       pr "   * lengths for the different socket types.\n";
-       pr "   */\n";
-       pr "  PyObject *%s;\n" n
+       pr "  PyObject *%s;\n" n;
+       pr "  struct sockaddr_storage %s_sa;\n" n;
+       pr "  socklen_t %s_len;\n" n;
     | String n -> pr "  const char *%s;\n" n
     | StringList n ->
        pr "  PyObject *py_%s;\n" n;
@@ -440,8 +441,10 @@ let print_python_binding name { args; optargs; ret; may_set_error } =
        pr "  %s = PyBytes_AS_STRING (py_%s);\n" n n;
        pr "  assert (%s != NULL);\n" n
     | SizeT n -> ()
-    | SockAddrAndLen _ ->
-       pr "  abort (); /* XXX SockAddrAndLen not implemented */\n";
+    | SockAddrAndLen (n, _) ->
+       pr "  if (nbd_internal_py_get_sockaddr (%s, &%s_sa, &%s_len) == -1)\n"
+         n n n;
+       pr "    goto out;\n"
     | String _ -> ()
     | StringList n ->
        pr "  %s = nbd_internal_py_get_string_list (py_%s);\n" n n;
@@ -468,7 +471,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } =
     | Int64 n -> pr ", %s_i64" n
     | Path n -> pr ", %s" n
     | SizeT n -> pr ", (size_t)%s" n
-    | SockAddrAndLen (n, _) -> pr ", /* XXX */ (void *) %s, 0" n
+    | SockAddrAndLen (n, _) -> pr ", (struct sockaddr *) &%s_sa, %s_len" n n
     | String n -> pr ", %s" n
     | StringList n -> pr ", %s" n
     | UInt n | UIntPtr n -> pr ", %s" n
diff --git a/python/Makefile.am b/python/Makefile.am
index 718f891..3779410 100644
--- a/python/Makefile.am
+++ b/python/Makefile.am
@@ -81,9 +81,13 @@ TESTS_ENVIRONMENT = \
 	LIBNBD_DEBUG=1 \
 	MALLOC_CHECK_=1 \
 	MALLOC_PERTURB_=$(shell bash -c 'echo $$(( 1 + (RANDOM & 255) ))') \
+	PYTHON="$(PYTHON)" \
 	$(NULL)
 LOG_COMPILER = $(top_builddir)/run
-TESTS += run-python-tests
+TESTS += \
+	python-aio-connect-unix.sh \
+	run-python-tests \
+	$(NULL)
 
 endif HAVE_NBDKIT
 
diff --git a/python/python-aio-connect-unix.sh b/python/python-aio-connect-unix.sh
new file mode 100755
index 0000000..7be0f87
--- /dev/null
+++ b/python/python-aio-connect-unix.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+# nbd client library in userspace
+# Copyright (C) 2020-2021 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
+# 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
+
+. ../tests/functions.sh
+
+set -e
+set -x
+
+requires nbdkit --version
+requires $PYTHON --version
+
+nbdkit -U - null --run '$PYTHON -c "
+import nbd
+import sys
+h = nbd.NBD()
+addr = sys.argv[1]
+h.aio_connect(addr)
+while h.aio_is_connecting():
+    h.poll(1)
+assert h.aio_is_ready()
+" "$unixsocket"'
diff --git a/python/utils.c b/python/utils.c
index 0e3164c..37f0c55 100644
--- a/python/utils.c
+++ b/python/utils.c
@@ -26,6 +26,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 #include <libnbd.h>
 
@@ -92,3 +94,64 @@ nbd_internal_py_free_string_list (char **argv)
     free (argv[i]);
   free (argv);
 }
+
+/* Convert a Python object into a struct sockaddr, according to the
+ * general rules described here:
+ * https://docs.python.org/3/library/socket.html
+ *
+ * There is a function in cpython called getsockaddrarg which roughly
+ * does the same thing, but in cpython they know the socket family
+ * already (which we do not).  In any case that function cannot be
+ * called directly.
+ */
+int
+nbd_internal_py_get_sockaddr (PyObject *addr,
+                              struct sockaddr_storage *ss, socklen_t *len)
+{
+  memset (ss, 0, sizeof *ss);
+
+  if (PyUnicode_Check (addr)) { /* AF_UNIX */
+    struct sockaddr_un *sun = (struct sockaddr_un *)ss;
+    const char *unixsocket;
+    size_t namelen;
+
+    sun->sun_family = AF_UNIX;
+    unixsocket = PyUnicode_AsUTF8 (addr);
+    if (!unixsocket)
+      goto err;
+    namelen = strlen (unixsocket);
+    if (namelen > sizeof sun->sun_path) {
+      PyErr_SetString (PyExc_RuntimeError,
+                       "get_sockaddr: Unix domain socket name too long");
+      return -1;
+    }
+    memcpy (sun->sun_path, unixsocket, namelen);
+    *len = sizeof *sun;
+    return 0;
+  }
+
+#if 0
+  else if (PyTuple_Check (addr)) {
+    Py_ssize_t n = PyTuple_Size (addr);
+
+    switch (n) {
+    case 2:                     /* AF_INET */
+      /* XXX TODO */
+      break;
+
+    case 4:                     /* AF_INET6 */
+      /* XXX TODO */
+      break;
+
+    default:
+      goto err;
+    }
+  }
+#endif
+
+  else {
+  err:
+    PyErr_SetString (PyExc_TypeError, "get_sockaddr: unknown address type");
+    return -1;
+  }
+}
-- 
2.32.0




More information about the Libguestfs mailing list