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