diff options
| author | Garrett D'Amore <garrett@damore.org> | 2024-12-30 11:45:36 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2024-12-30 12:12:07 -0800 |
| commit | 9c6b2929d8019dcc11935550bc3520ae39c2964e (patch) | |
| tree | c8311a91c02e7f78dbc8c26d8456b373ef33b5f4 /src/platform/posix | |
| parent | 60c9c1c4054a5dbb3c1cad2068e1a793789618ff (diff) | |
| download | nng-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.c | 3 | ||||
| -rw-r--r-- | src/platform/posix/posix_ipclisten.c | 61 | ||||
| -rw-r--r-- | src/platform/posix/posix_tcplisten.c | 66 |
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, }, }; |
