diff options
| author | Garrett D'Amore <garrett@damore.org> | 2018-02-23 14:48:46 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2018-02-23 14:48:46 -0800 |
| commit | c457bddfae64521ea9861f2211e6cb25858559b3 (patch) | |
| tree | 4c0ef1c7450198ce14ef4fbcdddb5d47e911fb4f /src/compat | |
| parent | c31c9147320741904b604e2172f99d5ca08eb417 (diff) | |
| download | nng-c457bddfae64521ea9861f2211e6cb25858559b3.tar.gz nng-c457bddfae64521ea9861f2211e6cb25858559b3.tar.bz2 nng-c457bddfae64521ea9861f2211e6cb25858559b3.zip | |
Move compatibility header so that <nanomsg/nn.h> works.
Basically, we have moved the compat stuff into a separate directory.
Compatibility layer users will have to update their compile flags, but
should be able to avoid changing any *source* files with this change.
Diffstat (limited to 'src/compat')
| -rw-r--r-- | src/compat/nanomsg/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | src/compat/nanomsg/nn.c | 728 | ||||
| -rw-r--r-- | src/compat/nanomsg/nn.h | 315 |
3 files changed, 1058 insertions, 0 deletions
diff --git a/src/compat/nanomsg/CMakeLists.txt b/src/compat/nanomsg/CMakeLists.txt new file mode 100644 index 00000000..d1a692b7 --- /dev/null +++ b/src/compat/nanomsg/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# Copyright 2018 Capitar IT Group BV <info@capitar.com> +# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +# +# 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. +# + +set(COMPAT_SOURCES compat/nanomsg/nn.c) +set(COMPAT_HEADERS compat/nanomsg/nn.h) + +set(NNG_SOURCES ${NNG_SOURCES} ${COMPAT_SOURCES} PARENT_SCOPE) +set(NNG_HEADERS ${NNG_HEADERS} ${COMPAT_HEADERS} PARENT_SCOPE) diff --git a/src/compat/nanomsg/nn.c b/src/compat/nanomsg/nn.c new file mode 100644 index 00000000..1d5bcfbb --- /dev/null +++ b/src/compat/nanomsg/nn.c @@ -0,0 +1,728 @@ +// +// Copyright 2018 Garrett D'Amore <garrett@damore.org> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// 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 "nn.h" +#include "nng.h" +#include "protocol/bus0/bus.h" +#include "protocol/pair0/pair.h" +#include "protocol/pipeline0/pull.h" +#include "protocol/pipeline0/push.h" +#include "protocol/pubsub0/pub.h" +#include "protocol/pubsub0/sub.h" +#include "protocol/reqrep0/rep.h" +#include "protocol/reqrep0/req.h" +#include "protocol/survey0/respond.h" +#include "protocol/survey0/survey.h" + +#include <stdio.h> +#include <string.h> + +// This file supplies the legacy compatibility API. Applications should +// avoid using these if at all possible, and instead use the new style APIs. + +static const struct { + int nerr; + int perr; +} nn_errnos[] = { + // clang-format off + { NNG_EINTR, EINTR }, + { NNG_ENOMEM, ENOMEM }, + { NNG_EINVAL, EINVAL }, + { NNG_EBUSY, EBUSY }, + { NNG_ETIMEDOUT, ETIMEDOUT }, + { NNG_ECONNREFUSED, ECONNREFUSED }, + { NNG_ECLOSED, EBADF }, + { NNG_EAGAIN, EAGAIN }, + { NNG_ENOTSUP, ENOTSUP }, + { NNG_EADDRINUSE, EADDRINUSE }, + { NNG_ESTATE, EFSM }, + { NNG_ENOENT, ENOENT }, + { NNG_EPROTO, EPROTO }, + { NNG_EUNREACHABLE, EHOSTUNREACH }, + { NNG_EADDRINVAL, EADDRNOTAVAIL }, + { NNG_EPERM, EACCES }, + { NNG_EMSGSIZE, EMSGSIZE }, + { NNG_ECONNABORTED, ECONNABORTED }, + { NNG_ECONNRESET, ECONNRESET }, + { NNG_ECANCELED, EBADF }, + { 0, 0 }, + // clang-format on +}; + +const char * +nn_strerror(int err) +{ + int i; + static char msgbuf[32]; + + for (i = 0; nn_errnos[i].perr != 0; i++) { + if (nn_errnos[i].perr == err) { + return (nng_strerror(nn_errnos[i].nerr)); + } + } + if (err == EIO) { + return ("Unknown I/O error"); + } + + // Arguably we could use strerror() here, but we should only + // be getting errnos we understand at this point. + (void) snprintf(msgbuf, sizeof(msgbuf), "Unknown error %d", err); + return (msgbuf); +} + +static void +nn_seterror(int err) +{ + int i; + + for (i = 0; nn_errnos[i].nerr != 0; i++) { + if (nn_errnos[i].nerr == err) { + errno = nn_errnos[i].perr; + return; + } + } + // No idea... + errno = EIO; +} + +int +nn_errno(void) +{ + return (errno); +} + +static const struct { + uint16_t p_id; + int (*p_open)(nng_socket *); +} nn_protocols[] = { +// clang-format off +#ifdef NNG_HAVE_BUS0 + { NN_BUS, nng_bus0_open }, +#endif +#ifdef NNG_HAVE_PAIR0 + { NN_PAIR, nng_pair0_open }, +#endif +#ifdef NNG_HAVE_PUSH0 + { NN_PUSH, nng_push0_open }, +#endif +#ifdef NNG_HAVE_PULL0 + { NN_PULL, nng_pull0_open }, +#endif +#ifdef NNG_HAVE_PUB0 + { NN_PUB, nng_pub0_open }, +#endif +#ifdef NNG_HAVE_SUB0 + { NN_SUB, nng_sub0_open }, +#endif +#ifdef NNG_HAVE_REQ0 + { NN_REQ, nng_req0_open }, +#endif +#ifdef NNG_HAVE_REP0 + { NN_REP, nng_rep0_open }, +#endif +#ifdef NNG_HAVE_SURVEYOR0 + { NN_SURVEYOR, nng_surveyor0_open }, +#endif +#ifdef NNG_HAVE_RESPONDENT0 + { NN_RESPONDENT, nng_respondent0_open }, +#endif + { 0, NULL }, + // clang-format on +}; + +int +nn_socket(int domain, int protocol) +{ + nng_socket sock; + int rv; + int i; + + if ((domain != AF_SP) && (domain != AF_SP_RAW)) { + errno = EAFNOSUPPORT; + return (-1); + } + + for (i = 0; nn_protocols[i].p_id != 0; i++) { + if (nn_protocols[i].p_id == protocol) { + break; + } + } + if (nn_protocols[i].p_open == NULL) { + errno = ENOTSUP; + return (-1); + } + + if ((rv = nn_protocols[i].p_open(&sock)) != 0) { + nn_seterror(rv); + return (-1); + } + if (domain == AF_SP_RAW) { + if ((rv = nng_setopt_int(sock, NNG_OPT_RAW, 1)) != 0) { + nn_seterror(rv); + nng_close(sock); + return (-1); + } + } + return ((int) sock); +} + +int +nn_close(int s) +{ + int rv; + + if ((rv = nng_close((nng_socket) s)) != 0) { + nn_seterror(rv); + return (-1); + } + return (0); +} + +int +nn_bind(int s, const char *addr) +{ + int rv; + nng_listener l; + + if ((rv = nng_listen((nng_socket) s, addr, &l, 0)) != 0) { + nn_seterror(rv); + return (-1); + } + return ((int) l); +} + +int +nn_connect(int s, const char *addr) +{ + int rv; + nng_dialer d; + + if ((rv = nng_dial((nng_socket) s, addr, &d, NNG_FLAG_NONBLOCK)) != + 0) { + nn_seterror(rv); + return (-1); + } + return ((int) d); +} + +int +nn_shutdown(int s, int ep) +{ + int rv; + (void) s; // Unused + + // Socket is wired into the endpoint... so passing a bad endpoint + // ID can result in affecting the wrong socket. But this requires + // a buggy application, and because we don't recycle endpoints + // until wrap, its unlikely to actually come up in practice. + // Note that listeners and dialers share the same namespace + // in the core, so we can close either one this way. + + if (((rv = nng_dialer_close((nng_dialer) ep)) != 0) && + ((rv = nng_listener_close((nng_listener) ep)) != 0)) { + nn_seterror(rv); + return (-1); + } + return (0); +} + +void * +nn_allocmsg(size_t size, int type) +{ + nng_msg *msg; + int rv; + + // Validate type and non-zero size. This also checks for overflow. + if ((type != 0) || (size < 1) || ((size + sizeof(msg) < size))) { + nn_seterror(NNG_EINVAL); + return (NULL); + } + + // So our "messages" from nn are really going to be nng messages + // but to make this work, we use a bit of headroom in the message + // to stash the message header. + if ((rv = nng_msg_alloc(&msg, size + (sizeof(msg)))) != 0) { + nn_seterror(rv); + return (NULL); + } + + // This counts on message bodies being aligned sensibly. + *(nng_msg **) (nng_msg_body(msg)) = msg; + + // We are counting on the implementation of nn_msg_trim to not + // reallocate the message but just to leave the prefix inplace. + (void) nng_msg_trim(msg, sizeof(msg)); + + return (nng_msg_body(msg)); +} + +int +nn_freemsg(void *ptr) +{ + nng_msg *msg; + + msg = *(nng_msg **) (((char *) ptr) - sizeof(msg)); + nng_msg_free(msg); + return (0); +} + +void * +nn_reallocmsg(void *ptr, size_t len) +{ + nng_msg *msg; + int rv; + + if ((len + sizeof(msg)) < len) { + // overflowed! + nn_seterror(NNG_EINVAL); + return (NULL); + } + + // This counts on message bodies being aligned sensibly. + msg = *(nng_msg **) (((char *) ptr) - sizeof(msg)); + + // We need to realloc the requested len, plus size for our header. + if ((rv = nng_msg_realloc(msg, len + sizeof(msg))) != 0) { + // We don't free the old message. Code is free to cope + // as it sees fit. + nn_seterror(rv); + return (NULL); + } + // Stash the msg header pointer + *(nng_msg **) (nng_msg_body(msg)) = msg; + nng_msg_trim(msg, sizeof(msg)); + return (nng_msg_body(msg)); +} + +static int +nn_flags(int flags) +{ + switch (flags) { + case 0: + return (0); + + case NN_DONTWAIT: + return (NNG_FLAG_NONBLOCK); + + default: + nn_seterror(NNG_EINVAL); + return (-1); + } +} + +int +nn_send(int s, const void *buf, size_t len, int flags) +{ + struct nn_iovec iov; + struct nn_msghdr hdr; + + iov.iov_base = (void *) buf; + iov.iov_len = len; + + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + + return (nn_sendmsg(s, &hdr, flags)); +} + +int +nn_recv(int s, void *buf, size_t len, int flags) +{ + struct nn_iovec iov; + struct nn_msghdr hdr; + + iov.iov_base = buf; + iov.iov_len = len; + + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = NULL; + hdr.msg_controllen = 0; + + return (nn_recvmsg(s, &hdr, flags)); +} + +int +nn_recvmsg(int s, struct nn_msghdr *mh, int flags) +{ + int rv; + nng_msg *msg; + size_t len; + int keep = 0; + + if ((flags = nn_flags(flags)) == -1) { + return (-1); + } + if (mh == NULL) { + nn_seterror(NNG_EINVAL); + return (-1); + } + if (mh->msg_iovlen < 0) { + nn_seterror(NNG_EMSGSIZE); + return (-1); + } + + if ((rv = nng_recvmsg((nng_socket) s, &msg, flags)) != 0) { + nn_seterror(rv); + return (-1); + } + if ((mh->msg_iovlen == 1) && (mh->msg_iov[0].iov_len == NN_MSG)) { + // Receiver wants to have a dynamically allocated message. + // There can only be one of these. + if ((rv = nng_msg_insert(msg, &msg, sizeof(msg))) != 0) { + nng_msg_free(msg); + nn_seterror(rv); + return (-1); + } + nng_msg_trim(msg, sizeof(msg)); + *(void **) (mh->msg_iov[0].iov_base) = nng_msg_body(msg); + len = nng_msg_len(msg); + keep = 1; // Do not discard message! + } else { + // copyout to multiple iovecs. + char * ptr = nng_msg_body(msg); + int i; + size_t n; + len = nng_msg_len(msg); + + for (i = 0; i < mh->msg_iovlen; i++) { + if ((n = mh->msg_iov[i].iov_len) == NN_MSG) { + // This is forbidden! + nn_seterror(NNG_EINVAL); + nng_msg_free(msg); + return (-1); + } + if (n > len) { + n = len; + } + memcpy(mh->msg_iov[i].iov_base, ptr, n); + len -= n; + ptr += n; + } + + // If we copied everything, len will be zero, otherwise, + // it represents the amount of data that we were unable to + // copyout. The caller is responsible for noticing this, + // as there is no API to pass this information out. + len = nng_msg_len(msg); + } + + // If the caller has requested control information (header details), + // we grab it. + if (mh->msg_control != NULL) { + char * cdata; + size_t clen; + size_t tlen; + size_t spsz; + struct nn_cmsghdr *hdr; + unsigned char * ptr; + + spsz = nng_msg_header_len(msg); + clen = NN_CMSG_SPACE(sizeof(spsz) + spsz); + + if ((tlen = mh->msg_controllen) == NN_MSG) { + // Ideally we'd use the same msg, but we would need + // to set up reference counts on the message, so + // instead we just make a new message. + nng_msg *nmsg; + + rv = nng_msg_alloc(&nmsg, clen + sizeof(nmsg)); + if (rv != 0) { + nng_msg_free(msg); + nn_seterror(rv); + return (-1); + } + memcpy(nng_msg_body(nmsg), &nmsg, sizeof(nmsg)); + nng_msg_trim(nmsg, sizeof(nmsg)); + cdata = nng_msg_body(nmsg); + *(void **) mh->msg_control = cdata; + tlen = clen; + } else { + cdata = mh->msg_control; + memset(cdata, 0, + tlen > sizeof(*hdr) ? sizeof(*hdr) : tlen); + } + + if (clen <= tlen) { + ptr = NN_CMSG_DATA(cdata); + hdr = (void *) cdata; + hdr->cmsg_len = clen; + hdr->cmsg_level = PROTO_SP; + hdr->cmsg_type = SP_HDR; + + memcpy(ptr, &spsz, sizeof(spsz)); + ptr += sizeof(spsz); + memcpy(ptr, nng_msg_header(msg), spsz); + } + } + + if (!keep) { + nng_msg_free(msg); + } + return ((int) len); +} + +int +nn_sendmsg(int s, const struct nn_msghdr *mh, int flags) +{ + nng_msg *msg = NULL; + nng_msg *cmsg = NULL; + char * cdata; + int keep = 0; + size_t sz; + int rv; + + if ((flags = nn_flags(flags)) == -1) { + return (-1); + } + + if (mh == NULL) { + nn_seterror(NNG_EINVAL); + return (-1); + } + + if (mh->msg_iovlen < 0) { + nn_seterror(NNG_EMSGSIZE); + return (-1); + } + + if ((mh->msg_iovlen == 1) && (mh->msg_iov[0].iov_len == NN_MSG)) { + char *bufp = *(char **) (mh->msg_iov[0].iov_base); + + msg = *(nng_msg **) (bufp - sizeof(msg)); + keep = 1; // keep the message on error + } else { + char *ptr; + int i; + + sz = 0; + // Get the total message size. + for (i = 0; i < mh->msg_iovlen; i++) { + sz += mh->msg_iov[i].iov_len; + } + if ((rv = nng_msg_alloc(&msg, sz)) != 0) { + nn_seterror(rv); + return (-1); + } + // Now copy it out. + ptr = nng_msg_body(msg); + for (i = 0; i < mh->msg_iovlen; i++) { + memcpy(ptr, mh->msg_iov[i].iov_base, + mh->msg_iov[i].iov_len); + ptr += mh->msg_iov[i].iov_len; + } + } + + // Now suck up the control data... + // This POSIX-inspired API is one of the most painful for + // usability we've ever seen. + cmsg = NULL; + if ((cdata = mh->msg_control) != NULL) { + size_t clen; + size_t offs; + size_t spsz; + struct nn_cmsghdr *chdr; + unsigned char * data; + + if ((clen = mh->msg_controllen) == NN_MSG) { + // Underlying data is a message. This is awkward, + // because we have to copy the data, but we should + // only free this message on success. So we save the + // message now. + cdata = *(void **) cdata; + cmsg = *(nng_msg **) (cdata - sizeof(cmsg)); + clen = nng_msg_len(cmsg); + } else { + clen = mh->msg_controllen; + } + + offs = 0; + while ((offs + sizeof(NN_CMSG_LEN(0))) < clen) { + chdr = (void *) (cdata + offs); + if ((chdr->cmsg_level != PROTO_SP) || + (chdr->cmsg_type != SP_HDR)) { + offs += chdr->cmsg_len; + } + + // SP header in theory. Starts with size, then + // any backtrace details. + if (chdr->cmsg_len < sizeof(size_t)) { + offs += chdr->cmsg_len; + continue; + } + data = NN_CMSG_DATA(chdr); + memcpy(&spsz, data, sizeof(spsz)); + if ((spsz + sizeof(spsz)) > chdr->cmsg_len) { + // Truncated header? Ignore it. + offs += chdr->cmsg_len; + continue; + } + data += sizeof(spsz); + rv = nng_msg_header_append(msg, data, spsz); + if (rv != 0) { + if (!keep) { + nng_msg_free(msg); + } + nn_seterror(rv); + return (-1); + } + + break; + } + } + + sz = nng_msg_len(msg); + if ((rv = nng_sendmsg((nng_socket) s, msg, flags)) != 0) { + if (!keep) { + nng_msg_free(msg); + } + nn_seterror(rv); + return (-1); + } + + if (cmsg != NULL) { + // We sent successfully, so free up the control message. + nng_msg_free(cmsg); + } + return ((int) sz); +} + +// options which we convert -- most of the array is initialized at run time. +static const struct { + int nnlevel; + int nnopt; + const char *opt; +} options[] = { + { NN_SOL_SOCKET, NN_LINGER, NNG_OPT_LINGER }, // review + { NN_SOL_SOCKET, NN_SNDBUF, NNG_OPT_SENDBUF }, + { NN_SOL_SOCKET, NN_RCVBUF, NNG_OPT_RECVBUF }, + { NN_SOL_SOCKET, NN_RECONNECT_IVL, NNG_OPT_RECONNMINT }, + { NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, NNG_OPT_RECONNMAXT }, + { NN_SOL_SOCKET, NN_SNDFD, NNG_OPT_SENDFD }, + { NN_SOL_SOCKET, NN_RCVFD, NNG_OPT_RECVFD }, + { NN_SOL_SOCKET, NN_RCVMAXSIZE, NNG_OPT_RECVMAXSZ }, + { NN_SOL_SOCKET, NN_MAXTTL, NNG_OPT_MAXTTL }, + { NN_SOL_SOCKET, NN_RCVTIMEO, NNG_OPT_RECVTIMEO }, + { NN_SOL_SOCKET, NN_SNDTIMEO, NNG_OPT_SENDTIMEO }, + { NN_SOL_SOCKET, NN_DOMAIN, NNG_OPT_DOMAIN }, + { NN_SOL_SOCKET, NN_SOCKET_NAME, NNG_OPT_SOCKNAME }, + { NN_REQ, NN_REQ_RESEND_IVL, NNG_OPT_REQ_RESENDTIME }, + { NN_SUB, NN_SUB_SUBSCRIBE, NNG_OPT_SUB_SUBSCRIBE }, + { NN_SUB, NN_SUB_UNSUBSCRIBE, NNG_OPT_SUB_UNSUBSCRIBE }, + { NN_SURVEYOR, NN_SURVEYOR_DEADLINE, NNG_OPT_SURVEYOR_SURVEYTIME }, + // XXX: IPV4ONLY, SNDPRIO, RCVPRIO +}; + +int +nn_getsockopt(int s, int nnlevel, int nnopt, void *valp, size_t *szp) +{ + const char *name = NULL; + int rv; + + for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + if ((options[i].nnlevel == nnlevel) && + (options[i].nnopt == nnopt)) { + name = options[i].opt; + break; + } + } + + if (name == NULL) { + errno = ENOPROTOOPT; + return (-1); + } + + if ((rv = nng_getopt((nng_socket) s, name, valp, szp)) != 0) { + nn_seterror(rv); + return (-1); + } + + return (0); +} + +int +nn_setsockopt(int s, int nnlevel, int nnopt, const void *valp, size_t sz) +{ + const char *name = NULL; + int rv; + + for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) { + if ((options[i].nnlevel == nnlevel) && + (options[i].nnopt == nnopt)) { + name = options[i].opt; + break; + } + } + if (name == NULL) { + return (ENOPROTOOPT); + } + + if ((rv = nng_setopt((nng_socket) s, name, valp, sz)) != 0) { + nn_seterror(rv); + return (-1); + } + return (0); +} + +struct nn_cmsghdr * +nn_cmsg_next(struct nn_msghdr *mh, struct nn_cmsghdr *first) +{ + size_t clen; + char * data; + + // We only support SP headers, so there can be at most one header. + if (first != NULL) { + return (NULL); + } + if ((clen = mh->msg_controllen) == NN_MSG) { + nng_msg *msg; + data = *((void **) (mh->msg_control)); + msg = *(nng_msg **) (data - sizeof(msg)); + clen = nng_msg_len(msg); + } else { + data = mh->msg_control; + } + + if (first == NULL) { + first = (void *) data; + } else { + first = first + first->cmsg_len; + } + + if (((char *) first + sizeof(*first)) > (data + clen)) { + return (NULL); + } + return (first); +} + +int +nn_device(int s1, int s2) +{ + int rv; + + rv = nng_device((nng_socket) s1, (nng_socket) s2); + // rv must always be nonzero + nn_seterror(rv); + return (-1); +} + +// nn_term is suitable only for shutting down the entire library, +// and is not thread-safe with other functions. +void +nn_term(void) +{ + // This function is relatively toxic, since it can affect + // all sockets in the process, including those + // in use by libraries, etc. Accordingly, do not use this + // in a library -- only e.g. atexit() and similar. + nng_closeall(); +} diff --git a/src/compat/nanomsg/nn.h b/src/compat/nanomsg/nn.h new file mode 100644 index 00000000..8c5cee6f --- /dev/null +++ b/src/compat/nanomsg/nn.h @@ -0,0 +1,315 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// +// 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. +// + +#ifndef NNG_COMPAT_H +#define NNG_COMPAT_H + +// This header contains interfaces that are intended to offer compatibility +// with nanomsg v1.0. These are not the "preferred" interfaces for nng, +// and consumers should only use thse if they are porting software that +// previously used nanomsg. New programs should use the nng native APIs. + +// Note that compatibility promises are limited to public portions of the +// nanomsg API, and specifically do NOT extend to the ABI. Furthermore, +// there may be other limitations around less commonly used portions of the +// API; for example only SP headers may be transported in control data for +// messages, there is almost no compatibility offered for statistics. +// Error values may differ from those returned by nanomsg as well; the nng +// error reporting facility expresses only a subset of the possibilities of +// nanomsg. + +// Note that unlinke nanomsg, nng does not aggressively recycle socket or +// endpoint IDs, which means applications which made assumptions that these +// would be relatively small integers (e.g. to use them as array indices) +// may break. (No promise about values was ever made.) + +#ifdef __cplusplus +extern "C" { +#endif + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +// clang-format gets in the way of most of this file. +// We turn it off, at least until it gets smarter about aligning +// macro definitions or we adopt enums or somesuch. +// clang-format off + +// NNG_DECL is used on declarations to deal with scope. +// For building Windows DLLs, it should be the appropriate +// __declspec(). (We recommend *not* building this library +// as a DLL, but instead linking it statically for your projects +// to minimize questions about link dependencies later.) +#ifndef NN_DECL +#if defined(_WIN32) && !defined(NNG_STATIC_LIB) +#if defined(NNG_SHARED_LIB) +#define NN_DECL __declspec(dllexport) +#else +#define NN_DECL __declspec(dllimport) +#endif // NNG_SHARED_LIB +#else +#define NN_DECL extern +#endif // _WIN32 && !NNG_STATIC_LIB +#endif // NN_DECL + +#define AF_SP 1 +#define AF_SP_RAW 2 + +// Protocol stuff +#define NN_PROTO_PAIR 1 +#define NN_PROTO_PUBSUB 2 +#define NN_PROTO_REQREP 3 +#define NN_PROTO_PIPELINE 5 +#define NN_PROTO_SURVEY 6 +#define NN_PROTO_BUS 7 + +#define NN_PAIR (NN_PROTO_PAIR * 16 + 0) +#define NN_PAIR_v0 (NN_PROTO_PAIR * 16 + 0) +#define NN_PAIR_V1 (NN_PROTO_PAIR * 16 + 1) +#define NN_PUB (NN_PROTO_PUBSUB * 16 + 0) +#define NN_SUB (NN_PROTO_PUBSUB * 16 + 1) +#define NN_REQ (NN_PROTO_REQREP * 16 + 0) +#define NN_REP (NN_PROTO_REQREP * 16 + 1) +#define NN_PUSH (NN_PROTO_PIPELINE * 16 + 0) +#define NN_PULL (NN_PROTO_PIPELINE * 16 + 1) +#define NN_SURVEYOR (NN_PROTO_SURVEY * 16 + 2) +#define NN_RESPONDENT (NN_PROTO_SURVEY * 16 + 3) +#define NN_BUS (NN_PROTO_BUS * 16 + 0) + +#define NN_SOCKADDR_MAX 128 +#define NN_SOL_SOCKET 0 + +// Flag for send/recv (nonblocking) +#define NN_DONTWAIT 1 + +// CMSG data type +#define PROTO_SP 1 +#define SP_HDR 1 + +// Errnos. Legacy nanomsg uses posix errnos where possible. +// If a define is not set, use add NN_ERRBASE. nng does not +// return all of these values, so there may be some loss of +// of information for edge cases, but we don't expect that to be +// a problem really. +#define NN_ERRBASE (0x10000000) +#ifndef ENOTSUP +#define ENOTSUP (NN_ERRBASE+1) +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT (NN_ERRBASE+2) +#endif +#ifndef ENOBUFS +#define ENOBUFS (NN_ERRBASE+3) +#endif +#ifndef ENETDOWN +#define ENETDOWN (NN_ERRBASE+4) +#endif +#ifndef EADDRINUSE +#define EADDRINUSE (NN_ERRBASE+5) +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL (NN_ERRBASE+6) +#endif +#ifndef ENOTSOCK +#define ENOTSOCK (NN_ERRBASE+7) +#endif +#ifndef EAGAIN +#define EAGAIN (NN_ERRBASE+8) +#endif +#ifndef EBADF +#define EBADF (NN_ERRBASE+9) +#endif +#ifndef EINVAL +#define EINVAL (NN_ERRBASE+10) +#endif +#ifndef EMFILE +#define EMFILE (NN_ERRBASE+11) +#endif +#ifndef EFAULT +#define EFAULT (NN_ERRBASE+12) +#endif +#ifndef EACCES +#define EACCES (NN_ERRBASE+13) +#endif +#ifndef ENETRESET +#define ENETRESET (NN_ERRBASE+14) +#endif +#ifndef ENETUNREACH +#define ENETUNREACH (NN_ERRBASE+15) +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH (NN_ERRBASE+16) +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (NN_ERRBASE+17) +#endif +#ifndef EINPROGRESS +#define EINPROGRESS (NN_ERRBASE+18) +#endif +#ifndef EPROTO +#define EPROTO (NN_ERRBASE+19) +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED (NN_ERRBASE+20) +#endif +#ifndef ENOTCONN +#define ENOTCONN (NN_ERRBASE+21) +#endif +#ifndef EMSGSIZE +#define EMSGSIZE (NN_ERRBASE+22) +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT (NN_ERRBASE+23) +#endif +#ifndef ECONNABORTED +#define ECONNABORTED (NN_ERRBASE+24) +#endif +#ifndef ECONNRESET +#define ECONNRESET (NN_ERRBASE+25) +#endif +#ifndef ENOPROTOOPT +#define ENOPROTOOPT (NN_ERRBASE+26) +#endif +#ifndef EISCONN +#define EISCONN (NN_ERRBASE+27) +#endif +#ifndef ESOCKNOSUPPORT +#define ESOCKNOSPPORT (NN_ERRBASE+28) +#endif +#ifndef ETERM +#define ETERM (NN_ERRBASE+29) +#endif +#ifndef EFSM +#define EFSM (NN_ERRBASE+30) +#endif +#ifndef ENOENT +#define ENOENT (NN_ERRBASE+31) +#endif +#ifndef EIO +#define EIO (NN_ERRBASE+32) +#endif + +// Socket options +#define NN_LINGER 1 +#define NN_SNDBUF 2 +#define NN_RCVBUF 3 +#define NN_SNDTIMEO 4 +#define NN_RCVTIMEO 5 +#define NN_RECONNECT_IVL 6 +#define NN_RECONNECT_IVL_MAX 7 +#define NN_SNDPRIO 8 +#define NN_RCVPRIO 9 +#define NN_SNDFD 10 +#define NN_RCVFD 11 +#define NN_DOMAIN 12 +#define NN_PROTOCOL 13 +#define NN_IPV4ONLY 14 +#define NN_SOCKET_NAME 15 +#define NN_RCVMAXSIZE 16 +#define NN_MAXTTL 17 + +// Protocol-specific options. To simplify thins we encode the protocol +// level in the option. +#define NN_SUB_SUBSCRIBE (NN_SUB * 16 + 1) +#define NN_SUB_UNSUBSCRIBE (NN_SUB * 16 + 2) +#define NN_REQ_RESEND_IVL (NN_REQ * 16 + 1) +#define NN_SURVEYOR_DEADLINE (NN_SURVEYOR * 16 + 1) + +// Level options for tranports +#define NN_INPROC (-1) +#define NN_IPC (-2) +#define NN_IPC_SEC_ATTR 1 +#define NN_IPC_OUTBUFSZ 2 +#define NN_IPC_INBUFSZ 3 +#define NN_TCP (-3) +#define NN_TCP_NODELAY 1 +#define NN_WS (-4) +#define NN_WS_MSG_TYPE 1 +#define NN_WS_MSG_TYPE_TEXT 1 +#define NN_WS_MSG_TYPE_BINARY 2 + +// from this point on formatting is fine +// clang-format on + +// Poll stuff +#define NN_POLLIN 1 +#define NN_POLLOUT 2 +struct nn_pollfd { + int fd; + uint16_t events; + uint16_t revents; +}; + +// Magical size for allocation +#define NN_MSG ((size_t) -1) + +struct nn_iovec { + void * iov_base; + size_t iov_len; +}; + +struct nn_msghdr { + struct nn_iovec *msg_iov; + int msg_iovlen; + void * msg_control; + size_t msg_controllen; +}; + +struct nn_cmsghdr { + size_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +#define NN_CMSG_ALIGN(len) \ + (((len) + sizeof(size_t) - 1) & (size_t) ~(sizeof(size_t) - 1)) + +// Unlike old nanomsg, we explicitly only support the SP header as attached +// cmsg data. It turns out that old nanomsg didn't really store anything +// useful otherwise anyway. (One specific exception was that it stored the +// message type of text or binary for the websocket transport. We don't think +// anyone used that in practice though.) +#define NN_CMSG_FIRSTHDR(mh) nn_cmsg_next((struct nn_msghdr *) (mh), NULL) +#define NN_CMSG_NXTHDR(mh, ch) \ + nn_cmsg_next((struct nn_msghdr *) (mh), (struct nn_cmsghdr *) ch) +#define NN_CMSG_DATA(ch) ((unsigned char *) (((struct nn_cmsghdr *) (ch)) + 1)) +#define NN_CMSG_SPACE(len) \ + (NN_CMSG_ALIGN(len) + NN_CMSG_ALIGN(sizeof(struct nn_cmsghdr))) +#define NN_CMSG_LEN(len) (NN_CMSG_ALIGN(sizeof(struct nn_cmsghdr)) + (len)) + +NN_DECL struct nn_cmsghdr *nn_cmsg_next( + struct nn_msghdr *, struct nn_cmsghdr *); +NN_DECL int nn_socket(int, int); +NN_DECL int nn_setsockopt(int, int, int, const void *, size_t); +NN_DECL int nn_getsockopt(int, int, int, void *, size_t *); +NN_DECL int nn_bind(int, const char *); +NN_DECL int nn_connect(int, const char *); +NN_DECL int nn_shutdown(int, int); +NN_DECL int nn_send(int, const void *, size_t, int); +NN_DECL int nn_recv(int, void *, size_t, int); +NN_DECL int nn_sendmsg(int, const struct nn_msghdr *, int); +NN_DECL int nn_recvmsg(int, struct nn_msghdr *, int); +NN_DECL int nn_close(int); +NN_DECL int nn_poll(struct nn_pollfd *, int, int); +NN_DECL int nn_device(int, int); +NN_DECL uint64_t nn_get_statistic(int, int); +NN_DECL void * nn_allocmsg(size_t, int); +NN_DECL void * nn_reallocmsg(void *, size_t); +NN_DECL int nn_freemsg(void *); +NN_DECL int nn_errno(void); +NN_DECL const char *nn_strerror(int); +NN_DECL void nn_term(void); + +#ifdef __cplusplus +} +#endif + +#endif // NNG_COMPAT_H |
