diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-01-11 12:55:46 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-01-12 20:47:24 -0800 |
| commit | d615c5e51268a23887e2d29b5828a0447ba5409b (patch) | |
| tree | b990cbbb5c56c99f445f0989783babd203610daa | |
| parent | 5637b36e2ed59a0c65384804b780e323c9c451c0 (diff) | |
| download | nng-d615c5e51268a23887e2d29b5828a0447ba5409b.tar.gz nng-d615c5e51268a23887e2d29b5828a0447ba5409b.tar.bz2 nng-d615c5e51268a23887e2d29b5828a0447ba5409b.zip | |
Add IPC (UNIX domain sockets) for POSIX, and test suite.
| -rw-r--r-- | src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/core/platform.h | 38 | ||||
| -rw-r--r-- | src/core/transport.c | 2 | ||||
| -rw-r--r-- | src/platform/posix/posix_impl.h | 9 | ||||
| -rw-r--r-- | src/platform/posix/posix_ipc.c | 342 | ||||
| -rw-r--r-- | src/transport/ipc/ipc.c | 375 | ||||
| -rw-r--r-- | src/transport/tcp/tcp.c | 2 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/ipc.c | 18 |
9 files changed, 789 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c1b8e204..4380ff04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,7 @@ set (NNG_SOURCES platform/posix/posix_alloc.c platform/posix/posix_clock.c platform/posix/posix_debug.c + platform/posix/posix_ipc.c platform/posix/posix_net.c platform/posix/posix_rand.c platform/posix/posix_thread.c @@ -86,6 +87,8 @@ set (NNG_SOURCES transport/inproc/inproc.c + transport/ipc/ipc.c + transport/tcp/tcp.c ) diff --git a/src/core/platform.h b/src/core/platform.h index 5a504dec..2955215b 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -64,6 +64,7 @@ typedef struct nni_plat_mtx nni_plat_mtx; typedef struct nni_plat_cv nni_plat_cv; typedef struct nni_plat_thr nni_plat_thr; typedef struct nni_plat_tcpsock nni_plat_tcpsock; +typedef struct nni_plat_ipcsock nni_plat_ipcsock; // Mutex handling. @@ -208,6 +209,43 @@ extern int nni_plat_tcp_send(nni_plat_tcpsock *, nni_iov *, int); // full, or an error condition occurs. extern int nni_plat_tcp_recv(nni_plat_tcpsock *, nni_iov *, int); +// nni_plat_ipc_init initializes the socket, for example it can +// set underlying file descriptors to -1, etc. +extern void nni_plat_ipc_init(nni_plat_ipcsock *); + +// nni_plat_ipc_fini just closes an IPC socket, and releases any related +// resources. +extern void nni_plat_ipc_fini(nni_plat_ipcsock *); + +// nni_plat_ipc_shutdown performs a shutdown of the socket. For +// BSD sockets, this closes both sides of the IPC connection gracefully, +// but the underlying file descriptor is left open. (This part is critical +// to prevention of close() related races.) +extern void nni_plat_ipc_shutdown(nni_plat_ipcsock *); + +// nni_plat_tcp_listen creates an IPC socket in listening mode, bound +// to the specified path. Note that nni_plat_ipcsock should be defined +// to whatever your platform uses. For most systems its just "int". +extern int nni_plat_ipc_listen(nni_plat_ipcsock *, const char *); + +// nni_plat_ipc_accept does the accept to accept an inbound connection. +// The ipcsock used for the server will have been set up with the +// nni_plat_ipc_listen. +extern int nni_plat_ipc_accept(nni_plat_ipcsock *, nni_plat_ipcsock *); + +// nni_plat_ipc_connect is the client side. +extern int nni_plat_ipc_connect(nni_plat_ipcsock *, const char *); + +// nni_plat_ipc_send sends data to the peer. The platform is responsible +// for attempting to send all of the data. The iov count will never be +// larger than 4. The platform may modify the iovs. +extern int nni_plat_ipc_send(nni_plat_ipcsock *, nni_iov *, int); + +// nni_plat_ipc_recv recvs data into the buffers provided by the +// iovs. The implementation does not return until the iovs are completely +// full, or an error condition occurs. +extern int nni_plat_ipc_recv(nni_plat_ipcsock *, nni_iov *, int); + // nni_plat_seed_prng seeds the PRNG subsystem. The specified number // of bytes of entropy should be stashed. When possible, cryptographic // quality entropy sources should be used. Note that today we prefer diff --git a/src/core/transport.c b/src/core/transport.c index cf9f38c3..25a881d6 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -15,10 +15,12 @@ // to the system dynamically is something that might be considered later. extern nni_tran nni_inproc_tran; extern nni_tran nni_tcp_tran; +extern nni_tran nni_ipc_tran; static nni_tran *transports[] = { &nni_inproc_tran, &nni_tcp_tran, + &nni_ipc_tran, NULL }; diff --git a/src/platform/posix/posix_impl.h b/src/platform/posix/posix_impl.h index 9825b151..c155e15c 100644 --- a/src/platform/posix/posix_impl.h +++ b/src/platform/posix/posix_impl.h @@ -20,6 +20,7 @@ #define PLATFORM_POSIX_ALLOC #define PLATFORM_POSIX_DEBUG #define PLATFORM_POSIX_CLOCK +#define PLATFORM_POSIX_IPC #define PLATFORM_POSIX_NET #define PLATFORM_POSIX_RANDOM #define PLATFORM_POSIX_THREAD @@ -40,6 +41,14 @@ struct nni_plat_tcpsock { }; #endif +#ifdef PLATFORM_POSIX_IPC +struct nni_plat_ipcsock { + int fd; + int devnull; // used for shutting down blocking accept() + char * unlink; // path to unlink at termination +}; +#endif + // Define types that this platform uses. #ifdef PLATFORM_POSIX_THREAD diff --git a/src/platform/posix/posix_ipc.c b/src/platform/posix/posix_ipc.c new file mode 100644 index 00000000..8c40397a --- /dev/null +++ b/src/platform/posix/posix_ipc.c @@ -0,0 +1,342 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "core/nng_impl.h" + +#ifdef PLATFORM_POSIX_IPC + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <fcntl.h> +#include <unistd.h> +#include <netdb.h> + + +#ifdef SOCK_CLOEXEC +#define NNI_IPC_SOCKTYPE (SOCK_STREAM | SOCK_CLOEXEC) +#else +#define NNI_IPC_SOCKTYPE SOCK_STREAM +#endif + +static int +nni_plat_ipc_path_to_sockaddr(struct sockaddr_un *sun, const char *path) +{ + memset(sun, 0, sizeof (*sun)); + sun->sun_family = PF_UNIX; + + // Technically on some platforms we could support path names larger + // than the path, and on others we could skip null termination. We + // take a conservative approach, which is that the path must fit in + // the supplied character array, and *must* be NULL terminated. + + // TODO: abstract sockets, including autobind sockets. + if (strlen(path) >= sizeof (sun->sun_path)) { + return (NNG_EADDRINVAL); + } + if (strlen(path) == 0) { + return (-1); + } + snprintf(sun->sun_path, sizeof (sun->sun_path), "%s", path); + return (sizeof (*sun)); +} + + +int +nni_plat_ipc_send(nni_plat_ipcsock *s, nni_iov *iovs, int cnt) +{ + struct iovec iov[4]; // We never have more than 3 at present + int i; + int offset; + int resid = 0; + int rv; + + if (cnt > 4) { + return (NNG_EINVAL); + } + + for (i = 0; i < cnt; i++) { + iov[i].iov_base = iovs[i].iov_buf; + iov[i].iov_len = iovs[i].iov_len; + resid += iov[i].iov_len; + } + + i = 0; + while (resid) { + rv = writev(s->fd, iov, cnt); + if (rv < 0) { + if (rv == EINTR) { + continue; + } + return (nni_plat_errno(errno)); + } + if (rv > resid) { + nni_panic("writev says it wrote too much!"); + } + resid -= rv; + while (rv) { + if (iov[i].iov_len <= rv) { + rv -= iov[i].iov_len; + i++; + cnt--; + } else { + iov[i].iov_len -= rv; + iov[i].iov_base += rv; + rv = 0; + } + } + } + + return (0); +} + + +int +nni_plat_ipc_recv(nni_plat_ipcsock *s, nni_iov *iovs, int cnt) +{ + struct iovec iov[4]; // We never have more than 3 at present + int i; + int offset; + int resid = 0; + int rv; + + if (cnt > 4) { + return (NNG_EINVAL); + } + + for (i = 0; i < cnt; i++) { + iov[i].iov_base = iovs[i].iov_buf; + iov[i].iov_len = iovs[i].iov_len; + resid += iov[i].iov_len; + } + i = 0; + while (resid) { + rv = readv(s->fd, iov, cnt); + if (rv < 0) { + if (errno == EINTR) { + continue; + } + return (nni_plat_errno(errno)); + } + if (rv == 0) { + return (NNG_ECLOSED); + } + if (rv > resid) { + nni_panic("readv says it read too much!"); + } + + resid -= rv; + while (rv) { + if (iov[i].iov_len <= rv) { + rv -= iov[i].iov_len; + i++; + cnt--; + } else { + iov[i].iov_len -= rv; + iov[i].iov_base += rv; + rv = 0; + } + } + } + + return (0); +} + + +static void +nni_plat_ipc_setopts(int fd) +{ + int one; + + // Try to ensure that both CLOEXEC is set, and that we don't + // generate SIGPIPE. (Note that SIGPIPE suppression in this way + // only works on BSD systems. Linux wants us to use sendmsg().) + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +#if defined(F_SETNOSIGPIPE) + (void) fcntl(fd, F_SETNOSIGPIPE, 1); +#elif defined(SO_NOSIGPIPE) + one = 1; + (void) setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof (one)); +#endif +} + + +void +nni_plat_ipc_init(nni_plat_ipcsock *s) +{ + s->fd = -1; +} + + +void +nni_plat_ipc_fini(nni_plat_ipcsock *s) +{ + if (s->fd != -1) { + (void) close(s->fd); + s->fd = -1; + } + if (s->unlink != NULL) { + (void) unlink(s->unlink); + nni_free(s->unlink, strlen(s->unlink) + 1); + } +} + + +void +nni_plat_ipc_shutdown(nni_plat_ipcsock *s) +{ + if (s->fd != -1) { + (void) shutdown(s->fd, SHUT_RDWR); + // This causes the equivalent of a close. Hopefully waking + // up anything that didn't get the hint with the shutdown. + // (macOS does not see the shtudown). + (void) dup2(nni_plat_devnull, s->fd); + } +} + + +// nni_plat_ipc_listen creates a file descriptor bound to the given address. +// This basically does the equivalent of socket, bind, and listen. We have +// chosen a default value for the listen backlog of 128, which should be +// plenty. (If it isn't, then the accept thread can't get enough resources +// to keep up, and your clients are going to experience bad things. Normally +// the actual backlog should hover near 0 anyway.) +int +nni_plat_ipc_listen(nni_plat_ipcsock *s, const char *path) +{ + int fd, checkfd; + struct sockaddr_un sun; + int rv; + + if (nni_plat_ipc_path_to_sockaddr(&sun, path) < 0) { + return (NNG_EADDRINVAL); + } + + if ((fd = socket(AF_UNIX, NNI_IPC_SOCKTYPE, 0)) < 0) { + return (nni_plat_errno(errno)); + } + + // We are going to check to see if there was a name already there. + // If there was, and nothing is listening (ECONNREFUSED), then we + // will just try to cleanup the old socket. Note that this is not + // perfect in all scenarios, so use this with caution. + if ((checkfd = socket(AF_UNIX, NNI_IPC_SOCKTYPE, 0)) < 0) { + (void) close(fd); + return (nni_plat_errno(errno)); + } + + // Nonblocking because we don't want to wait for any remote server. + (void) fcntl(checkfd, F_SETFL, O_NONBLOCK); + if (connect(checkfd, (struct sockaddr *) &sun, sizeof (sun)) < 0) { + if (errno == ECONNREFUSED) { + (void) unlink(path); + } + } + (void) close(checkfd); + + nni_plat_ipc_setopts(fd); + + if ((s->unlink = nni_alloc(strlen(path) + 1)) == NULL) { + return (NNG_ENOMEM); + } + strcpy(s->unlink, path); + if (bind(fd, (struct sockaddr *) &sun, sizeof (sun)) < 0) { + rv = nni_plat_errno(errno); + nni_free(s->unlink, strlen(path) + 1); + s->unlink = NULL; + (void) close(fd); + return (rv); + } + + // Listen -- 128 depth is probably sufficient. If it isn't, other + // bad things are going to happen. + if (listen(fd, 128) < 0) { + rv = nni_plat_errno(errno); + (void) close(fd); + return (rv); + } + s->fd = fd; + return (0); +} + + +int +nni_plat_ipc_connect(nni_plat_ipcsock *s, const char *path) +{ + int fd; + int len; + struct sockaddr_un sun; + int rv; + + if (nni_plat_ipc_path_to_sockaddr(&sun, path) < 0) { + return (NNG_EADDRINVAL); + } + + if ((fd = socket(AF_UNIX, NNI_IPC_SOCKTYPE, 0)) < 0) { + return (nni_plat_errno(errno)); + } + + nni_plat_ipc_setopts(fd); + + if (connect(fd, (struct sockaddr *) &sun, sizeof (sun)) != 0) { + rv = nni_plat_errno(errno); + (void) close(fd); + if (rv == NNG_ENOENT) { + // In this case we want to treat this the same as + // ECONNREFUSED, since they mean the same to us. + rv = NNG_ECONNREFUSED; + } + return (rv); + } + s->fd = fd; + return (0); +} + + +int +nni_plat_ipc_accept(nni_plat_ipcsock *s, nni_plat_ipcsock *server) +{ + int fd; + + for (;;) { +#ifdef NNG_USE_ACCEPT4 + fd = accept4(server->fd, NULL, NULL, SOCK_CLOEXEC); + if ((fd < 0) && ((errno == ENOSYS) || (errno == ENOTSUP))) { + fd = accept(server->fd, NULL, NULL); + } +#else + fd = accept(server->fd, NULL, NULL); +#endif + + if (fd < 0) { + if ((errno == EINTR) || (errno == ECONNABORTED)) { + // These are not fatal errors, keep trying + continue; + } + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + continue; + } + return (nni_plat_errno(errno)); + } else { + break; + } + } + + nni_plat_ipc_setopts(fd); + + s->fd = fd; + return (0); +} + + +#endif // PLATFORM_POSIX_IPC diff --git a/src/transport/ipc/ipc.c b/src/transport/ipc/ipc.c new file mode 100644 index 00000000..8a28dfe5 --- /dev/null +++ b/src/transport/ipc/ipc.c @@ -0,0 +1,375 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "core/nng_impl.h" + +// IPC transport. Platform specific IPC operations must be +// supplied as well. Normally the IPC is UNIX domain sockets or +// Windows named pipes. Other platforms could use other mechanisms. + +typedef struct nni_ipc_pipe nni_ipc_pipe; +typedef struct nni_ipc_ep nni_ipc_ep; + +// nni_ipc_pipe is one end of an IPC connection. +struct nni_ipc_pipe { + const char * addr; + nni_plat_ipcsock fd; + uint16_t peer; + uint16_t proto; + uint32_t rcvmax; +}; + +struct nni_ipc_ep { + char addr[NNG_MAXADDRLEN+1]; + nni_plat_ipcsock fd; + int closed; + uint16_t proto; + uint32_t rcvmax; +}; + +static int +nni_ipc_tran_init(void) +{ + return (0); +} + + +static void +nni_ipc_tran_fini(void) +{ +} + + +static void +nni_ipc_pipe_close(void *arg) +{ + nni_ipc_pipe *pipe = arg; + + nni_plat_ipc_shutdown(&pipe->fd); +} + + +static void +nni_ipc_pipe_destroy(void *arg) +{ + nni_ipc_pipe *pipe = arg; + + nni_plat_ipc_fini(&pipe->fd); + NNI_FREE_STRUCT(pipe); +} + + +static int +nni_ipc_pipe_send(void *arg, nni_msg *msg) +{ + nni_ipc_pipe *pipe = arg; + uint64_t len; + uint8_t buf[sizeof (len)]; + nni_iov iov[4]; + int rv; + uint8_t msgtype; + + msgtype = 1; // "inband", the only defined option at present + + iov[0].iov_buf = &msgtype; + iov[0].iov_len = 1; + iov[1].iov_buf = buf; + iov[1].iov_len = sizeof (buf); + iov[2].iov_buf = nni_msg_header(msg); + iov[2].iov_len = nni_msg_header_len(msg); + iov[3].iov_buf = nni_msg_body(msg); + iov[3].iov_len = nni_msg_len(msg); + + len = (uint64_t) iov[2].iov_len + (uint64_t) iov[3].iov_len; + NNI_PUT64(buf, len); + + if ((rv = nni_plat_ipc_send(&pipe->fd, iov, 4)) == 0) { + nni_msg_free(msg); + } + return (rv); +} + + +static int +nni_ipc_pipe_recv(void *arg, nni_msg **msgp) +{ + nni_ipc_pipe *pipe = arg; + nni_msg *msg; + uint64_t len; + uint8_t buf[sizeof (len)]; + nni_iov iov[2]; + int rv; + uint8_t msgtype; + + iov[0].iov_buf = &msgtype; + iov[0].iov_len = 1; + iov[1].iov_buf = buf; + iov[1].iov_len = sizeof (buf); + if ((rv = nni_plat_ipc_recv(&pipe->fd, iov, 2)) != 0) { + return (rv); + } + if (msgtype != 1) { + return (NNG_EPROTO); + } + NNI_GET64(buf, len); + if (len > pipe->rcvmax) { + return (NNG_EPROTO); + } + + if ((rv = nng_msg_alloc(&msg, len)) != 0) { + return (rv); + } + + iov[0].iov_len = nng_msg_len(msg); + iov[0].iov_buf = nng_msg_body(msg); + + if ((rv = nni_plat_ipc_recv(&pipe->fd, iov, 1)) == 0) { + *msgp = msg; + } else { + nni_msg_free(msg); + } + return (rv); +} + + +static uint16_t +nni_ipc_pipe_peer(void *arg) +{ + nni_ipc_pipe *pipe = arg; + + return (pipe->peer); +} + + +static int +nni_ipc_pipe_getopt(void *arg, int option, void *buf, size_t *szp) +{ +#if 0 + nni_inproc_pipe *pipe = arg; + size_t len; + + switch (option) { + case NNG_OPT_LOCALADDR: + case NNG_OPT_REMOTEADDR: + len = strlen(pipe->addr) + 1; + if (len > *szp) { + (void) memcpy(buf, pipe->addr, *szp); + } else { + (void) memcpy(buf, pipe->addr, len); + } + *szp = len; + return (0); + } +#endif + return (NNG_ENOTSUP); +} + + +static int +nni_ipc_ep_init(void **epp, const char *url, uint16_t proto) +{ + nni_ipc_ep *ep; + int rv; + + if (strlen(url) > NNG_MAXADDRLEN-1) { + return (NNG_EADDRINVAL); + } + if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { + return (NNG_ENOMEM); + } + ep->closed = 0; + ep->proto = proto; + ep->rcvmax = 1024 * 1024; // XXX: fix this + nni_plat_ipc_init(&ep->fd); + + (void) snprintf(ep->addr, sizeof (ep->addr), "%s", url); + + *epp = ep; + return (0); +} + + +static void +nni_ipc_ep_fini(void *arg) +{ + nni_ipc_ep *ep = arg; + + nni_plat_ipc_fini(&ep->fd); + NNI_FREE_STRUCT(ep); +} + + +static void +nni_ipc_ep_close(void *arg) +{ + nni_ipc_ep *ep = arg; + + nni_plat_ipc_shutdown(&ep->fd); +} + + +static int +nni_ipc_negotiate(nni_ipc_pipe *pipe) +{ + int rv; + nni_iov iov; + uint8_t buf[8]; + uint16_t peer; + + // First send our header.. + buf[0] = 0; + buf[1] = 'S'; + buf[2] = 'P'; + buf[3] = 0; // version + NNI_PUT16(&buf[4], pipe->proto); + NNI_PUT16(&buf[6], 0); + + iov.iov_buf = buf; + iov.iov_len = 8; + if ((rv = nni_plat_ipc_send(&pipe->fd, &iov, 1)) != 0) { + return (rv); + } + + iov.iov_buf = buf; + iov.iov_len = 8; + if ((rv = nni_plat_ipc_recv(&pipe->fd, &iov, 1)) != 0) { + return (rv); + } + + if ((buf[0] != 0) || (buf[1] != 'S') || + (buf[2] != 'P') || (buf[3] != 0) || + (buf[6] != 0) || (buf[7] != 0)) { + return (NNG_EPROTO); + } + + NNI_GET16(&buf[4], pipe->peer); + return (0); +} + + +static int +nni_ipc_ep_connect(void *arg, void **pipep) +{ + nni_ipc_ep *ep = arg; + nni_ipc_pipe *pipe; + int rv; + const char *path; + + if (strncmp(ep->addr, "ipc://", strlen("ipc://")) != 0) { + return (NNG_EADDRINVAL); + } + path = ep->addr + strlen("ipc://"); + + if ((pipe = NNI_ALLOC_STRUCT(pipe)) == NULL) { + return (NNG_ENOMEM); + } + nni_plat_ipc_init(&pipe->fd); + pipe->proto = ep->proto; + pipe->rcvmax = ep->rcvmax; + + rv = nni_plat_ipc_connect(&pipe->fd, path); + if (rv != 0) { + NNI_FREE_STRUCT(pipe); + return (rv); + } + + if ((rv = nni_ipc_negotiate(pipe)) != 0) { + nni_plat_ipc_shutdown(&pipe->fd); + nni_plat_ipc_fini(&pipe->fd); + NNI_FREE_STRUCT(pipe); + return (rv); + } + *pipep = pipe; + return (0); +} + + +static int +nni_ipc_ep_bind(void *arg) +{ + nni_ipc_ep *ep = arg; + int rv; + const char *path; + + // We want to strok this, so make a copy. Skip the scheme. + if (strncmp(ep->addr, "ipc://", strlen("ipc://")) != 0) { + return (NNG_EADDRINVAL); + } + path = ep->addr + strlen("ipc://"); + + if ((rv = nni_plat_ipc_listen(&ep->fd, path)) != 0) { + return (rv); + } + return (0); +} + + +static int +nni_ipc_ep_accept(void *arg, void **pipep) +{ + nni_ipc_ep *ep = arg; + nni_ipc_pipe *pipe; + int rv; + + + if ((pipe = NNI_ALLOC_STRUCT(pipe)) == NULL) { + return (NNG_ENOMEM); + } + pipe->proto = ep->proto; + pipe->rcvmax = ep->rcvmax; + nni_plat_ipc_init(&pipe->fd); + + if ((rv = nni_plat_ipc_accept(&pipe->fd, &ep->fd)) != 0) { + NNI_FREE_STRUCT(pipe); + return (rv); + } + if ((rv = nni_ipc_negotiate(pipe)) != 0) { + nni_plat_ipc_shutdown(&pipe->fd); + nni_plat_ipc_fini(&pipe->fd); + NNI_FREE_STRUCT(pipe); + return (rv); + } + *pipep = pipe; + return (0); +} + + +static nni_tran_pipe nni_ipc_pipe_ops = { + .pipe_destroy = nni_ipc_pipe_destroy, + .pipe_send = nni_ipc_pipe_send, + .pipe_recv = nni_ipc_pipe_recv, + .pipe_close = nni_ipc_pipe_close, + .pipe_peer = nni_ipc_pipe_peer, + .pipe_getopt = nni_ipc_pipe_getopt, +}; + +static nni_tran_ep nni_ipc_ep_ops = { + .ep_init = nni_ipc_ep_init, + .ep_fini = nni_ipc_ep_fini, + .ep_connect = nni_ipc_ep_connect, + .ep_bind = nni_ipc_ep_bind, + .ep_accept = nni_ipc_ep_accept, + .ep_close = nni_ipc_ep_close, + .ep_setopt = NULL, + .ep_getopt = NULL, +}; + +// This is the IPC transport linkage, and should be the only global +// symbol in this entire file. +struct nni_tran nni_ipc_tran = { + .tran_scheme = "ipc", + .tran_ep = &nni_ipc_ep_ops, + .tran_pipe = &nni_ipc_pipe_ops, + .tran_init = nni_ipc_tran_init, + .tran_fini = nni_ipc_tran_fini, +}; diff --git a/src/transport/tcp/tcp.c b/src/transport/tcp/tcp.c index fe06cca4..9b8cdb1d 100644 --- a/src/transport/tcp/tcp.c +++ b/src/transport/tcp/tcp.c @@ -171,7 +171,7 @@ nni_tcp_ep_init(void **epp, const char *url, uint16_t proto) int rv; if (strlen(url) > NNG_MAXADDRLEN-1) { - return (NNG_EINVAL); + return (NNG_EADDRINVAL); } if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { return (NNG_ENOMEM); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 16389601..5f8dc7f9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -55,6 +55,7 @@ endif () add_nng_test(bus 5) add_nng_test(idhash 5) add_nng_test(inproc 5) +add_nng_test(ipc 5) add_nng_test(list 5) add_nng_test(platform 5) add_nng_test(reqrep 5) diff --git a/tests/ipc.c b/tests/ipc.c new file mode 100644 index 00000000..69fe36d0 --- /dev/null +++ b/tests/ipc.c @@ -0,0 +1,18 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "convey.h" +#include "trantest.h" + + +// Inproc tests. + +TestMain("IPC Transport", { + trantest_test_all("ipc:///tmp/nng_ipc_test"); +}) |
