diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-08-17 23:57:09 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-08-18 01:57:35 -0700 |
| commit | 446b150032f24c34644f0ab91ac6ab9206250865 (patch) | |
| tree | e47b4e3812016716187201961c43b9e407a3ed50 /src/core | |
| parent | 76c1fc80c931b086493835d037245ebbb5f8d406 (diff) | |
| download | nng-446b150032f24c34644f0ab91ac6ab9206250865.tar.gz nng-446b150032f24c34644f0ab91ac6ab9206250865.tar.bz2 nng-446b150032f24c34644f0ab91ac6ab9206250865.zip | |
Endpoint API completely implemented.
This supports creating listeners and dialers, managing options
on them (though only a few options are supported at present),
starting them and closing them, all independently.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/endpt.c | 33 | ||||
| -rw-r--r-- | src/core/endpt.h | 2 | ||||
| -rw-r--r-- | src/core/options.c | 79 | ||||
| -rw-r--r-- | src/core/options.h | 7 | ||||
| -rw-r--r-- | src/core/pipe.c | 4 | ||||
| -rw-r--r-- | src/core/socket.c | 299 | ||||
| -rw-r--r-- | src/core/socket.h | 3 | ||||
| -rw-r--r-- | src/core/transport.c | 21 | ||||
| -rw-r--r-- | src/core/transport.h | 6 |
9 files changed, 358 insertions, 96 deletions
diff --git a/src/core/endpt.c b/src/core/endpt.c index 34debc0e..f90bd068 100644 --- a/src/core/endpt.c +++ b/src/core/endpt.c @@ -418,6 +418,7 @@ nni_ep_dial(nni_ep *ep, int flags) } if ((flags & NNG_FLAG_NONBLOCK) != 0) { + ep->ep_started = 1; nni_ep_con_start(ep); nni_mtx_unlock(&ep->ep_mtx); return (0); @@ -567,6 +568,38 @@ nni_ep_pipe_remove(nni_ep *ep, nni_pipe *pipe) nni_mtx_unlock(&ep->ep_mtx); } +int +nni_ep_setopt(nni_ep *ep, int opt, const void *val, size_t sz, int check) +{ + int rv; + + if (ep->ep_ops.ep_setopt == NULL) { + return (NNG_ENOTSUP); + } + nni_mtx_lock(&ep->ep_mtx); + if (check && ep->ep_started) { + nni_mtx_unlock(&ep->ep_mtx); + return (NNG_ESTATE); + } + rv = ep->ep_ops.ep_setopt(ep->ep_data, opt, val, sz); + nni_mtx_unlock(&ep->ep_mtx); + return (rv); +} + +int +nni_ep_getopt(nni_ep *ep, int opt, void *valp, size_t *szp) +{ + int rv; + + if (ep->ep_ops.ep_getopt == NULL) { + return (NNG_ENOTSUP); + } + nni_mtx_lock(&ep->ep_mtx); + rv = ep->ep_ops.ep_getopt(ep->ep_data, opt, valp, szp); + nni_mtx_unlock(&ep->ep_mtx); + return (rv); +} + void nni_ep_list_init(nni_list *list) { diff --git a/src/core/endpt.h b/src/core/endpt.h index fbb10911..de058d4b 100644 --- a/src/core/endpt.h +++ b/src/core/endpt.h @@ -27,6 +27,8 @@ extern void nni_ep_close(nni_ep *); extern int nni_ep_dial(nni_ep *, int); extern int nni_ep_listen(nni_ep *, int); extern void nni_ep_list_init(nni_list *); +extern int nni_ep_setopt(nni_ep *, int, const void *, size_t, int); +extern int nni_ep_getopt(nni_ep *, int, void *, size_t *); extern int nni_ep_pipe_add(nni_ep *ep, nni_pipe *); extern void nni_ep_pipe_remove(nni_ep *, nni_pipe *); diff --git a/src/core/options.c b/src/core/options.c index b243b262..ecfa437e 100644 --- a/src/core/options.c +++ b/src/core/options.c @@ -13,57 +13,98 @@ #include <string.h> int -nni_setopt_usec(nni_duration *ptr, const void *val, size_t size) +nni_chkopt_usec(const void *v, size_t sz) +{ + nni_duration val; + if (sz != sizeof(val)) { + return (NNG_EINVAL); + } + memcpy(&val, v, sz); + if (val < -1) { + return (NNG_EINVAL); + } + return (0); +} + +int +nni_chkopt_int(const void *v, size_t sz, int minv, int maxv) +{ + int val; + if (sz != sizeof(val)) { + return (NNG_EINVAL); + } + memcpy(&val, v, sz); + if ((val < minv) || (val > maxv)) { + return (NNG_EINVAL); + } + return (0); +} + +int +nni_chkopt_size(const void *v, size_t sz, size_t minv, size_t maxv) +{ + size_t val; + if (sz != sizeof(val)) { + return (NNG_EINVAL); + } + memcpy(&val, v, sz); + if ((val < minv) || (val > maxv)) { + return (NNG_EINVAL); + } + return (0); +} + +int +nni_setopt_usec(nni_duration *dp, const void *v, size_t sz) { nni_duration dur; - if (size != sizeof(*ptr)) { + if (sz != sizeof(*dp)) { return (NNG_EINVAL); } - memcpy(&dur, val, sizeof(dur)); + memcpy(&dur, v, sizeof(dur)); if (dur < -1) { return (NNG_EINVAL); } - *ptr = dur; + *dp = dur; return (0); } int -nni_setopt_int(int *ptr, const void *val, size_t size, int minval, int maxval) +nni_setopt_int(int *ip, const void *v, size_t sz, int minv, int maxv) { - int v; + int i; - if (size != sizeof(v)) { + if (sz != sizeof(i)) { return (NNG_EINVAL); } - memcpy(&v, val, sizeof(v)); - if (v > maxval) { + memcpy(&i, v, sizeof(i)); + if (i > maxv) { return (NNG_EINVAL); } - if (v < minval) { + if (i < minv) { return (NNG_EINVAL); } - *ptr = v; + *ip = i; return (0); } int -nni_setopt_size( - size_t *ptr, const void *val, size_t size, size_t minval, size_t maxval) +nni_setopt_size(size_t *sp, const void *v, size_t sz, size_t minv, size_t maxv) { - size_t v; + size_t val; - if (size != sizeof(v)) { + if (sz != sizeof(val)) { return (NNG_EINVAL); } - memcpy(&v, val, sizeof(v)); - if (v > maxval) { + memcpy(&val, v, sizeof(val)); + if (val > maxv) { return (NNG_EINVAL); } - if (v < minval) { + if (val < minv) { return (NNG_EINVAL); } - *ptr = v; + *sp = val; return (0); } diff --git a/src/core/options.h b/src/core/options.h index 8e6e1edf..4b29c4b6 100644 --- a/src/core/options.h +++ b/src/core/options.h @@ -1,5 +1,6 @@ // -// Copyright 2016 Garrett D'Amore <garrett@damore.org> +// 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 @@ -51,4 +52,8 @@ extern int nni_getopt_size(size_t *, void *, size_t *); // nni_getopt_fd obtains a notification file descriptor. extern int nni_getopt_fd(nni_sock *, nni_notifyfd *, int, void *, size_t *); +extern int nni_chkopt_usec(const void *, size_t); +extern int nni_chkopt_int(const void *, size_t, int, int); +extern int nni_chkopt_size(const void *, size_t, size_t, size_t); + #endif // CORE_OPTIONS_H diff --git a/src/core/pipe.c b/src/core/pipe.c index cdbeb6a7..94524da4 100644 --- a/src/core/pipe.c +++ b/src/core/pipe.c @@ -117,7 +117,9 @@ nni_pipe_destroy(nni_pipe *p) if (nni_list_node_active(&p->p_ep_node)) { nni_ep_pipe_remove(p->p_ep, p); } - nni_sock_pipe_remove(p->p_sock, p); + if (nni_list_node_active(&p->p_sock_node)) { + nni_sock_pipe_remove(p->p_sock, p); + } if (p->p_tran_data != NULL) { p->p_tran_ops.p_fini(p->p_tran_data); diff --git a/src/core/socket.c b/src/core/socket.c index 9aa89a2d..79c1602b 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -18,6 +18,13 @@ static nni_list nni_sock_list; static nni_idhash *nni_sock_hash; static nni_mtx nni_sock_lk; +typedef struct nni_sockopt { + nni_list_node node; + int opt; + size_t sz; + void * data; +} nni_sockopt; + struct nni_socket { nni_list_node s_node; nni_mtx s_mx; @@ -44,7 +51,8 @@ struct nni_socket { nni_duration s_rcvtimeo; // receive timeout nni_duration s_reconn; // reconnect time nni_duration s_reconnmax; // max reconnect time - size_t s_rcvmaxsz; // maximum receive size + size_t s_rcvmaxsz; // max receive size + nni_list s_options; // opts not handled by sock/proto nni_list s_eps; // active endpoints nni_list s_pipes; // active pipes @@ -63,6 +71,13 @@ struct nni_socket { nni_notifyfd s_recv_fd; }; +static void +nni_free_opt(nni_sockopt *opt) +{ + nni_free(opt->data, opt->sz); + NNI_FREE_STRUCT(opt); +} + uint32_t nni_sock_id(nni_sock *s) { @@ -268,6 +283,8 @@ nni_sock_unnotify(nni_sock *sock, nni_notify *notify) static void nni_sock_destroy(nni_sock *s) { + nni_sockopt *sopt; + if (s == NULL) { return; } @@ -285,6 +302,11 @@ nni_sock_destroy(nni_sock *s) s->s_sock_ops.sock_fini(s->s_data); } + while ((sopt = nni_list_first(&s->s_options)) != NULL) { + nni_list_remove(&s->s_options, sopt); + nni_free_opt(sopt); + } + nni_ev_fini(&s->s_send_ev); nni_ev_fini(&s->s_recv_ev); nni_msgq_fini(s->s_urq); @@ -305,8 +327,8 @@ nni_sock_create(nni_sock **sp, const nni_proto *proto) return (NNG_ENOMEM); } s->s_linger = 0; - s->s_sndtimeo = -1; - s->s_rcvtimeo = -1; + s->s_sndtimeo = NNI_TIME_NEVER; + s->s_rcvtimeo = NNI_TIME_NEVER; s->s_closing = 0; s->s_reconn = NNI_SECOND; s->s_reconnmax = 0; @@ -328,6 +350,7 @@ nni_sock_create(nni_sock **sp, const nni_proto *proto) NNI_ASSERT(s->s_pipe_ops.pipe_stop != NULL); NNI_LIST_NODE_INIT(&s->s_node); + NNI_LIST_INIT(&s->s_options, nni_sockopt, node); nni_pipe_sock_list_init(&s->s_pipes); nni_ep_list_init(&s->s_eps); nni_mtx_init(&s->s_mx); @@ -338,10 +361,23 @@ nni_sock_create(nni_sock **sp, const nni_proto *proto) if (((rv = nni_msgq_init(&s->s_uwq, 0)) != 0) || ((rv = nni_msgq_init(&s->s_urq, 0)) != 0) || - ((rv = s->s_sock_ops.sock_init(&s->s_data, s)) != 0)) { + ((rv = s->s_sock_ops.sock_init(&s->s_data, s)) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_LINGER, &s->s_linger, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_SNDTIMEO, &s->s_sndtimeo, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RCVTIMEO, &s->s_rcvtimeo, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RECONN_TIME, &s->s_reconn, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RECONN_MAXTIME, &s->s_reconnmax, + sizeof(nni_duration))) != 0) || + ((rv = nni_sock_setopt(s, NNG_OPT_RCVMAXSZ, &s->s_rcvmaxsz, + sizeof(size_t))) != 0)) { nni_sock_destroy(s); return (rv); } + *sp = s; return (rv); } @@ -383,7 +419,6 @@ nni_sock_open(nni_sock **sockp, const nni_proto *proto) if (((rv = nni_init()) != 0) || ((rv = nni_sock_create(&s, proto)) != 0)) { - nni_sock_destroy(s); return (rv); } @@ -690,12 +725,6 @@ nni_sock_peer(nni_sock *sock) return (sock->s_peer_id.p_id); } -size_t -nni_sock_rcvmaxsz(nni_sock *sock) -{ - return (sock->s_rcvmaxsz); -} - void nni_sock_reconntimes(nni_sock *sock, nni_duration *rcur, nni_duration *rmax) { @@ -707,15 +736,25 @@ nni_sock_reconntimes(nni_sock *sock, nni_duration *rcur, nni_duration *rmax) } int -nni_sock_ep_add(nni_sock *sock, nni_ep *ep) +nni_sock_ep_add(nni_sock *s, nni_ep *ep) { - nni_mtx_lock(&sock->s_mx); - if (sock->s_closing) { - nni_mtx_unlock(&sock->s_mx); + nni_sockopt *sopt; + + nni_mtx_lock(&s->s_mx); + if (s->s_closing) { + nni_mtx_unlock(&s->s_mx); return (NNG_ECLOSED); } - nni_list_append(&sock->s_eps, ep); - nni_mtx_unlock(&sock->s_mx); + NNI_LIST_FOREACH (&s->s_options, sopt) { + int rv; + rv = nni_ep_setopt(ep, sopt->opt, sopt->data, sopt->sz, 0); + if ((rv != 0) && (rv != NNG_ENOTSUP)) { + nni_mtx_unlock(&s->s_mx); + return (rv); + } + } + nni_list_append(&s->s_eps, ep); + nni_mtx_unlock(&s->s_mx); return (0); } @@ -745,108 +784,222 @@ nni_sock_senderr(nni_sock *sock, int err) } int -nni_sock_setopt(nni_sock *sock, int opt, const void *val, size_t size) +nni_sock_setopt(nni_sock *s, int opt, const void *val, size_t size) { - int rv = NNG_ENOTSUP; + int rv = NNG_ENOTSUP; + nni_ep * ep; + int commits = 0; + nni_sockopt *optv; + nni_sockopt *oldv = NULL; - nni_mtx_lock(&sock->s_mx); - if (sock->s_closing) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_lock(&s->s_mx); + if (s->s_closing) { + nni_mtx_unlock(&s->s_mx); return (NNG_ECLOSED); } - if (sock->s_sock_ops.sock_setopt != NULL) { - rv = - sock->s_sock_ops.sock_setopt(sock->s_data, opt, val, size); + if (s->s_sock_ops.sock_setopt != NULL) { + rv = s->s_sock_ops.sock_setopt(s->s_data, opt, val, size); if (rv != NNG_ENOTSUP) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_unlock(&s->s_mx); return (rv); } } + + // Some options do not go down to transports. Handle them + // directly. switch (opt) { - case NNG_OPT_LINGER: - rv = nni_setopt_usec(&sock->s_linger, val, size); - break; - case NNG_OPT_SNDTIMEO: - rv = nni_setopt_usec(&sock->s_sndtimeo, val, size); - break; - case NNG_OPT_RCVTIMEO: - rv = nni_setopt_usec(&sock->s_rcvtimeo, val, size); - break; case NNG_OPT_RECONN_TIME: - rv = nni_setopt_usec(&sock->s_reconn, val, size); + rv = nni_setopt_usec(&s->s_reconn, val, size); break; case NNG_OPT_RECONN_MAXTIME: - rv = nni_setopt_usec(&sock->s_reconnmax, val, size); + rv = nni_setopt_usec(&s->s_reconnmax, val, size); break; case NNG_OPT_SNDBUF: - rv = nni_setopt_buf(sock->s_uwq, val, size); + rv = nni_setopt_buf(s->s_uwq, val, size); break; case NNG_OPT_RCVBUF: - rv = nni_setopt_buf(sock->s_urq, val, size); + rv = nni_setopt_buf(s->s_urq, val, size); break; - case NNG_OPT_RCVMAXSZ: - rv = nni_setopt_size( - &sock->s_rcvmaxsz, val, size, 0, NNI_MAXSZ); + case NNG_OPT_SNDFD: + case NNG_OPT_RCVFD: + case NNG_OPT_LOCALADDR: + case NNG_OPT_REMOTEADDR: + // these options can be read, but cannot be set + rv = NNG_EINVAL; break; } - nni_mtx_unlock(&sock->s_mx); + + nni_mtx_unlock(&s->s_mx); + + // If the option was already handled one way or the other, + if (rv != NNG_ENOTSUP) { + return (rv); + } + + // Validation of transport options. This is stateless, so + // transports should not fail to set an option later if they + // passed it here. + rv = nni_tran_chkopt(opt, val, size); + + // Also check a few generic things. We do this if no transport + // check was found, or even if a transport rejected one of the + // settings. + if ((rv == NNG_ENOTSUP) || (rv == 0)) { + switch (opt) { + case NNG_OPT_LINGER: + rv = nni_chkopt_usec(val, size); + break; + case NNG_OPT_SNDTIMEO: + rv = nni_chkopt_usec(val, size); + break; + case NNG_OPT_RCVTIMEO: + rv = nni_chkopt_usec(val, size); + break; + case NNG_OPT_RCVMAXSZ: + // just a sanity test on the size; it also ensures that + // a size can be set even with no transport configured. + rv = nni_chkopt_size(val, size, 0, NNI_MAXSZ); + break; + } + } + + if (rv != 0) { + return (rv); + } + + // Prepare a copy of the sockoption. + if ((optv = NNI_ALLOC_STRUCT(optv)) == NULL) { + return (NNG_ENOMEM); + } + if ((optv->data = nni_alloc(size)) == NULL) { + NNI_FREE_STRUCT(optv); + return (NNG_ENOMEM); + } + memcpy(optv->data, val, size); + optv->opt = opt; + optv->sz = size; + NNI_LIST_NODE_INIT(&optv->node); + + nni_mtx_lock(&s->s_mx); + NNI_LIST_FOREACH (&s->s_options, oldv) { + if (oldv->opt == opt) { + if ((oldv->sz != size) || + (memcmp(oldv->data, val, size) != 0)) { + break; + } + + // The values are the same. This is a no-op. + nni_mtx_unlock(&s->s_mx); + nni_free_opt(optv); + return (0); + } + } + + // Apply the options. Failure to set any option on any transport + // (other than ENOTSUP) stops the operation altogether. Its + // important that transport wide checks properly pre-validate. + NNI_LIST_FOREACH (&s->s_eps, ep) { + int x; + x = nni_ep_setopt(ep, opt, optv->data, size, 0); + if (x != NNG_ENOTSUP) { + if ((rv = x) != 0) { + nni_mtx_unlock(&s->s_mx); + nni_free_opt(optv); + return (rv); + } + } + } + + // For some options, which also have an impact on the socket + // behavior, we save a local value. Note that the transport + // will already have had a chance to veto this. + switch (opt) { + case NNG_OPT_LINGER: + rv = nni_setopt_usec(&s->s_linger, val, size); + break; + case NNG_OPT_SNDTIMEO: + rv = nni_setopt_usec(&s->s_sndtimeo, val, size); + break; + case NNG_OPT_RCVTIMEO: + rv = nni_setopt_usec(&s->s_rcvtimeo, val, size); + break; + } + + if (rv == 0) { + // Remove and toss the old value, we are using a new one. + if (oldv != NULL) { + nni_list_remove(&s->s_options, oldv); + nni_free_opt(oldv); + } + + // Insert our new value. This permits it to be compared + // against later, and for new endpoints to automatically + // receive these values, + nni_list_append(&s->s_options, optv); + } else { + nni_free_opt(optv); + } + + nni_mtx_unlock(&s->s_mx); return (rv); } int -nni_sock_getopt(nni_sock *sock, int opt, void *val, size_t *sizep) +nni_sock_getopt(nni_sock *s, int opt, void *val, size_t *szp) { - int rv = NNG_ENOTSUP; + int rv = NNG_ENOTSUP; + nni_sockopt *sopt; - nni_mtx_lock(&sock->s_mx); - if (sock->s_closing) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_lock(&s->s_mx); + if (s->s_closing) { + nni_mtx_unlock(&s->s_mx); return (NNG_ECLOSED); } - if (sock->s_sock_ops.sock_getopt != NULL) { - rv = sock->s_sock_ops.sock_getopt( - sock->s_data, opt, val, sizep); + if (s->s_sock_ops.sock_getopt != NULL) { + rv = s->s_sock_ops.sock_getopt(s->s_data, opt, val, szp); if (rv != NNG_ENOTSUP) { - nni_mtx_unlock(&sock->s_mx); + nni_mtx_unlock(&s->s_mx); return (rv); } } + // Options that are handled by socket core, and never + // passed down. switch (opt) { - case NNG_OPT_LINGER: - rv = nni_getopt_usec(&sock->s_linger, val, sizep); - break; - case NNG_OPT_SNDTIMEO: - rv = nni_getopt_usec(&sock->s_sndtimeo, val, sizep); - break; - case NNG_OPT_RCVTIMEO: - rv = nni_getopt_usec(&sock->s_rcvtimeo, val, sizep); - break; case NNG_OPT_RECONN_TIME: - rv = nni_getopt_usec(&sock->s_reconn, val, sizep); + rv = nni_getopt_usec(&s->s_reconn, val, szp); break; case NNG_OPT_RECONN_MAXTIME: - rv = nni_getopt_usec(&sock->s_reconnmax, val, sizep); + rv = nni_getopt_usec(&s->s_reconnmax, val, szp); break; case NNG_OPT_SNDBUF: - rv = nni_getopt_buf(sock->s_uwq, val, sizep); + rv = nni_getopt_buf(s->s_uwq, val, szp); break; case NNG_OPT_RCVBUF: - rv = nni_getopt_buf(sock->s_urq, val, sizep); - break; - case NNG_OPT_RCVMAXSZ: - rv = nni_getopt_size(&sock->s_rcvmaxsz, val, sizep); + rv = nni_getopt_buf(s->s_urq, val, szp); break; case NNG_OPT_SNDFD: - rv = nni_getopt_fd( - sock, &sock->s_send_fd, NNG_EV_CAN_SND, val, sizep); + rv = nni_getopt_fd(s, &s->s_send_fd, NNG_EV_CAN_SND, val, szp); break; case NNG_OPT_RCVFD: - rv = nni_getopt_fd( - sock, &sock->s_recv_fd, NNG_EV_CAN_RCV, val, sizep); + rv = nni_getopt_fd(s, &s->s_recv_fd, NNG_EV_CAN_RCV, val, szp); + break; + default: + NNI_LIST_FOREACH (&s->s_options, sopt) { + if (sopt->opt == opt) { + size_t sz = sopt->sz; + if (sopt->sz > *szp) { + sz = *szp; + } + *szp = sopt->sz; + memcpy(val, sopt->data, sz); + rv = 0; + break; + } + } break; } - nni_mtx_unlock(&sock->s_mx); + nni_mtx_unlock(&s->s_mx); return (rv); } diff --git a/src/core/socket.h b/src/core/socket.h index 9fa6c0fa..931fefac 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -64,8 +64,7 @@ extern nni_msgq *nni_sock_sendq(nni_sock *); // inject incoming messages from pipes to it. extern nni_msgq *nni_sock_recvq(nni_sock *); -extern size_t nni_sock_rcvmaxsz(nni_sock *); -extern void nni_sock_reconntimes(nni_sock *, nni_duration *, nni_duration *); +extern void nni_sock_reconntimes(nni_sock *, nni_duration *, nni_duration *); // nni_sock_flags returns the socket flags, used to indicate whether read // and or write are appropriate for the protocol. diff --git a/src/core/transport.c b/src/core/transport.c index 2f373f9a..3130ae26 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -89,6 +89,27 @@ nni_tran_find(const char *addr) return (NULL); } +int +nni_tran_chkopt(int o, const void *v, size_t sz) +{ + nni_transport *t; + int rv = NNG_ENOTSUP; + nni_mtx_lock(&nni_tran_lk); + NNI_LIST_FOREACH (&nni_tran_list, t) { + int x; + if (t->t_tran.tran_chkopt == NULL) { + continue; + } + if ((x = t->t_tran.tran_chkopt(o, v, sz)) != NNG_ENOTSUP) { + if ((rv = x) != 0) { + break; + } + } + } + nni_mtx_unlock(&nni_tran_lk); + return (rv); +} + // nni_tran_sys_init initializes the entire transport subsystem, including // each individual transport. int diff --git a/src/core/transport.h b/src/core/transport.h index 78cb2bbf..2891d8a4 100644 --- a/src/core/transport.h +++ b/src/core/transport.h @@ -29,6 +29,11 @@ struct nni_tran { // tran_pipe links our pipe-specific operations. const nni_tran_pipe *tran_pipe; + // tran_chkopt, if not NULL, is used to validate that the + // option data presented is valid. This allows an option to + // be set on a socket, even if no endpoints are configured. + int (*tran_chkopt)(int, const void *, size_t); + // tran_init, if not NULL, is called once during library // initialization. int (*tran_init)(void); @@ -135,6 +140,7 @@ struct nni_tran_pipe { // These APIs are used by the framework internally, and not for use by // transport implementations. extern nni_tran *nni_tran_find(const char *); +extern int nni_tran_chkopt(int, const void *, size_t); extern int nni_tran_sys_init(void); extern void nni_tran_sys_fini(void); extern int nni_tran_register(const nni_tran *); |
