diff --git a/.travis.yml b/.travis.yml index a3197a2cec61380bdf9a161331f69564f8cb2a3a..e828daa083980d5f49d9a8351f3228c9369adfb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ before_script: install: - sudo apt-get update > /dev/null - - sudo apt-get -q install binutils-dev python-daemon python-yaml uuid-dev + - sudo apt-get -q install binutils-dev python-daemon python-yaml - sudo apt-get -q install libmysqlclient-dev libpq-dev postgresql-server-dev-all script: diff --git a/CMakeLists.txt b/CMakeLists.txt index d107dacfd43ab6c5ac02e9f1fde42df9b5dce439..424d296f848ee6302457e797b3c7a36e28d69b6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,7 @@ endif() check_function_exists(open_memstream HAVE_OPEN_MEMSTREAM) check_function_exists(fmemopen HAVE_FMEMOPEN) check_function_exists(funopen HAVE_FUNOPEN) +check_function_exists(uuidgen HAVE_UUIDGEN) # # Some versions of GNU libc define non-portable __libc_stack_end @@ -305,13 +306,6 @@ include(BuildLibYAML) libyaml_build() add_dependencies(build_bundled_libs yaml) -# -# LibUUID -# - -set(LIBUUID_FIND_REQUIRED ON) -find_package(LibUUID) - # # Third-Party misc # diff --git a/README.md b/README.md index ca3d201b4837fa2bf21d910ae5f899414c9a9b83..223a68b012f233464ca4dcae0cfe63331e14c9b2 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,6 @@ CMake is used for configuration management. The build depends on the following external libraries: - libreadline and libreadline-dev -- uuid and uuid-dev. - GNU bfd (part of GNU binutils). Please follow these steps to compile Tarantool: diff --git a/cmake/BuildLibEIO.cmake b/cmake/BuildLibEIO.cmake index 582c96fe17887e4a8c93819a596b027b971d71ad..3f4c59a924f242d9e8f7920e53ee3a7efa4ba5aa 100644 --- a/cmake/BuildLibEIO.cmake +++ b/cmake/BuildLibEIO.cmake @@ -5,6 +5,7 @@ macro(libeio_build) set(eio_compile_flags "${eio_compile_flags} -Wno-unused-result") set(eio_compile_flags "${eio_compile_flags} -Wno-dangling-else") + set(eio_compile_flags "${eio_compile_flags} -Wno-unused-value") set(eio_compile_flags "${eio_compile_flags} -DENABLE_BUNDLED_LIBEIO=1") set(eio_compile_flags "${eio_compile_flags} -DEIO_STACKSIZE=0") diff --git a/cmake/FindLibUUID.cmake b/cmake/FindLibUUID.cmake deleted file mode 100644 index 0db8ae7e5e155eb0b371a02909566c449b2ff255..0000000000000000000000000000000000000000 --- a/cmake/FindLibUUID.cmake +++ /dev/null @@ -1,31 +0,0 @@ -if(NOT LIBUUID_FOUND) - find_path(LIBUUID_INCLUDE_DIR - NAMES uuid.h - PATH_SUFFIXES uuid - ) - if (LIBUUID_INCLUDE_DIR) - if (NOT LIBUUID_FIND_QUIETLY) - message(STATUS "Found libuuid includes: ${LIBUUID_INCLUDE_DIR}") - endif () - set(LIBUUID_INCLUDE_DIRS ${LIBUUID_INCLUDE_DIR}) - check_library_exists(uuid uuid_is_null "" HAVE_LIBUUID_LINUX) - if (HAVE_LIBUUID_LINUX) - if (NOT LIBUUID_FIND_QUIETLY) - message(STATUS "Found libuuid library: ${LIBUUID_LIBRARIES}") - endif () - set(LIBUUID_FOUND ON) - set(LIBUUID_LIBRARIES uuid) - else() - check_library_exists(c uuid_is_nil "" HAVE_LIBUUID_BSD) - if (HAVE_LIBUUID_BSD) - set(LIBUUID_FOUND ON) - elseif (LIBUUID_FIND_REQUIRED) - message(FATAL_ERROR "Could not find uuid libraries") - endif() - endif() - elseif(LIBUUID_FIND_REQUIRED) - message(FATAL_ERROR "Could not find uuid development files") - endif() -endif (NOT LIBUUID_FOUND) - -mark_as_advanced(LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS) diff --git a/debian/control b/debian/control index a75e10a13487acba64280c921aa5710728623e52..c54bb43cb8fb2355f1167b3300ef5544458bd119 100644 --- a/debian/control +++ b/debian/control @@ -7,8 +7,7 @@ Build-Depends: cdbs, debhelper (>= 8), libncurses5-dev, libiberty-dev | binutils-dev, libmysqlclient-dev, - libpq-dev, - uuid-dev + libpq-dev Section: database Standards-Version: 3.9.5 Homepage: http://tarantool.org/ diff --git a/doc/user/tutorial.xml b/doc/user/tutorial.xml index b563003b482056fc852d6c014aefbcad491f27b1..184c5d3fbd40a040a82d63c682deedc1a5dec16b 100644 --- a/doc/user/tutorial.xml +++ b/doc/user/tutorial.xml @@ -283,7 +283,6 @@ ones unless you intend to work on the documentation.</para> <listitem><para>binutils-dev or binutils-devel # contains GNU bfd for printing stack traces</para></listitem> <listitem><para> gcc or clang # see above</para></listitem> <listitem><para> git # see above</para></listitem> - <listitem><para> uuid-dev # for uuid_* functions and replication</para></listitem> <listitem><para> cmake # see above</para></listitem> <listitem><para> libreadline-dev # for interactive mode</para></listitem> <listitem><para> libncurses5-dev or ncurses-devel # see above</para></listitem> @@ -459,7 +458,7 @@ the server (but not the documentation), and run tests after build. <para> To build with SUSE 13.1, the steps are as described above, except that the appropriate YaST2 package names are: -binutils-devel, libuuid-devel, cmake, ncurses-devel, lynx, jing, libxml2-devel, docbook_5, saxon, libxslt-devel. +binutils-devel, cmake, ncurses-devel, lynx, jing, libxml2-devel, docbook_5, saxon, libxslt-devel. The python connector can be installed with <code>sudo easy_install pip</code> and <code>sudo pip install tarantool</code>. </para> diff --git a/extra/rpm/tarantool.rpm.spec.in b/extra/rpm/tarantool.rpm.spec.in index c7dc52640d1b37615eae9ed193d86b62b21d364b..b241071d92c97f844c075078ae061a30778965f3 100644 --- a/extra/rpm/tarantool.rpm.spec.in +++ b/extra/rpm/tarantool.rpm.spec.in @@ -23,12 +23,6 @@ BuildRequires: gcc >= 4.5 BuildRequires: binutils-devel %endif -%if 0%{?rhel} < 6 && 0%{?rhel} > 0 -BuildRequires: e2fsprogs-devel -%else -BuildRequires: libuuid-devel -%endif - %if 0%{?fedora} > 0 BuildRequires: perl-podlators %endif @@ -45,11 +39,6 @@ Vendor: tarantool.org License: BSD Requires: %{?scl_prefix}tarantool-debuginfo = @RPM_PACKAGE_VERSION@-@RPM_PACKAGE_RELEASE@ Requires: readline -%if 0%{?rhel} <= 5 && 0%{?rhel} > 0 -Requires: e2fsprogs-libs -%else -Requires: libuuid -%endif URL: http://tarantool.org Source0: @RPM_PACKAGE_SOURCE_FILE_NAME@ %description diff --git a/src/box/cluster.cc b/src/box/cluster.cc index a5bf9664222f4f2c677b2c33d5e426d72bd2fd1d..48f4f6293162fc621d6b6dda725d1ae4f23ea427 100644 --- a/src/box/cluster.cc +++ b/src/box/cluster.cc @@ -53,7 +53,7 @@ cluster_add_server(const tt_uuid *server_uuid, uint32_t server_id) /* Add server */ vclock_add_server(&r->vclock, server_id); - if (tt_uuid_cmp(&r->server_uuid, server_uuid) == 0) { + if (tt_uuid_is_equal(&r->server_uuid, server_uuid)) { /* Assign local server id */ assert(r->server_id == 0); r->server_id = server_id; diff --git a/src/box/log_io.cc b/src/box/log_io.cc index c0e719e38aba3fc773d03ef1d349300550a51bd5..69e6d1660d96a257229c9f05d4e026eaeb7aeef4 100644 --- a/src/box/log_io.cc +++ b/src/box/log_io.cc @@ -709,7 +709,7 @@ log_io_verify_meta(struct log_io *l, const tt_uuid *server_uuid) } if (server_uuid != NULL && !tt_uuid_is_nil(server_uuid) && - tt_uuid_cmp(server_uuid, &l->server_uuid)) { + !tt_uuid_is_equal(server_uuid, &l->server_uuid)) { say_error("%s: invalid server uuid", l->filename); return -1; } diff --git a/src/box/replication.cc b/src/box/replication.cc index 81c8300b78790e7b05e3cd1767f06939ef0cdb82..8b410d13f09d47c5979b0ca100743d97a6856716 100644 --- a/src/box/replication.cc +++ b/src/box/replication.cc @@ -249,7 +249,7 @@ replication_subscribe(int fd, struct iproto_header *packet) * replica connect, and refuse a connection from a replica * which belongs to a different cluster. */ - if (tt_uuid_cmp(&uu, &cluster_id) != 0) { + if (!tt_uuid_is_equal(&uu, &cluster_id)) { tnt_raise(ClientError, ER_CLUSTER_ID_MISMATCH, tt_uuid_str(&uu), tt_uuid_str(&cluster_id)); } diff --git a/src/find_path.c b/src/find_path.c index f0767238056c83c328b25558d62380ed5d7dc6e5..749596573d466f8fbe11c683cbae1675cb0e5816 100644 --- a/src/find_path.c +++ b/src/find_path.c @@ -21,7 +21,7 @@ find_path(const char *argv0) return path; char buf[PATH_MAX]; - uint32_t size = PATH_MAX - 1; + size_t size = PATH_MAX - 1; if (argv0[0] == '/') snprintf(buf, size, "%s", argv0); else { @@ -35,7 +35,8 @@ find_path(const char *argv0) snprintf(buf, size, "%s", getexecname()); rc = 0; #elif defined(__APPLE__) - rc = _NSGetExecutablePath(buf, &size); + uint32_t usize = size; + rc = _NSGetExecutablePath(buf, &usize); #endif if (rc == -1) snprintf(buf, sizeof(buf) - 1, "%s", getenv("_")); diff --git a/src/iproto.cc b/src/iproto.cc index 06a237cafcdb8d6aee5c3f815574921d89950cc0..ad52bc05ada65fe67fee61ab860347d227567581 100644 --- a/src/iproto.cc +++ b/src/iproto.cc @@ -745,13 +745,12 @@ iproto_request_new(struct iproto_connection *con, } const char * -iproto_greeting(int *salt) +iproto_greeting(const char *salt) { static __thread char greeting[IPROTO_GREETING_SIZE + 1]; char base64buf[SESSION_SEED_SIZE * 4 / 3 + 5]; - base64_encode((char *) salt, SESSION_SEED_SIZE, - base64buf, sizeof(base64buf)); + base64_encode(salt, SESSION_SEED_SIZE, base64buf, sizeof(base64buf)); snprintf(greeting, sizeof(greeting), "Tarantool %-20s %-32s\n%-63s\n", tarantool_version(), custom_proc_title, base64buf); diff --git a/src/lib/bit/bit.c b/src/lib/bit/bit.c index b4686dcaa3b17b0c2f4ffecb05192a34d6f0355c..0ebf555919ee166d6e614886a4ccd34da6367f0f 100644 --- a/src/lib/bit/bit.c +++ b/src/lib/bit/bit.c @@ -68,6 +68,9 @@ bit_rotr_u32(uint32_t x, int r); extern inline uint64_t bit_rotr_u64(uint64_t x, int r); +extern inline uint16_t +bswap_u16(uint16_t x); + extern inline uint32_t bswap_u32(uint32_t x); diff --git a/src/lib/bit/bit.h b/src/lib/bit/bit.h index b0eaa71c90941fac9673ce60549f2123dbb0d3a3..7ed64c9b570e8e305959dbbe1196ec026191737c 100644 --- a/src/lib/bit/bit.h +++ b/src/lib/bit/bit.h @@ -317,6 +317,20 @@ bit_rotr_u64(uint64_t x, int r) return ((x >> r) | (x << (64 - r))); } +/** + * @copydoc bswap_u32 + */ +inline uint16_t +bswap_u16(uint16_t x) +{ +#if defined(HAVE_BUILTIN_BSWAP16) + return __builtin_bswap16(x); +#else /* !defined(HAVE_BUILTIN_BSWAP16) */ + return ((x << 8) & UINT16_C(0xff00)) | + ((x >> 8) & UINT16_C(0x00ff)); +#endif +} + /** * @brief Returns a byte order swapped integer @a x. * This function does not take into account host architecture diff --git a/src/lua/bsdsocket.lua b/src/lua/bsdsocket.lua index 487f867b72c71802fd67a601bd7f2460b8be5070..06be8295d4a2ea84f70e691161991e924f6388b6 100644 --- a/src/lua/bsdsocket.lua +++ b/src/lua/bsdsocket.lua @@ -925,9 +925,29 @@ local function tcp_connect(host, port, timeout) break end - if s:writable(timeout) then - boxerrno(0) - return s + while true do + + if s:writable(timeout) then + if s:peer(self) ~= nil then + boxerrno(0) + return s + + elseif boxerrno() ~= boxerrno.EAGAIN then + break + end + + timeout = timeout - (boxfiber.time() - started) + started = boxfiber.time() + + if timeout <= 0 then + s:close() + break + end + + else + break + end + end end diff --git a/src/lua/uuid.lua b/src/lua/uuid.lua index c1d0b921c0b3226c0dfc72f1d7307bde19dabedf..cc91363c029e322a656c7af45a11c407c3f66681 100644 --- a/src/lua/uuid.lua +++ b/src/lua/uuid.lua @@ -4,34 +4,131 @@ local ffi = require("ffi") local builtin = ffi.C ffi.cdef[[ - /* from <uuid/uuid.h> */ - typedef unsigned char uuid_t[16]; - void uuid_generate(uuid_t out); +struct tt_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +}; - /* from libc */ - int snprintf(char *str, size_t size, const char *format, ...); +void +tt_uuid_create(struct tt_uuid *uu); +int +tt_uuid_from_string(const char *in, struct tt_uuid *uu); +void +tt_uuid_to_string(const struct tt_uuid *uu, char *out); +void +tt_uuid_bswap(struct tt_uuid *uu); +bool +tt_uuid_is_nil(const struct tt_uuid *uu); +bool +tt_uuid_is_equal(const struct tt_uuid *lhs, const struct tt_uuid *rhs); +char * +tt_uuid_str(const struct tt_uuid *uu); +extern const struct tt_uuid uuid_nil; ]] -local uuid_bin = function() - local uuid = ffi.new('uuid_t') - builtin.uuid_generate(uuid) - return ffi.string(uuid, 16) +local uuid_t = ffi.typeof('struct tt_uuid') +local UUID_STR_LEN = 36 +local UUID_LEN = ffi.sizeof(uuid_t) +local uuidbuf = ffi.new(uuid_t) + +local uuid_tostring = function(uu) + return ffi.string(builtin.tt_uuid_str(uu), UUID_STR_LEN) +end + +local uuid_fromstr = function(str) + if type(str) ~= 'string' then + error("fromstr(str)") + end + local uu = ffi.new(uuid_t) + local rc = builtin.tt_uuid_from_string(str, uu) + if rc ~= 0 then + return nil + end + return uu +end + +local need_bswap = function(order) + if order == nil or order == 'l' or order == 'h' or order == 'host' then + return false + elseif order == 'b' or order == 'n' or order == 'network' then + return true + else + error('invalid byteorder, valid is l, b, h, n') + end +end + +local uuid_tobin = function(uu, byteorder) + if need_bswap(byteorder) then + if uu ~= uuidbuf then + ffi.copy(uuidbuf, uu, UUID_LEN) + end + builtin.tt_uuid_bswap(uuidbuf) + return ffi.string(ffi.cast('char *', uuidbuf), UUID_LEN) + end + return ffi.string(ffi.cast('char *', uu), UUID_LEN) +end + +local uuid_frombin = function(bin, byteorder) + if type(bin) ~= 'string' or #bin ~= UUID_LEN then + error("frombin(bin, [byteorder])") + end + local uu = ffi.new(uuid_t) + ffi.copy(uu, bin, UUID_LEN) + if need_bswap(byteorder) then + builtin.tt_uuid_bswap(uu) + end + return uu end -local uuid_hex = function() - local uuid = ffi.new('uuid_t') - builtin.uuid_generate(uuid) - local uuid_hex = ffi.new('char[33]') - for i = 0,ffi.sizeof('uuid_t'),1 do - builtin.snprintf(uuid_hex + i * 2, 3, "%02x", - ffi.cast('unsigned int',uuid[i])) +local uuid_isnil = function(uu) + return builtin.tt_uuid_is_nil(uu) +end + +local uuid_eq = function(lhs, rhs) + if not ffi.istype(uuid_t, rhs) then + return false end - return ffi.string(uuid_hex, 32) + return builtin.tt_uuid_is_equal(lhs, rhs) +end + +local uuid_new = function() + local uu = ffi.new(uuid_t) + builtin.tt_uuid_create(uu) + return uu +end + +local uuid_new_bin = function(byteorder) + builtin.tt_uuid_create(uuidbuf) + return uuid_tobin(uuidbuf, byteorder) end +local uuid_new_str = function() + builtin.tt_uuid_create(uuidbuf) + return uuid_tostring(uuidbuf) +end + +local uuid_mt = { + __tostring = uuid_tostring; + __eq = uuid_eq; + __index = { + isnil = uuid_isnil; + bin = uuid_tobin; -- binary host byteorder + str = uuid_tostring; -- RFC4122 string + } +} + +ffi.metatype(uuid_t, uuid_mt) return setmetatable({ - bin = uuid_bin, - hex = uuid_hex + NULL = builtin.uuid_nil; + new = uuid_new; + fromstr = uuid_fromstr; + frombin = uuid_frombin; + bin = uuid_new_bin; -- optimized shortcut for new():bin() + str = uuid_new_str; -- optimized shortcut for new():str() }, { - __call = uuid_bin + __call = uuid_new; -- shortcut for new() }) diff --git a/src/random.c b/src/random.c index 914e631b3a4e0414e6490dea29624661dbb040e2..2b4e6679a942de2dd7b9662305904635c952658a 100644 --- a/src/random.c +++ b/src/random.c @@ -30,22 +30,68 @@ #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> +#include <sys/time.h> #include <unistd.h> #include <stdlib.h> -#ifdef __linux__ -#define DEV_RANDOM "/dev/urandom" -#else -#define DEV_RANDOM "/dev/random" -#endif +static int rfd; void random_init(void) { - int fd = open(DEV_RANDOM, O_RDONLY); - long int seed; - read(fd, &seed, sizeof(seed)); - close(fd); + int seed; + rfd = open("/dev/urandom", O_RDONLY); + if (rfd == -1) + rfd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (rfd == -1) { + struct timeval tv; + gettimeofday(&tv, 0); + seed = (getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec; + goto srand; + } + + int flags = fcntl(rfd, F_GETFD); + if (flags != -1) + fcntl(rfd, F_SETFD, flags | FD_CLOEXEC); + + read(rfd, &seed, sizeof(seed)); +srand: srandom(seed); srand(seed); } + +void +random_free(void) +{ + if (rfd == -1) + return; + close(rfd); +} + +void +random_bytes(char *buf, size_t size) +{ + size_t generated = 0; + + if (rfd == -1) + goto rand; + + int attempt = 0; + while (generated < size) { + ssize_t n = read(rfd, buf + generated, size - generated); + if (n <= 0) { + if (attempt++ > 5) + break; + continue; + } + generated += n; + attempt = 0; + } +rand: + /* fill remaining bytes with PRNG */ + generated -= generated % sizeof(int); + while (generated < size) { + *(int *)(buf + generated) = rand(); + generated += sizeof(int); + } +} diff --git a/src/random.h b/src/random.h index 98cd0e5e4ec16716882f5ec495d2a0285c04c888..6f9e8597ef026a95717801974192e294807c4d6d 100644 --- a/src/random.h +++ b/src/random.h @@ -28,12 +28,21 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + +#include <stddef.h> + #if defined(__cplusplus) extern "C" { #endif void random_init(void); +void +random_free(void); + +void +random_bytes(char *buf, size_t size); + #if defined(__cplusplus) } #endif /* extern "C" */ diff --git a/src/session.cc b/src/session.cc index 529ceeebcdc4ab2984fb1395daff4625eba744fd..7e3d8e0e30c93bccd0d394ab066567e30cd379fd 100644 --- a/src/session.cc +++ b/src/session.cc @@ -33,6 +33,7 @@ #include "assoc.h" #include "trigger.h" #include "exception.h" +#include "random.h" #include <sys/socket.h> static struct mh_i32ptr_t *session_registry; @@ -67,8 +68,7 @@ session_create(int fd, uint64_t cookie) * to make sure triggers run correctly. */ session_set_user(session, ADMIN, ADMIN); - for (int i = 0; i < SESSION_SEED_SIZE/sizeof(*session->salt); i++) - session->salt[i] = rand(); + random_bytes(session->salt, SESSION_SEED_SIZE); struct mh_i32ptr_node_t node; node.key = session->id; node.val = session; diff --git a/src/session.h b/src/session.h index ab0e620ba1311d7bace4338913799f57479056b4..8f9ef818ea624d9aa01d6757476d5396a4ebfc79 100644 --- a/src/session.h +++ b/src/session.h @@ -53,7 +53,7 @@ struct session { /** Peer cookie - description of the peer. */ uint64_t cookie; /** Authentication salt. */ - int salt[SESSION_SEED_SIZE/sizeof(int)]; + char salt[SESSION_SEED_SIZE]; /** A look up key to quickly find session user. */ uint8_t auth_token; /** User id of the authenticated user. */ diff --git a/src/tarantool.cc b/src/tarantool.cc index fe1dcb54851b53b4c57b3f6e5456c2b0a1cc1bfd..bc9700996019cc263b3e59c061d4690060439d27 100644 --- a/src/tarantool.cc +++ b/src/tarantool.cc @@ -61,6 +61,7 @@ #include "box/box.h" #include "scoped_guard.h" #include "random.h" +#include "tt_uuid.h" #include "iobuf.h" #include <third_party/gopt/gopt.h> #include "cfg.h" @@ -511,6 +512,7 @@ tarantool_free(void) session_free(); fiber_free(); memory_free(); + random_free(); #ifdef ENABLE_GCOV __gcov_flush(); #endif diff --git a/src/trivia/config.h.cmake b/src/trivia/config.h.cmake index a89e0548185d34ab5749ec2e3b3c8b2e542e17bb..e04b7cb4b66c48c495d5fcf65bd6bf607a186895 100644 --- a/src/trivia/config.h.cmake +++ b/src/trivia/config.h.cmake @@ -133,8 +133,7 @@ #cmakedefine HAVE_OPEN_MEMSTREAM 1 #cmakedefine HAVE_FMEMOPEN 1 -#cmakedefine HAVE_LIBUUID_LINUX 1 -#cmakedefine HAVE_LIBUUID_BSD 1 +#cmakedefine HAVE_UUIDGEN 1 /* * predefined /etc directory prefix. diff --git a/src/tt_uuid.c b/src/tt_uuid.c index 590e2d81c1c92736854a94883fa5e1b058fca352..d56eea58f096766219986eeddb59a0aaa6a30ceb 100644 --- a/src/tt_uuid.c +++ b/src/tt_uuid.c @@ -28,11 +28,54 @@ */ #include "tt_uuid.h" #include "msgpuck/msgpuck.h" -#include <stdio.h> +#include <random.h> +#include <trivia/config.h> /* Zeroed by the linker. */ const struct tt_uuid uuid_nil; +#define CT_ASSERT(e) typedef char __ct_assert_##__LINE__[(e) ? 1 : -1] +CT_ASSERT(sizeof(struct tt_uuid) == UUID_LEN); + +#if defined(HAVE_UUIDGEN) +#include <sys/uuid.h> + +CT_ASSERT(sizeof(struct tt_uuid) == sizeof(struct uuid)); + +void +tt_uuid_create(struct tt_uuid *uu) +{ + uuidgen((struct uuid *) uu, 1); /* syscall */ +} +#else + +void +tt_uuid_create(struct tt_uuid *uu) +{ + random_bytes((char *) uu, sizeof(*uu)); + + uu->clock_seq_hi_and_reserved &= 0x3f; + uu->clock_seq_hi_and_reserved |= 0x80; /* variant 1 = RFC4122 */ + uu->time_hi_and_version &= 0x0FFF; + uu->time_hi_and_version |= (4 << 12); /* version 4 = random */ +} +#endif + +extern inline int +tt_uuid_from_string(const char *in, struct tt_uuid *uu); + +extern inline void +tt_uuid_to_string(const struct tt_uuid *uu, char *out); + +extern inline void +tt_uuid_bswap(struct tt_uuid *uu); + +extern inline bool +tt_uuid_is_nil(const struct tt_uuid *uu); + +extern inline bool +tt_uuid_is_equal(const struct tt_uuid *lhs, const struct tt_uuid *rhs); + static __thread char buf[UUID_STR_LEN + 1]; char * diff --git a/src/tt_uuid.h b/src/tt_uuid.h index 34ece80e3487ad84611ffda0157964bc9215ec67..09f519133863153bdc306471c62ff3d26bab5342 100644 --- a/src/tt_uuid.h +++ b/src/tt_uuid.h @@ -28,11 +28,12 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include <trivia/config.h> #include <string.h> +#include <stdint.h> #include <stdbool.h> -#include <stdlib.h> -#include <assert.h> +#include <string.h> +#include <stdio.h> /* snprintf */ +#include <lib/bit/bit.h> #if defined(__cplusplus) extern "C" { @@ -40,108 +41,102 @@ extern "C" { enum { UUID_LEN = 16, UUID_STR_LEN = 36 }; -#if defined(HAVE_LIBUUID_LINUX) - -#include <uuid/uuid.h> - -struct tt_uuid { - uuid_t id; -}; - -static inline void -tt_uuid_create(struct tt_uuid *uu) -{ - uuid_generate(uu->id); -} - -static inline int -tt_uuid_from_string(const char *in, struct tt_uuid *uu) -{ - return uuid_parse((char *) in, uu->id); -} - -static inline void -tt_uuid_to_string(const struct tt_uuid *uu, char *out) -{ - uuid_unparse(uu->id, out); -} - -static inline void -tt_uuid_bin(const struct tt_uuid *uu, void *out) -{ - memcpy(out, uu->id, sizeof(uu->id)); -} - -static inline bool -tt_uuid_is_nil(const struct tt_uuid *uu) -{ - return uuid_is_null(uu->id); -} - -static inline bool -tt_uuid_cmp(const struct tt_uuid *lhs, const struct tt_uuid *rhs) -{ - return uuid_compare(lhs->id, rhs->id); -} - -#elif defined(HAVE_LIBUUID_BSD) - -#include <uuid.h> - +/** + * \brief UUID structure struct + */ struct tt_uuid { - struct uuid id; + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; }; -static inline int -tt_uuid_create(struct tt_uuid *uu) -{ - uint32_t status; - uuid_create(&uu->id, &status); - return status == uuid_s_ok; -} - -static inline int +/** + * \brief Generate new UUID + * \param uu[out] UUID + */ +void +tt_uuid_create(struct tt_uuid *uu); + +/** + * \brief Parse UUID from string. + * \param in string + * \param uu[out] UUID + * \return + */ +inline int tt_uuid_from_string(const char *in, struct tt_uuid *uu) { - uint32_t status; - uuid_from_string(in, &uu->id, &status); - return status == uuid_s_ok; + if (strlen(in) != UUID_STR_LEN || + sscanf(in, "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &uu->time_low, &uu->time_mid, &uu->time_hi_and_version, + &uu->clock_seq_hi_and_reserved, &uu->clock_seq_low, + &uu->node[0], &uu->node[1], &uu->node[2], &uu->node[3], + &uu->node[4], &uu->node[5]) != 11) + return 1; + + /* Check variant (NCS, RFC4122, MSFT) */ + uint8_t n = uu->clock_seq_hi_and_reserved; + if ((n & 0x80) != 0x00 && (n & 0xc0) != 0x80 && (n & 0xe0) != 0xc0) + return 1; + return 0; } -static inline void +/** + * \brief Format UUID to RFC 4122 string. + * \param uu uuid + * \param[out] out buffer, must be at least UUID_STR_LEN + 1 length + */ +inline void tt_uuid_to_string(const struct tt_uuid *uu, char *out) { - uint32_t status; - char *buf = NULL; - uuid_to_string(&uu->id, &buf, &status); - assert(status == uuid_s_ok); - strncpy(out, buf, UUID_STR_LEN); - out[UUID_STR_LEN] = '\0'; - free(buf); + sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uu->time_low, uu->time_mid, uu->time_hi_and_version, + uu->clock_seq_hi_and_reserved, uu->clock_seq_low, uu->node[0], + uu->node[1], uu->node[2], uu->node[3], uu->node[4], uu->node[5]); } -static inline bool -tt_uuid_cmp(const struct tt_uuid *lhs, const struct tt_uuid *rhs) +/** + * \brief Return byte order swapped UUID (LE -> BE and vice versa) + * \param uu + */ +inline void +tt_uuid_bswap(struct tt_uuid *uu) { - uint32_t status; - return uuid_compare(&lhs->id, &rhs->id, &status); + uu->time_low = bswap_u32(uu->time_low); + uu->time_mid = bswap_u16(uu->time_mid); + uu->time_hi_and_version = bswap_u16(uu->time_hi_and_version); } -static inline bool +/** + * \brief Test that uuid is nil + * \param uu UUID + * \retval true if all members of \a uu 0 + * \retval false otherwise + */ +inline bool tt_uuid_is_nil(const struct tt_uuid *uu) { - uint32_t status; - return uuid_is_nil(&uu->id, &status); + const uint64_t *p = (const uint64_t *) uu; + return !p[0] && !p[1]; } -static inline void -tt_uuid_bin(const struct tt_uuid *uu, void *out) +/** + * \brief Test that \a lhs equal \a rhs + * \param lhs UUID + * \param rhs UUID + * \retval true if \a lhs equal \a rhs + * \retval false otherwise + */ +inline bool +tt_uuid_is_equal(const struct tt_uuid *lhs, const struct tt_uuid *rhs) { - uuid_enc_le(out, &uu->id); + const uint64_t *lp = (const uint64_t *) lhs; + const uint64_t *rp = (const uint64_t *) rhs; + return lp[0] == rp[0] && lp[1] == rp[1]; } -#else -#error Unsupported libuuid -#endif /* HAVE_LIBUUID_XXX */ extern const struct tt_uuid uuid_nil; diff --git a/test/box/uuid.result b/test/box/uuid.result index 7e12ebc149d177f59474796aa97619841801f63a..eb5fba13da12238466bf9c828d3d4984bb90b9ea 100644 --- a/test/box/uuid.result +++ b/test/box/uuid.result @@ -2,18 +2,248 @@ uuid = require('uuid') --- ... -string.len(uuid.bin()) +-- +-- RFC4122 compliance +-- +uu = uuid.new() +--- +... +-- new()always generates RFC4122 variant +bit.band(uu.clock_seq_hi_and_reserved, 0xc0) == 0x80 +--- +- true +... +vsn = bit.rshift(uu.time_hi_and_version, 12) +--- +... +-- new() generates time-based or random-based version +vsn == 1 or vsn == 4 +--- +- true +... +-- +-- to/from string +-- +uu = uuid() +--- +... +#uu:str() +--- +- 36 +... +string.match(uu:str(), '^[a-f0-9%-]+$') ~= nil +--- +- true +... +uu == uuid.fromstr(uu:str()) +--- +- true +... +uu = uuid.fromstr('ba90d815-14e0-431d-80c0-ce587885bb78') +--- +... +uu:str() +--- +- ba90d815-14e0-431d-80c0-ce587885bb78 +... +tostring(uu) +--- +- ba90d815-14e0-431d-80c0-ce587885bb78 +... +tostring(uu) == uu:str() +--- +- true +... +uu.time_low; +--- +- 3130054677 +... +uu.time_mid; +--- +- 5344 +... +uu.time_hi_and_version; +--- +- 17181 +... +uu.clock_seq_hi_and_reserved; +--- +- 128 +... +uu.clock_seq_low; +--- +- 192 +... +uu.node[0] +--- +- 206 +... +uu.node[1] +--- +- 88 +... +uu.node[2] +--- +- 120 +... +uu.node[3] +--- +- 133 +... +uu.node[4] +--- +- 187 +... +uu.node[5] +--- +- 120 +... +-- aliases +#uuid.str() +--- +- 36 +... +-- invalid values +uuid.fromstr(nil) +--- +- error: 'builtin/uuid.lua:44: fromstr(str)' +... +uuid.fromstr('') +--- +- null +... +uuid.fromstr('blablabla') +--- +- null +... +uuid.fromstr(string.rep(' ', 36)) +--- +- null +... +uuid.fromstr('ba90d81514e0431d80c0ce587885bb78') +--- +- null +... +uuid.fromstr('ba90d815-14e0-431d-80c0') +--- +- null +... +uuid.fromstr('ba90d815-14e0-431d-80c0-tt587885bb7') +--- +- null +... +-- +-- to/from binary +-- +uu = uuid() +--- +... +#uu:bin() --- - 16 ... -string.len(uuid.hex()) +#uu:bin('h') --- -- 32 +- 16 +... +#uu:bin('l') +--- +- 16 +... +#uu:bin('n') +--- +- 16 +... +#uu:bin('b') +--- +- 16 +... +uu:bin() == uu:bin('h') +--- +- true ... -string.match(uuid.hex(), '^[a-f0-9]+$') ~= nil +uu:bin('n') ~= uu:bin('h') --- - true ... +uu:bin('b') ~= uu:bin('l') +--- +- true +... +uu == uuid.frombin(uu:bin()) +--- +- true +... +uu == uuid.frombin(uu:bin('b'), 'b') +--- +- true +... +uu == uuid.frombin(uu:bin('l'), 'l') +--- +- true +... +uu = uuid.fromstr('adf9d02e-0756-11e4-b5cf-525400123456') +--- +... +uu:bin('l') +--- +- !!binary LtD5rVYH5BG1z1JUABI0Vg== +... +uu:bin('b') +--- +- !!binary rfnQLgdWEeS1z1JUABI0Vg== +... +-- aliases +#uuid.bin() +--- +- 16 +... +#uuid.bin('l') +--- +- 16 +... +-- +-- eq and nil +-- +uu = uuid.new() +--- +... +uuid.NULL +--- +- 00000000-0000-0000-0000-000000000000 +... +uuid.NULL:isnil() +--- +- true +... +uuid.NULL ~= uu +--- +- true +... +uu:isnil() +--- +- false +... +uu == uu +--- +- true +... +uu == uu +--- +- true +... +uu == nil +--- +- false +... +uu == 12345 +--- +- false +... +uu == "blablabla" +--- +- false +... uuid = nil --- ... diff --git a/test/box/uuid.test.lua b/test/box/uuid.test.lua index 244e15c11dff566f780b98d07589ae305fe28372..c1c0a8bd9552270114b0687ea19796e68e2534a9 100644 --- a/test/box/uuid.test.lua +++ b/test/box/uuid.test.lua @@ -1,6 +1,88 @@ -- box.uuid uuid = require('uuid') -string.len(uuid.bin()) -string.len(uuid.hex()) -string.match(uuid.hex(), '^[a-f0-9]+$') ~= nil + +-- +-- RFC4122 compliance +-- +uu = uuid.new() +-- new()always generates RFC4122 variant +bit.band(uu.clock_seq_hi_and_reserved, 0xc0) == 0x80 +vsn = bit.rshift(uu.time_hi_and_version, 12) +-- new() generates time-based or random-based version +vsn == 1 or vsn == 4 + +-- +-- to/from string +-- +uu = uuid() +#uu:str() +string.match(uu:str(), '^[a-f0-9%-]+$') ~= nil +uu == uuid.fromstr(uu:str()) +uu = uuid.fromstr('ba90d815-14e0-431d-80c0-ce587885bb78') +uu:str() +tostring(uu) +tostring(uu) == uu:str() +uu.time_low; +uu.time_mid; +uu.time_hi_and_version; +uu.clock_seq_hi_and_reserved; +uu.clock_seq_low; +uu.node[0] +uu.node[1] +uu.node[2] +uu.node[3] +uu.node[4] +uu.node[5] + +-- aliases +#uuid.str() + +-- invalid values +uuid.fromstr(nil) +uuid.fromstr('') +uuid.fromstr('blablabla') +uuid.fromstr(string.rep(' ', 36)) +uuid.fromstr('ba90d81514e0431d80c0ce587885bb78') +uuid.fromstr('ba90d815-14e0-431d-80c0') +uuid.fromstr('ba90d815-14e0-431d-80c0-tt587885bb7') + +-- +-- to/from binary +-- +uu = uuid() +#uu:bin() +#uu:bin('h') +#uu:bin('l') +#uu:bin('n') +#uu:bin('b') +uu:bin() == uu:bin('h') +uu:bin('n') ~= uu:bin('h') +uu:bin('b') ~= uu:bin('l') +uu == uuid.frombin(uu:bin()) +uu == uuid.frombin(uu:bin('b'), 'b') +uu == uuid.frombin(uu:bin('l'), 'l') + +uu = uuid.fromstr('adf9d02e-0756-11e4-b5cf-525400123456') +uu:bin('l') +uu:bin('b') + +-- aliases +#uuid.bin() +#uuid.bin('l') + +-- +-- eq and nil +-- + +uu = uuid.new() +uuid.NULL +uuid.NULL:isnil() +uuid.NULL ~= uu +uu:isnil() +uu == uu +uu == uu +uu == nil +uu == 12345 +uu == "blablabla" + uuid = nil