aboutsummaryrefslogtreecommitdiff
path: root/src/platform/posix
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2024-12-30 11:45:36 -0800
committerGarrett D'Amore <garrett@damore.org>2024-12-30 12:12:07 -0800
commit9c6b2929d8019dcc11935550bc3520ae39c2964e (patch)
treec8311a91c02e7f78dbc8c26d8456b373ef33b5f4 /src/platform/posix
parent60c9c1c4054a5dbb3c1cad2068e1a793789618ff (diff)
downloadnng-9c6b2929d8019dcc11935550bc3520ae39c2964e.tar.gz
nng-9c6b2929d8019dcc11935550bc3520ae39c2964e.tar.bz2
nng-9c6b2929d8019dcc11935550bc3520ae39c2964e.zip
fixes #863 socket activation: for TCP and IPC (POSIX only)
This introduces a new option "NNG_OPT_LISTEN_FD", understood by TCP, TLS, and (on POSIX systems) IPC. This option is used to pass a file descriptor or handle (Windows) that is already listening (ready for ACCEPT to be called). For TCP and TLS, the socket must be of type AF_INET or AF_INET6, and for IPC it must be of type AF_UNIX.
Diffstat (limited to 'src/platform/posix')
-rw-r--r--src/platform/posix/posix_debug.c3
-rw-r--r--src/platform/posix/posix_ipclisten.c61
-rw-r--r--src/platform/posix/posix_tcplisten.c66
3 files changed, 130 insertions, 0 deletions
diff --git a/src/platform/posix/posix_debug.c b/src/platform/posix/posix_debug.c
index 912000cc..3c0a6604 100644
--- a/src/platform/posix/posix_debug.c
+++ b/src/platform/posix/posix_debug.c
@@ -90,6 +90,9 @@ static struct {
{ ENFILE, NNG_ENOFILES },
{ EMFILE, NNG_ENOFILES },
{ EEXIST, NNG_EEXIST },
+#ifdef ENOTSOCK
+ { ENOTSOCK, NNG_EINVAL },
+#endif
// must be last
{ 0, 0 },
// clang-format on
diff --git a/src/platform/posix/posix_ipclisten.c b/src/platform/posix/posix_ipclisten.c
index aa6ffda8..d13ac063 100644
--- a/src/platform/posix/posix_ipclisten.c
+++ b/src/platform/posix/posix_ipclisten.c
@@ -252,6 +252,60 @@ ipc_listener_set_perms(void *arg, const void *buf, size_t sz, nni_type t)
return (0);
}
+static int
+ipc_listener_set_listen_fd(void *arg, const void *buf, size_t sz, nni_type t)
+{
+ ipc_listener *l = arg;
+ int fd;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+ int rv;
+
+ if ((rv = nni_copyin_int(&fd, buf, sz, 0, NNI_MAXINT, t)) != 0) {
+ return (rv);
+ }
+
+ if (getsockname(fd, (void *) &ss, &len) != 0) {
+ return (nni_plat_errno(errno));
+ }
+
+ if (((nni_posix_sockaddr2nn(&l->sa, &ss, len)) != 0) ||
+ (ss.ss_family != AF_UNIX)) {
+ return (NNG_EADDRINVAL);
+ }
+
+ nni_mtx_lock(&l->mtx);
+ if (l->started) {
+ nni_mtx_unlock(&l->mtx);
+ return (NNG_EBUSY);
+ }
+ if (l->closed) {
+ nni_mtx_unlock(&l->mtx);
+ return (NNG_ECLOSED);
+ }
+ nni_posix_pfd_init(&l->pfd, fd, ipc_listener_cb, l);
+ l->started = true;
+ nni_mtx_unlock(&l->mtx);
+ return (0);
+}
+
+#ifdef NNG_TEST_LIB
+// this is readable only for test code -- user code should never rely on this
+static int
+ipc_listener_get_listen_fd(void *arg, void *buf, size_t *szp, nni_type t)
+{
+ int rv;
+ ipc_listener *l = arg;
+
+ nni_mtx_lock(&l->mtx);
+ NNI_ASSERT(l->started);
+ NNI_ASSERT(!l->closed);
+ rv = nni_copyout_int(nni_posix_pfd_fd(&l->pfd), buf, szp, t);
+ nni_mtx_unlock(&l->mtx);
+ return (rv);
+}
+#endif
+
static const nni_option ipc_listener_options[] = {
{
.o_name = NNG_OPT_LOCADDR,
@@ -262,6 +316,13 @@ static const nni_option ipc_listener_options[] = {
.o_set = ipc_listener_set_perms,
},
{
+ .o_name = NNG_OPT_LISTEN_FD,
+ .o_set = ipc_listener_set_listen_fd,
+#ifdef NNG_TEST_LIB
+ .o_get = ipc_listener_get_listen_fd,
+#endif
+ },
+ {
.o_name = NULL,
},
};
diff --git a/src/platform/posix/posix_tcplisten.c b/src/platform/posix/posix_tcplisten.c
index 6e7f4d14..29b5ea65 100644
--- a/src/platform/posix/posix_tcplisten.c
+++ b/src/platform/posix/posix_tcplisten.c
@@ -10,6 +10,7 @@
//
#include "core/nng_impl.h"
+#include "nng/nng.h"
#include <arpa/inet.h>
#include <errno.h>
@@ -402,6 +403,64 @@ tcp_listener_get_port(void *arg, void *buf, size_t *szp, nni_type t)
return (nni_copyout_int(port, buf, szp, t));
}
+static int
+tcp_listener_set_listen_fd(void *arg, const void *buf, size_t sz, nni_type t)
+{
+ tcp_listener *l = arg;
+ int fd;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+ int rv;
+
+ if ((rv = nni_copyin_int(&fd, buf, sz, 0, NNI_MAXINT, t)) != 0) {
+ return (rv);
+ }
+
+ if (getsockname(fd, (void *) &ss, &len) != 0) {
+ return (nni_plat_errno(errno));
+ }
+
+ if (((nni_posix_sockaddr2nn(&l->sa, &ss, len)) != 0) ||
+#ifdef NNG_ENABLE_IPV6
+ ((ss.ss_family != AF_INET) && (ss.ss_family != AF_INET6))
+#else
+ (ss.ss_family != AF_INET)
+#endif
+ ) {
+ return (NNG_EADDRINVAL);
+ }
+
+ nni_mtx_lock(&l->mtx);
+ if (l->started) {
+ nni_mtx_unlock(&l->mtx);
+ return (NNG_EBUSY);
+ }
+ if (l->closed) {
+ nni_mtx_unlock(&l->mtx);
+ return (NNG_ECLOSED);
+ }
+ nni_posix_pfd_init(&l->pfd, fd, tcp_listener_cb, l);
+ l->started = true;
+ nni_mtx_unlock(&l->mtx);
+ return (0);
+}
+
+#ifdef NNG_TEST_LIB
+// this is readable only for test code -- user code should never rely on this
+static int
+tcp_listener_get_listen_fd(void *arg, void *buf, size_t *szp, nni_type t)
+{
+ int rv;
+ tcp_listener *l = arg;
+ nni_mtx_lock(&l->mtx);
+ NNI_ASSERT(l->started);
+ NNI_ASSERT(!l->closed);
+ rv = nni_copyout_int(nni_posix_pfd_fd(&l->pfd), buf, szp, t);
+ nni_mtx_unlock(&l->mtx);
+ return (rv);
+}
+#endif
+
static const nni_option tcp_listener_options[] = {
{
.o_name = NNG_OPT_LOCADDR,
@@ -422,6 +481,13 @@ static const nni_option tcp_listener_options[] = {
.o_get = tcp_listener_get_port,
},
{
+ .o_name = NNG_OPT_LISTEN_FD,
+ .o_set = tcp_listener_set_listen_fd,
+#ifdef NNG_TEST_LIB
+ .o_get = tcp_listener_get_listen_fd,
+#endif
+ },
+ {
.o_name = NULL,
},
};