From c4da7817b4c8dd71b2a07d4d1c46b486ec57eeb4 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Mon, 5 Mar 2018 17:27:04 -0800 Subject: fixes #265 nngcat should support persistent ZT nodes fixes #267 zerotier transport should lock ZT_HOME --- src/core/file.c | 28 +++++++++++++++ src/core/file.h | 6 ++++ src/core/platform.h | 12 +++++++ src/platform/posix/posix_file.c | 27 +++++++++++++++ src/platform/posix/posix_impl.h | 4 +++ src/platform/windows/win_debug.c | 13 +++---- src/platform/windows/win_file.c | 24 +++++++++++++ src/platform/windows/win_impl.h | 4 +++ src/transport/zerotier/zerotier.c | 71 +++++++++++++++++++++++++-------------- tools/nngcat/nngcat.c | 26 ++++++++++++++ 10 files changed, 183 insertions(+), 32 deletions(-) diff --git a/src/core/file.c b/src/core/file.c index 4715be2a..b76fdcda 100644 --- a/src/core/file.c +++ b/src/core/file.c @@ -128,4 +128,32 @@ const char * nni_file_basename(const char *path) { return (nni_plat_file_basename(path)); +} + +struct nni_file_lockh { + nni_plat_flock lk; +}; + +int +nni_file_lock(const char *path, nni_file_lockh **hp) +{ + nni_file_lockh *h; + int rv; + if ((h = NNI_ALLOC_STRUCT(h)) == NULL) { + return (NNG_ENOMEM); + } + rv = nni_plat_file_lock(path, &h->lk); + if (rv != 0) { + NNI_FREE_STRUCT(h); + return (rv); + } + *hp = h; + return (0); +} + +void +nni_file_unlock(nni_file_lockh *h) +{ + nni_plat_file_unlock(&h->lk); + NNI_FREE_STRUCT(h); } \ No newline at end of file diff --git a/src/core/file.h b/src/core/file.h index 7969ae5f..3baa1ccd 100644 --- a/src/core/file.h +++ b/src/core/file.h @@ -84,4 +84,10 @@ extern bool nni_file_is_file(const char *); // false if an error occurs, or the path references something else. extern bool nni_file_is_dir(const char *); +typedef struct nni_file_lockh nni_file_lockh; + +extern int nni_file_lock(const char *, nni_file_lockh **); + +extern void nni_file_unlock(nni_file_lockh *); + #endif // CORE_FILE_H diff --git a/src/core/platform.h b/src/core/platform.h index 3f336f11..d5fc40f7 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -434,6 +434,18 @@ typedef int (*nni_plat_file_walker)(const char *, void *); // with the path name, and the supplied void * argument. extern int nni_plat_file_walk(const char *, nni_plat_file_walker, void *, int); +typedef struct nni_plat_flock nni_plat_flock; + +// nni_plat_file_lock locks the file. This usually means open it (creating +// if it does not exist) and doing a lock operation. The nni_plat_flock +// is our handle for the lock, to unlock. Usually its just a file descriptor, +// and we can unlock by doing close(). Note that this is a "try-lock" +// operation -- if the file is already locked then NNG_EBUSY is returned. +extern int nni_plat_file_lock(const char *path, nni_plat_flock *); + +// nni_plat_file_unlock unlocks the previously locked file. +extern void nni_plat_file_unlock(nni_plat_flock *); + // nni_plat_dir_open attempts to "open a directory" for listing. The // handle for further operations is returned in the first argument, and // the directory name is supplied in the second. diff --git a/src/platform/posix/posix_file.c b/src/platform/posix/posix_file.c index 7863fdee..83d045fa 100644 --- a/src/platform/posix/posix_file.c +++ b/src/platform/posix/posix_file.c @@ -260,6 +260,33 @@ nni_plat_file_basename(const char *path) return (path); } +int +nni_plat_file_lock(const char *path, nni_plat_flock *lk) +{ + int fd; + if ((fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { + return (nni_plat_errno(errno)); + } + if (lockf(fd, F_TLOCK, 0) < 0) { + int rv = errno; + close(fd); + if (rv == EAGAIN) { + return (NNG_EBUSY); + } + return (nni_plat_errno(rv)); + } + lk->fd = fd; + return (0); +} + +void +nni_plat_file_unlock(nni_plat_flock *lk) +{ + int fd = lk->fd; + lk->fd = -1; + (void) close(fd); +} + char * nni_plat_temp_dir(void) { diff --git a/src/platform/posix/posix_impl.h b/src/platform/posix/posix_impl.h index 0b2a09f0..3616c69b 100644 --- a/src/platform/posix/posix_impl.h +++ b/src/platform/posix/posix_impl.h @@ -76,6 +76,10 @@ struct nni_plat_thr { void *arg; }; +struct nni_plat_flock { + int fd; +}; + #define NNG_PLATFORM_DIR_SEP "/" #endif diff --git a/src/platform/windows/win_debug.c b/src/platform/windows/win_debug.c index 00e327db..a47e7b41 100644 --- a/src/platform/windows/win_debug.c +++ b/src/platform/windows/win_debug.c @@ -1,5 +1,6 @@ // -// Copyright 2017 Garrett D'Amore +// Copyright 2018 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -83,9 +84,8 @@ nni_plat_errno(int errnum) static struct { int win_err; int nng_err; -} nni_win_errnos[] = - { - // clang-format off +} nni_win_errnos[] = { + // clang-format off { ERROR_FILE_NOT_FOUND, NNG_ENOENT }, { ERROR_PATH_NOT_FOUND, NNG_ENOENT }, { ERROR_ACCESS_DENIED, NNG_EPERM }, @@ -104,6 +104,7 @@ static struct { { ERROR_NO_DATA, NNG_ECLOSED }, { ERROR_PIPE_NOT_CONNECTED, NNG_ECLOSED }, { ERROR_OPERATION_ABORTED, NNG_ECLOSED }, + { ERROR_SHARING_VIOLATION, NNG_EBUSY }, { WAIT_TIMEOUT, NNG_ETIMEDOUT }, { WSAEINTR, NNG_EINTR }, { WSAEBADF, NNG_ECLOSED }, @@ -143,8 +144,8 @@ static struct { // Must be Last!! { 0, 0 }, - // clang-format on - }; + // clang-format on +}; // This converts a Windows API error (from GetLastError()) to an // nng standard error code. diff --git a/src/platform/windows/win_file.c b/src/platform/windows/win_file.c index 2a9504aa..515647e0 100644 --- a/src/platform/windows/win_file.c +++ b/src/platform/windows/win_file.c @@ -342,4 +342,28 @@ nni_plat_file_basename(const char *name) return (name); } +int +nni_plat_file_lock(const char *path, nni_plat_flock *lk) +{ + HANDLE h; + + // On Windows we do not have to explicitly lock the file, the + // dwShareMode being set to zeor effectively prevents it. + h = CreateFile(path, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + return (nni_win_error(GetLastError())); + } + lk->h = h; + return (0); +} + +void +nni_plat_file_unlock(nni_plat_flock *lk) +{ + HANDLE h = lk->h; + (void) CloseHandle(h); + lk->h = INVALID_HANDLE_VALUE; +} + #endif // NNG_PLATFORM_WINDOWS \ No newline at end of file diff --git a/src/platform/windows/win_impl.h b/src/platform/windows/win_impl.h index 0c22c240..b3c4738f 100644 --- a/src/platform/windows/win_impl.h +++ b/src/platform/windows/win_impl.h @@ -75,6 +75,10 @@ struct nni_win_event { nni_win_event_ops ops; }; +struct nni_plat_flock { + HANDLE h; +}; + extern int nni_win_error(int); extern int nni_win_event_init(nni_win_event *, nni_win_event_ops *, void *); diff --git a/src/transport/zerotier/zerotier.c b/src/transport/zerotier/zerotier.c index e31bf113..8f83881c 100644 --- a/src/transport/zerotier/zerotier.c +++ b/src/transport/zerotier/zerotier.c @@ -153,30 +153,31 @@ enum zt_errors { // them with a reference count, and uniquely identify them using the // homedir. struct zt_node { - char zn_path[NNG_MAXADDRLEN]; // ought to be sufficient - ZT_Node * zn_znode; - uint64_t zn_self; - nni_list_node zn_link; - int zn_closed; - nni_plat_udp *zn_udp4; - nni_plat_udp *zn_udp6; - nni_list zn_eplist; - nni_list zn_plist; - nni_idhash * zn_ports; - nni_idhash * zn_eps; - nni_idhash * zn_lpipes; - nni_idhash * zn_rpipes; - nni_idhash * zn_peers; // indexed by remote address - nni_aio * zn_rcv4_aio; - uint8_t * zn_rcv4_buf; - nng_sockaddr zn_rcv4_addr; - nni_aio * zn_rcv6_aio; - uint8_t * zn_rcv6_buf; - nng_sockaddr zn_rcv6_addr; - nni_thr zn_bgthr; - int64_t zn_bgtime; - nni_cv zn_bgcv; - nni_cv zn_snd6_cv; + char zn_path[NNG_MAXADDRLEN]; // ought to be sufficient + nni_file_lockh *zn_flock; + ZT_Node * zn_znode; + uint64_t zn_self; + nni_list_node zn_link; + int zn_closed; + nni_plat_udp * zn_udp4; + nni_plat_udp * zn_udp6; + nni_list zn_eplist; + nni_list zn_plist; + nni_idhash * zn_ports; + nni_idhash * zn_eps; + nni_idhash * zn_lpipes; + nni_idhash * zn_rpipes; + nni_idhash * zn_peers; // indexed by remote address + nni_aio * zn_rcv4_aio; + uint8_t * zn_rcv4_buf; + nng_sockaddr zn_rcv4_addr; + nni_aio * zn_rcv6_aio; + uint8_t * zn_rcv6_buf; + nng_sockaddr zn_rcv6_addr; + nni_thr zn_bgthr; + int64_t zn_bgtime; + nni_cv zn_bgcv; + nni_cv zn_snd6_cv; }; // The fragment list is used to keep track of incoming received @@ -300,7 +301,7 @@ static void zt_bgthr(void *arg) { zt_node *ztn = arg; - int64_t now; + int64_t now; nni_mtx_lock(&zt_lk); for (;;) { @@ -559,7 +560,7 @@ zt_send(zt_node *ztn, uint64_t nwid, uint8_t op, uint64_t raddr, { uint64_t srcmac = zt_node_to_mac(laddr >> 24, nwid); uint64_t dstmac = zt_node_to_mac(raddr >> 24, nwid); - int64_t now = zt_now(); + int64_t now = zt_now(); NNI_ASSERT(len >= zt_size_headers); data[zt_offset_op] = op; @@ -1422,6 +1423,9 @@ zt_node_destroy(zt_node *ztn) if (ztn->zn_rcv6_buf != NULL) { nni_free(ztn->zn_rcv6_buf, zt_rcv_bufsize); } + if (ztn->zn_flock != NULL) { + nni_file_unlock(ztn->zn_flock); + } nni_aio_fini(ztn->zn_rcv4_aio); nni_aio_fini(ztn->zn_rcv6_aio); nni_idhash_fini(ztn->zn_eps); @@ -1480,6 +1484,21 @@ zt_node_create(zt_node **ztnp, const char *path) return (rv); } + if (strlen(path) > 0) { + char *lkfile; + if ((lkfile = nni_file_join(path, "lock")) == NULL) { + zt_node_destroy(ztn); + return (NNG_ENOMEM); + } + + if ((rv = nni_file_lock(lkfile, &ztn->zn_flock)) != 0) { + zt_node_destroy(ztn); + nni_strfree(lkfile); + return (rv); + } + nni_strfree(lkfile); + } + // Setup for dynamic ephemeral port allocations. We // set the range to allow for ephemeral ports, but not // higher than the max port, and starting with an diff --git a/tools/nngcat/nngcat.c b/tools/nngcat/nngcat.c index 7459c319..9dfa9561 100644 --- a/tools/nngcat/nngcat.c +++ b/tools/nngcat/nngcat.c @@ -31,6 +31,7 @@ #include "supplemental/tls/tls.h" #include "supplemental/util/options.h" #include "supplemental/util/platform.h" +#include "transport/zerotier/zerotier.h" // Globals. We need this to avoid passing around everything. int format = 0; @@ -51,6 +52,7 @@ void * keyfile = NULL; size_t keylen = 0; void * certfile = NULL; size_t certlen = 0; +const char * zthome = NULL; // Options, must start at 1 because zero is sentinel. enum options { @@ -97,6 +99,7 @@ enum options { OPT_KEYFILE, OPT_CERTFILE, OPT_VERSION, + OPT_ZTHOME, }; static nng_optspec opts[] = { @@ -180,6 +183,9 @@ static nng_optspec opts[] = { .o_val = OPT_CERTFILE, .o_arg = true, }, + { + .o_name = "zt-home", .o_val = OPT_ZTHOME, .o_arg = true, + }, { .o_name = "version", .o_short = 'V', .o_val = OPT_VERSION }, // Sentinel. @@ -234,6 +240,7 @@ help(void) printf(" --verbose (or alias -v)\n"); printf(" --compat\n"); printf(" --async\n"); + printf(" --zt-home \n"); printf("\n may be one of:\n"); printf(" --file (or alias -F )\n"); printf(" --data (or alias -D )\n"); @@ -783,6 +790,9 @@ main(int ac, const char **av) } loadfile(arg, &certfile, &certlen); break; + case OPT_ZTHOME: + zthome = arg; + break; case OPT_INSECURE: insecure = 1; break; @@ -1006,6 +1016,14 @@ main(int ac, const char **av) fatal("Unable to get TLS config: %s", nng_strerror(rv)); } + if (zthome != NULL) { + rv = nng_dialer_setopt(d, NNG_OPT_ZT_HOME, + zthome, strlen(zthome) + 1); + if ((rv != 0) && (rv != NNG_ENOTSUP)) { + fatal("Unable to set ZT home: %s", + nng_strerror(rv)); + } + } rv = nng_dialer_start(d, async); act = "dial"; if ((rv == 0) && (verbose == OPT_VERBOSE)) { @@ -1034,6 +1052,14 @@ main(int ac, const char **av) fatal("Unable to get TLS config: %s", nng_strerror(rv)); } + if (zthome != NULL) { + rv = nng_listener_setopt(l, NNG_OPT_ZT_HOME, + zthome, strlen(zthome) + 1); + if ((rv != 0) && (rv != NNG_ENOTSUP)) { + fatal("Unable to set ZT home: %s", + nng_strerror(rv)); + } + } rv = nng_listener_start(l, async); act = "listen"; if ((rv == 0) && (verbose == OPT_VERBOSE)) { -- cgit v1.2.3-70-g09d2