diff --git a/.ci/after_success.sh b/.ci/after_success.sh index cb41432..e256689 100755 --- a/.ci/after_success.sh +++ b/.ci/after_success.sh @@ -4,5 +4,5 @@ set -e set -o pipefail if [[ -n "${GCOV}" ]]; then - coveralls -e test -e out/CMakeFiles/CompilerIdC/CMakeCCompilerId.c -e out/CMakeFiles/CompilerIdCXX/CMakeCXXCompilerId.cpp --gcov "$(which "${GCOV}")" --encoding iso-8859-1 -b out || echo 'coveralls upload failed.' + coveralls -e test -e build/CMakeFiles/CompilerIdC/CMakeCCompilerId.c -e build/CMakeFiles/CompilerIdCXX/CMakeCXXCompilerId.cpp --gcov "$(which "${GCOV}")" --encoding iso-8859-1 -b build || echo 'coveralls upload failed.' fi diff --git a/.ci/before_install.sh b/.ci/before_install.sh new file mode 100755 index 0000000..1bdd89d --- /dev/null +++ b/.ci/before_install.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +if [[ -n "${CI_TARGET}" ]]; then + exit +fi + +if [[ "${TRAVIS_OS_NAME}" == osx ]]; then + brew update +fi + +echo "Upgrade Python 2's pip." +pip2.7 -q install --user --upgrade pip + +if [[ "${TRAVIS_OS_NAME}" == osx ]]; then + echo "Install Python 3." + brew install python3 + echo "Upgrade Python 3's pip." + pip3 -q install --user --upgrade pip +else + # TODO: Replace with upgrade when Travis gets python3-pip package. + echo "Install pip for Python 3." + curl -sSL https://bootstrap.pypa.io/get-pip.py -o "${HOME}/get-pip.py" + # After this, pip in PATH will refer to Python 3's pip. + python3.3 "${HOME}/get-pip.py" --user --upgrade +fi diff --git a/.ci/install.sh b/.ci/install.sh new file mode 100755 index 0000000..7e1cb0a --- /dev/null +++ b/.ci/install.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +if [[ -n "${CI_TARGET}" ]]; then + exit +fi + +if [[ "${TRAVIS_OS_NAME}" == osx ]]; then + brew install gettext + brew reinstall -s libtool +fi + +# Use default CC to avoid compilation problems when installing Python modules. +echo "Install coveralls for Python 2." +CC=cc pip2.7 -q install --user --upgrade cpp-coveralls diff --git a/.travis.yml b/.travis.yml index beec4b0..f4cc41a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ matrix: env: EXTRA_FLAGS="-DCLANG_ADDRESS_SANITIZER=ON" - os: linux compiler: gcc-5 - env: GCOV=gcov-5 EXTRA_FLAGS="-DUSE_COVERAGE=ON" + env: GCOV=gcov-5 CMAKE_FLAGS="-DUSE_COVERAGE=ON" before_install: # cmocka @@ -17,16 +17,9 @@ before_install: - cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. && make -j2 && sudo make install - cd ../.. - rm -rf cmocka-1.0.1 - # libuv - - curl -L https://github.com/libuv/libuv/archive/v1.8.0.tar.gz | tar xzf - - - (cd libuv-1.8.0 && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install) - - rm -rf libuv-1.8.0 - # msgpack-c - - curl -L https://github.com/msgpack/msgpack-c/archive/cpp-1.4.1.tar.gz | tar xzf - - - (cd msgpack-c-cpp-1.4.1 && ./bootstrap && ./configure --prefix=/usr && make && sudo make install) - - rm -rf msgpack-c-cpp-1.4.1 - # coveralls - - pip install --user cpp-coveralls + - .ci/before_install.sh + +install: .ci/install.sh addons: apt: @@ -49,9 +42,10 @@ addons: - llvm-3.4-dev - ninja-build - pkg-config + - python3.3-dev - libbsd-dev - libhiredis-dev before_script: redis-server redis-test.conf -script: make sb && make test && make sb-makekey && ./out/bin/sb-makekey && ./out/bin/sb-test +script: make sb && make test && make sb-makekey && ./build/bin/sb-makekey && ./build/bin/sb-test after_success: .ci/after_success.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 75973e6..a0c15b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,16 @@ option(CLANG_MEMORY_SANITIZER "Enable clang memory sanitizer." OFF) option(CLANG_THREAD_SANITIZER "Enable clang thread sanitizer." OFF) option(CLANG_ANALYZER "Enable clang static analyzer." OFF) +# Prefer our bundled versions of dependencies. +set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/usr" CACHE PATH "Path prefix for finding dependencies") +if(CMAKE_CROSSCOMPILING AND NOT UNIX) + list(INSERT CMAKE_FIND_ROOT_PATH 0 ${DEPS_PREFIX}) + list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}/../host/bin) +else() + list(INSERT CMAKE_PREFIX_PATH 0 ${DEPS_PREFIX}) + set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${DEPS_PREFIX}/lib/pkgconfig") +endif() + list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") if(NOT CMAKE_BUILD_TYPE) @@ -97,8 +107,10 @@ file(GLOB MANPAGES # splonebox target sources set(SPLONEBOX-SOURCES src/main.c + src/main.h src/sb-common.h src/khash.h + src/klist.h src/kvec.h src/queue.h src/string.c @@ -123,13 +135,21 @@ set(SPLONEBOX-SOURCES src/address.c src/address.h src/sbmemzero.c + src/mem.c + src/mem.h src/api/sb-api.h src/api/register.c src/api/result.c src/api/run.c + src/api/broadcast.c + src/api/subscribe.c + src/api/unsubscribe.c + src/api/helpers.c + src/api/helpers.h src/rpc/sb-rpc.h src/rpc/connection/event.c src/rpc/connection/event.h + src/rpc/connection/event-defs.h src/rpc/connection/streamhandle.c src/rpc/connection/streamhandle.h src/rpc/connection/inputstream.c @@ -144,10 +164,9 @@ set(SPLONEBOX-SOURCES src/rpc/connection/crypto.c src/rpc/connection/crypto.h src/rpc/connection/loop.c - src/rpc/msgpack/sb-msgpack-rpc.h - src/rpc/msgpack/message.c - src/rpc/msgpack/pack.c - src/rpc/msgpack/unpack.c + src/rpc/connection/loop.h + src/rpc/msgpack/helpers.c + src/rpc/msgpack/helpers.h src/rpc/db/sb-db.h src/rpc/db/connect.c src/rpc/db/plugin.c @@ -159,6 +178,8 @@ set(SPLONEBOX-SOURCES set(SB-PLUGINKEY-SOURCES src/sb-pluginkey.c src/sb-common.h + src/mem.c + src/mem.h src/rpc/sb-rpc.h src/util.c src/confparse.c @@ -170,6 +191,8 @@ set(SB-PLUGINKEY-SOURCES # sb-makekey target sources set(SB-MAKEKEY-SOURCES src/sb-common.h + src/mem.c + src/mem.h src/reallocarray.c src/tweetnacl.c src/tweetnacl.h @@ -180,8 +203,10 @@ set(SB-MAKEKEY-SOURCES # splonebox test(s) sources set(TEST-SOURCES + src/main.h src/sb-common.h src/khash.h + src/klist.h src/queue.h src/string.c src/reallocarray.c @@ -202,6 +227,8 @@ set(TEST-SOURCES src/parse.h src/util.c src/util.h + src/mem.c + src/mem.h src/address.c src/address.h src/sbmemzero.c @@ -209,9 +236,15 @@ set(TEST-SOURCES src/api/register.c src/api/run.c src/api/result.c + src/api/broadcast.c + src/api/subscribe.c + src/api/unsubscribe.c + src/api/helpers.c + src/api/helpers.h src/rpc/sb-rpc.h src/rpc/connection/event.c src/rpc/connection/event.h + src/rpc/connection/event-defs.h src/rpc/connection/streamhandle.c src/rpc/connection/streamhandle.h src/rpc/connection/inputstream.c @@ -226,10 +259,9 @@ set(TEST-SOURCES src/rpc/connection/crypto.c src/rpc/connection/crypto.h src/rpc/connection/loop.c - src/rpc/msgpack/sb-msgpack-rpc.h - src/rpc/msgpack/message.c - src/rpc/msgpack/pack.c - src/rpc/msgpack/unpack.c + src/rpc/connection/loop.h + src/rpc/msgpack/helpers.c + src/rpc/msgpack/helpers.h src/rpc/db/sb-db.h src/rpc/db/connect.c src/rpc/db/plugin.c @@ -245,28 +277,7 @@ set(TEST-SOURCES test/helper-validate.h test/unit/server-start.c test/unit/server-stop.c - test/unit/pack-string.c - test/unit/pack-int.c - test/unit/pack-uint.c - test/unit/pack-array.c - test/unit/pack-nil.c - test/unit/pack-float.c - test/unit/pack-bool.c - test/unit/regression-issue-60.c - test/unit/unpack-string.c - test/unit/unpack-uint.c - test/unit/unpack-array.c test/unit/dispatch-table-get.c - test/unit/event-queue-put.c - test/unit/event-queue-get.c - test/unit/message-deserialize-request.c - test/unit/message-deserialize-response.c - test/unit/message-deserialize-error-response.c - test/unit/message-serialize-request.c - test/unit/message-serialize-response.c - test/unit/message-serialize-error-response.c - test/unit/message-is-request.c - test/unit/message-is-response.c test/functional/db-connect.c test/functional/db-plugin-add.c test/functional/db-pluginkey-verify.c @@ -278,9 +289,12 @@ set(TEST-SOURCES test/functional/dispatch-handle-register.c test/functional/dispatch-handle-run.c test/functional/dispatch-handle-result.c + test/functional/dispatch-handle-subscribe.c + test/functional/dispatch-handle-broadcast.c test/functional/crypto.c test/functional/confparse.c test/functional/db-whitelist.c + test/functional/msgpack-rpc-helper.c ) if(CLANG_ADDRESS_SANITIZER OR CLANG_MEMORY_SANITIZER OR CLANG_TSAN) @@ -289,7 +303,11 @@ endif() include_directories(${PROJECT_SOURCE_DIR}/src) include_directories(${PROJECT_SOURCE_DIR}/test) -include_directories(${BSD_INCLUDE_DIRS} ${LIBUV_INCLUDE_DIRS} ${MSGPACK_INCLUDE_DIRS} ${HIREDIS_INCLUDE_DIRS} ${CMOCKA_INCLUDE_DIRS}) +include_directories(SYSTEM ${BSD_INCLUDE_DIRS}) +include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) +include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS}) +include_directories(SYSTEM ${HIREDIS_INCLUDE_DIRS}) +include_directories(SYSTEM ${CMOCKA_INCLUDE_DIRS}) # sb target add_executable(sb ${SPLONEBOX-SOURCES}) @@ -311,7 +329,7 @@ add_executable(sb-test ${TEST-SOURCES}) add_executable(sb-pluginkey ${SB-PLUGINKEY-SOURCES}) # wrap some functions for testing -set_property(TARGET sb-test APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--wrap=outputstream_write,--wrap=loop_wait_for_response,--wrap=crypto_write ") +set_property(TARGET sb-test APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--wrap=outputstream_write,--wrap=loop_process_events_until,--wrap=crypto_write ") set_property(TARGET sb-test APPEND_STRING PROPERTY COMPILE_FLAGS "-DBOX_UNIT_TESTS ") target_link_libraries(sb-test diff --git a/Makefile b/Makefile index a19af4e..0f8ab3e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ BUILD_DIR := out CMAKE_BUILD_TYPE ?= Debug -FLAGS := -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) +CMAKE_FLAGS := -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) EXTRA_FLAGS ?= BUILD_TYPE ?= $(shell (type ninja > /dev/null 2>&1 && echo "Ninja") || echo "Unix Makefiles") @@ -26,35 +26,59 @@ endif BUILD_CMD = $(BUILD_TOOL) $(VERBOSE_FLAG) +# Extra CMake flags which extend the default set +CMAKE_EXTRA_FLAGS ?= +USE_BUNDLED_DEPS ?= +DEPS_CMAKE_FLAGS ?= + +ifneq (,$(USE_BUNDLED_DEPS)) + BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED_DEPS) +endif + all: sb test sb-makekey sb-pluginkey -sb: - test -d $(BUILD_DIR) || mkdir $(BUILD_DIR) - cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) .. - $(BUILD_CMD) -C out sb +sb: build/.ran-cmake deps + +$(BUILD_CMD) -C build sb + +cmake: + touch CMakeLists.txt + $(MAKE) build/.ran-cmake + +build/.ran-cmake: | deps + cd build && cmake -G '$(BUILD_TYPE)' $(CMAKE_FLAGS) $(CMAKE_EXTRA_FLAGS) .. + touch $@ + +deps: | build/.ran-third-party-cmake +ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),) + +$(BUILD_CMD) -C .deps +endif + +build/.ran-third-party-cmake: +ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),) + mkdir -p .deps + cd .deps && \ + cmake -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) $(BUNDLED_LUA_CMAKE_FLAG) \ + $(DEPS_CMAKE_FLAGS) ../third-party +endif + mkdir -p build + touch $@ -test: - test -d $(BUILD_DIR) || mkdir $(BUILD_DIR) - cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) .. - $(BUILD_CMD) -C out sb-test +test: build/.ran-cmake deps + +$(BUILD_CMD) -C build sb-test -sb-makekey: - test -d $(BUILD_DIR) || mkdir $(BUILD_DIR) - cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) .. - $(BUILD_CMD) -C out sb-makekey +sb-makekey: build/.ran-cmake deps + +$(BUILD_CMD) -C build sb-makekey -sb-pluginkey: - test -d $(BUILD_DIR) || mkdir $(BUILD_DIR) - cd out && cmake -G '$(BUILD_TYPE)' $(FLAGS) $(EXTRA_FLAGS) .. - $(BUILD_CMD) -C out sb-pluginkey +sb-pluginkey: build/.ran-cmake deps + +$(BUILD_CMD) -C build sb-pluginkey clean: - +test -d out && $(BUILD_CMD) -C out clean || true + +test -d build && $(BUILD_CMD) -C build clean || true distclean: clean - rm -rf out + rm -rf .deps build install: | sb +$(BUILD_CMD) -C out install -.PHONY: test clean distclean sb install sb-makekey +.PHONY: test clean distclean sb install sb-makekey deps cmake diff --git a/cmake/FindLIBUV.cmake b/cmake/FindLIBUV.cmake index 5affbc2..dcdd5e4 100644 --- a/cmake/FindLIBUV.cmake +++ b/cmake/FindLIBUV.cmake @@ -1,17 +1,108 @@ -find_package(PkgConfig) -if (PKG_CONFIG_FOUND) - pkg_check_modules(SHARED_LIBUV REQUIRED libuv) +# - Try to find libuv +# Once done, this will define +# +# LIBUV_FOUND - system has libuv +# LIBUV_INCLUDE_DIRS - the libuv include directories +# LIBUV_LIBRARIES - link these to use libuv +# +# Set the LIBUV_USE_STATIC variable to specify if static libraries should +# be preferred to shared ones. + +if(NOT LIBUV_USE_BUNDLED) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_LIBUV QUIET libuv) + endif() +else() + set(PC_LIBUV_INCLUDEDIR) + set(PC_LIBUV_INCLUDE_DIRS) + set(PC_LIBUV_LIBDIR) + set(PC_LIBUV_LIBRARY_DIRS) + set(LIMIT_SEARCH NO_DEFAULT_PATH) endif() find_path(LIBUV_INCLUDE_DIR uv.h - HINTS ${SHARED_LIBUV_INCLUDEDIR} ${SHARED_LIBUV_INCLUDE_DIRS} + HINTS ${PC_LIBUV_INCLUDEDIR} ${PC_LIBUV_INCLUDE_DIRS} ${LIMIT_SEARCH}) -find_library(LIBUV_LIBRARY NAMES uv - HINTS ${SHARED_LIBUV_LIBDIR} ${SHARED_LIBUV_LIBRARY_DIRS} +# If we're asked to use static linkage, add libuv.a as a preferred library name. +if(LIBUV_USE_STATIC) + list(APPEND LIBUV_NAMES + "${CMAKE_STATIC_LIBRARY_PREFIX}uv${CMAKE_STATIC_LIBRARY_SUFFIX}") +endif(LIBUV_USE_STATIC) + +if(MSVC) + list(APPEND LIBUV_NAMES libuv) +else() + list(APPEND LIBUV_NAMES uv) +endif() + +find_library(LIBUV_LIBRARY NAMES ${LIBUV_NAMES} + HINTS ${PC_LIBUV_LIBDIR} ${PC_LIBUV_LIBRARY_DIRS} ${LIMIT_SEARCH}) mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) -set(LIBUV_LIBRARIES ${LIBUV_LIBRARY} ${SHARED_LIBUV_LIBRARIES}) +if(PC_LIBUV_LIBRARIES) + list(REMOVE_ITEM PC_LIBUV_LIBRARIES uv) +endif() + +set(LIBUV_LIBRARIES ${LIBUV_LIBRARY} ${PC_LIBUV_LIBRARIES}) set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR}) + +# Deal with the fact that libuv.pc is missing important dependency information. + +include(CheckLibraryExists) + +check_library_exists(dl dlopen "dlfcn.h" HAVE_LIBDL) +if(HAVE_LIBDL) + list(APPEND LIBUV_LIBRARIES dl) +endif() + +check_library_exists(kstat kstat_lookup "kstat.h" HAVE_LIBKSTAT) +if(HAVE_LIBKSTAT) + list(APPEND LIBUV_LIBRARIES kstat) +endif() + +check_library_exists(kvm kvm_open "kvm.h" HAVE_LIBKVM) +if(HAVE_LIBKVM) + list(APPEND LIBUV_LIBRARIES kvm) +endif() + +check_library_exists(nsl gethostbyname "nsl.h" HAVE_LIBNSL) +if(HAVE_LIBNSL) + list(APPEND LIBUV_LIBRARIES nsl) +endif() + +check_library_exists(perfstat perfstat_cpu "libperfstat.h" HAVE_LIBPERFSTAT) +if(HAVE_LIBPERFSTAT) + list(APPEND LIBUV_LIBRARIES perfstat) +endif() + +check_library_exists(rt clock_gettime "time.h" HAVE_LIBRT) +if(HAVE_LIBRT) + list(APPEND LIBUV_LIBRARIES rt) +endif() + +check_library_exists(sendfile sendfile "" HAVE_LIBSENDFILE) +if(HAVE_LIBSENDFILE) + list(APPEND LIBUV_LIBRARIES sendfile) +endif() + +if(WIN32) + # check_library_exists() does not work for Win32 API calls in X86 due to name + # mangling calling conventions + list(APPEND LIBUV_LIBRARIES iphlpapi) + list(APPEND LIBUV_LIBRARIES psapi) + list(APPEND LIBUV_LIBRARIES userenv) + list(APPEND LIBUV_LIBRARIES ws2_32) +endif() + +include(FindPackageHandleStandardArgs) + +# handle the QUIETLY and REQUIRED arguments and set LIBUV_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(LibUV DEFAULT_MSG + LIBUV_LIBRARY LIBUV_INCLUDE_DIR) + +mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) diff --git a/cmake/FindMSGPACK.cmake b/cmake/FindMSGPACK.cmake index ac4006e..1774f59 100644 --- a/cmake/FindMSGPACK.cmake +++ b/cmake/FindMSGPACK.cmake @@ -1,21 +1,63 @@ -find_package(PkgConfig) -if (PKG_CONFIG_FOUND) - pkg_search_module(SHARED_MSGPACK QUIET +if(NOT MSGPACK_USE_BUNDLED) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_search_module(PC_MSGPACK QUIET msgpackc>=${Msgpack_FIND_VERSION} msgpack>=${Msgpack_FIND_VERSION}) + endif() +else() + set(PC_MSGPACK_INCLUDEDIR) + set(PC_MSGPACK_INCLUDE_DIRS) + set(PC_MSGPACK_LIBDIR) + set(PC_MSGPACK_LIBRARY_DIRS) + set(LIMIT_SEARCH NO_DEFAULT_PATH) endif() -find_path(MSGPACK_INCLUDE_DIR msgpack.h - HINTS ${SHARED_MSGPACK_INCLUDEDIR} ${SHARED_MSGPACK_INCLUDE_DIRS} +set(MSGPACK_DEFINITIONS ${PC_MSGPACK_CFLAGS_OTHER}) + +find_path(MSGPACK_INCLUDE_DIR msgpack/version_master.h + HINTS ${PC_MSGPACK_INCLUDEDIR} ${PC_MSGPACK_INCLUDE_DIRS} ${LIMIT_SEARCH}) - -list(APPEND MSGPACK_NAMES msgpackc msgpack) + +if(MSGPACK_INCLUDE_DIR) + file(READ ${MSGPACK_INCLUDE_DIR}/msgpack/version_master.h msgpack_version_h) + string(REGEX REPLACE ".*MSGPACK_VERSION_MAJOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MAJOR "${msgpack_version_h}") + string(REGEX REPLACE ".*MSGPACK_VERSION_MINOR +([0-9]+).*" "\\1" MSGPACK_VERSION_MINOR "${msgpack_version_h}") + string(REGEX REPLACE ".*MSGPACK_VERSION_REVISION +([0-9]+).*" "\\1" MSGPACK_VERSION_REVISION "${msgpack_version_h}") + set(MSGPACK_VERSION_STRING "${MSGPACK_VERSION_MAJOR}.${MSGPACK_VERSION_MINOR}.${MSGPACK_VERSION_REVISION}") +else() + set(MSGPACK_VERSION_STRING) +endif() + +# If we're asked to use static linkage, add libmsgpack{,c}.a as a preferred library name. +if(MSGPACK_USE_STATIC) + list(APPEND MSGPACK_NAMES + "${CMAKE_STATIC_LIBRARY_PREFIX}msgpackc${CMAKE_STATIC_LIBRARY_SUFFIX}" + "${CMAKE_STATIC_LIBRARY_PREFIX}msgpack${CMAKE_STATIC_LIBRARY_SUFFIX}") +endif() + +if(MSVC) + # The import library for the msgpack DLL has a different name + list(APPEND MSGPACK_NAMES msgpack_import) +else() + list(APPEND MSGPACK_NAMES msgpackc msgpack) +endif() find_library(MSGPACK_LIBRARY NAMES ${MSGPACK_NAMES} - HINTS ${SHARED_MSGPACK_LIBDIR} ${SHARED_MSGPACK_LIBRARY_DIRS} + # Check each directory for all names to avoid using headers/libraries from + # different places. + NAMES_PER_DIR + HINTS ${PC_MSGPACK_LIBDIR} ${PC_MSGPACK_LIBRARY_DIRS} ${LIMIT_SEARCH}) mark_as_advanced(MSGPACK_INCLUDE_DIR MSGPACK_LIBRARY) set(MSGPACK_LIBRARIES ${MSGPACK_LIBRARY}) set(MSGPACK_INCLUDE_DIRS ${MSGPACK_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set MSGPACK_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(Msgpack + REQUIRED_VARS MSGPACK_LIBRARY MSGPACK_INCLUDE_DIR + VERSION_VAR MSGPACK_VERSION_STRING) diff --git a/test/unit/pack-bool.c b/src/api/broadcast.c similarity index 59% rename from test/unit/pack-bool.c rename to src/api/broadcast.c index 4b31bca..22cf640 100644 --- a/test/unit/pack-bool.c +++ b/src/api/broadcast.c @@ -14,24 +14,25 @@ * along with this program. If not, see . */ -#include +#include +#include -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" +#include "rpc/db/sb-db.h" +#include "api/sb-api.h" +#include "api/helpers.h" -void unit_pack_bool(UNUSED(void **state)) +int api_broadcast(string event, array args, struct api_error *api_error) { - msgpack_sbuffer sbuf; - msgpack_packer pk; + object o = copy_object(ARRAY_OBJ(args)); - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + sbassert(api_error); - assert_int_equal(0, pack_bool(&pk, true)); - assert_int_equal(0, pack_bool(&pk, false)); - assert_int_not_equal(0, pack_bool(NULL, true)); + if (!connection_send_event(0, event.str, o.data.array)) { + error_set(api_error, API_ERROR_TYPE_VALIDATION, + "Broadcasting event failed"); + return -1; + } - msgpack_sbuffer_destroy(&sbuf); + return 0; } diff --git a/src/api/helpers.c b/src/api/helpers.c new file mode 100644 index 0000000..96d75ab --- /dev/null +++ b/src/api/helpers.c @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2015 splone UG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * This file incorporates code covered by the following terms: + * + * Copyright Neovim contributors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + #include + #include + + #include "api/sb-api.h" + #include "api/helpers.h" + #include "sb-common.h" + +void api_free_string(string value) +{ + if (!value.str) { + return; + } + + FREE(value.str); +} + +void api_free_object(object value) +{ + switch (value.type) { + case OBJECT_TYPE_NIL: + case OBJECT_TYPE_BOOL: + case OBJECT_TYPE_INT: + case OBJECT_TYPE_UINT: + case OBJECT_TYPE_FLOAT: + break; + + case OBJECT_TYPE_STR: + api_free_string(value.data.string); + break; + + case OBJECT_TYPE_ARRAY: + api_free_array(value.data.array); + break; + + case OBJECT_TYPE_DICTIONARY: + api_free_dictionary(value.data.dictionary); + break; + + default: + abort(); + } +} + +void api_free_array(array value) +{ + for (size_t i = 0; i < value.size; i++) { + api_free_object(value.items[i]); + } + + FREE(value.items); +} + +void api_free_dictionary(dictionary value) +{ + for (size_t i = 0; i < value.size; i++) { + api_free_string(value.items[i].key); + api_free_object(value.items[i].value); + } + + FREE(value.items); +} + +object copy_object(object obj) +{ + switch (obj.type) { + case OBJECT_TYPE_NIL: + case OBJECT_TYPE_BOOL: + case OBJECT_TYPE_INT: + case OBJECT_TYPE_UINT: + case OBJECT_TYPE_FLOAT: + return obj; + + case OBJECT_TYPE_STR: + return STRING_OBJ(cstring_copy_string(obj.data.string.str)); + + case OBJECT_TYPE_ARRAY: { + array rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < obj.data.array.size; i++) { + ADD(rv, copy_object(obj.data.array.items[i])); + } + return ARRAY_OBJ(rv); + } + + case OBJECT_TYPE_DICTIONARY: { + dictionary rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < obj.data.dictionary.size; i++) { + key_value_pair item = obj.data.dictionary.items[i]; + PUT(rv, item.key.str, copy_object(item.value)); + } + return DICTIONARY_OBJ(rv); + } + default: + abort(); + } +} diff --git a/src/api/helpers.h b/src/api/helpers.h new file mode 100644 index 0000000..e312a3b --- /dev/null +++ b/src/api/helpers.h @@ -0,0 +1,45 @@ +#pragma once + +#include "sb-common.h" + +#define OBJECT_INIT { .type = OBJECT_TYPE_NIL } + +#define OBJECT_OBJ(o) o + +#define BOOLEAN_OBJ(b) ((object) { \ + .type = OBJECT_TYPE_BOOL, \ + .data.boolean = b }) + +#define INTEGER_OBJ(i) ((object) { \ + .type = OBJECT_TYPE_INT, \ + .data.integer = i }) + +#define UINTEGER_OBJ(i) ((object) { \ + .type = OBJECT_TYPE_UINT, \ + .data.uinteger = i }) + +#define FLOATING_OBJ(f) ((object) { \ + .type = OBJECT_TYPE_FLOAT, \ + .data.floating = f }) + +#define STRING_OBJ(s) ((object) { \ + .type = OBJECT_TYPE_STR, \ + .data.string = s }) + +#define ARRAY_OBJ(a) ((object) { \ + .type = OBJECT_TYPE_ARRAY, \ + .data.array = a }) + +#define DICTIONARY_OBJ(d) ((object) { \ + .type = OBJECT_TYPE_DICTIONARY, \ + .data.dictionary = d }) + +#define NIL ((object) {.type = OBJECT_TYPE_NIL}) + +#define PUT(dict, k, v) \ + kv_push(dict, ((key_value_pair) { .key = cstring_copy_string(k), .value = v })) + +#define ADD(array, item) \ + kv_push(array, item) + +#define STATIC_CSTR_AS_STRING(s) ((string) {.str = s, .length = sizeof(s) - 1}) diff --git a/src/api/register.c b/src/api/register.c index ae2e91a..86613b1 100644 --- a/src/api/register.c +++ b/src/api/register.c @@ -19,13 +19,15 @@ #include "rpc/db/sb-db.h" #include "api/sb-api.h" +#include "api/helpers.h" -int api_register(string name, string desc, - string author, string license, array functions, uint64_t con_id, - uint32_t msgid, char *pluginkey, struct api_error *api_error) +int api_register(string name, string desc, string author, string license, + array functions, char *pluginkey, struct api_error *api_error) { - struct message_object *func; - array params = ARRAY_INIT; + object *func; + + sbassert(pluginkey); + sbassert(api_error); if (functions.size == 0) { error_set(api_error, API_ERROR_TYPE_VALIDATION, @@ -40,7 +42,7 @@ int api_register(string name, string desc, } for (size_t i = 0; i < functions.size; i++) { - func = &functions.obj[i]; + func = &functions.items[i]; if (func->type != OBJECT_TYPE_ARRAY) { error_set(api_error, API_ERROR_TYPE_VALIDATION, @@ -48,7 +50,7 @@ int api_register(string name, string desc, continue; } - if (db_function_add(pluginkey, &func->data.params) == -1) { + if (db_function_add(pluginkey, &func->data.array) == -1) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "Failed to register function in database."); continue; @@ -58,9 +60,5 @@ int api_register(string name, string desc, if (api_error->isset) return (-1); - if (connection_send_response(con_id, msgid, params, api_error) < 0) { - return (-1); - }; - return (0); } diff --git a/src/api/result.c b/src/api/result.c index 965e583..cd52dc2 100644 --- a/src/api/result.c +++ b/src/api/result.c @@ -18,83 +18,48 @@ #include #include "api/sb-api.h" +#include "api/helpers.h" #include "sb-common.h" -int api_result(char *targetpluginkey, uint64_t callid, - struct message_object args, uint64_t con_id, uint32_t msgid, +int api_result(char *targetpluginkey, uint64_t callid, array args, struct api_error *api_error) { - struct message_object *data; - struct message_object *meta; - array result_params; - array result_response_params; string result; - struct callinfo cinfo; + object res; - if (!api_error) - return (-1); - - result_params.size = 2; - result_params.obj = CALLOC(2, struct message_object); - - if (!result_params.obj) - return (-1); + sbassert(targetpluginkey); + sbassert(api_error); - /* data refs to first result_params parameter */ - meta = &result_params.obj[0]; + array meta = ARRAY_DICT_INIT; + ADD(meta, UINTEGER_OBJ(callid)); - meta->type = OBJECT_TYPE_ARRAY; - - /* meta = [nil, callid] */ - meta->data.params.size = 1; - meta->data.params.obj = CALLOC(1, struct message_object); - - if (!meta->data.params.obj) - return (-1); - - /* add callid */ - data = &meta->data.params.obj[0]; - data->type = OBJECT_TYPE_UINT; - data->data.uinteger = callid; - - /* add function parameters, data refs to third result_params parameter */ - data = &result_params.obj[1]; - - data->type = OBJECT_TYPE_ARRAY; - data->data.params = message_object_copy(args).data.params; + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, copy_object(ARRAY_OBJ(args))); /* send request */ result = (string) {.str = "result", .length = sizeof("result") - 1}; - cinfo = connection_send_request(targetpluginkey, result, result_params, + res = connection_send_request(targetpluginkey, result, request, api_error); if (api_error->isset) return (-1); - if (cinfo.response.params.size != 1) { + if (res.data.array.size != 1) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error dispatching result API response. Either response is broken " "or it just has wrong params size."); return (-1); } - if (!(cinfo.response.params.obj[0].type == OBJECT_TYPE_UINT && - callid == cinfo.response.params.obj[0].data.uinteger)) { + if (!(res.data.array.items[0].type == OBJECT_TYPE_UINT && + callid == res.data.array.items[0].data.uinteger)) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API response. Invalid callid"); return (-1); } - result_response_params.size = 1; - result_response_params.obj = CALLOC(1, struct message_object); - result_response_params.obj[0].type = OBJECT_TYPE_UINT; - result_response_params.obj[0].data.uinteger = callid; - - if (connection_send_response(con_id, msgid, result_response_params, api_error) < 0) { - return (-1); - }; - - free_params(cinfo.response.params); + api_free_array(res.data.array); return (0); } diff --git a/src/api/run.c b/src/api/run.c index 9f9b6d9..a3e7884 100644 --- a/src/api/run.c +++ b/src/api/run.c @@ -19,102 +19,60 @@ #include "rpc/db/sb-db.h" #include "api/sb-api.h" +#include "api/helpers.h" + int api_run(char *targetpluginkey, string function_name, uint64_t callid, - struct message_object args, uint64_t con_id, - uint32_t msgid, struct api_error *api_error) + array args, struct api_error *api_error) { - struct message_object *data; - struct message_object *meta; - array run_params; - array run_response_params; string run; - struct callinfo cinfo; + object result; + array meta = ARRAY_DICT_INIT; + array request = ARRAY_DICT_INIT; - if (!api_error) - return (-1); + sbassert(api_error); if (db_plugin_verify(targetpluginkey) == -1) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "API key is invalid."); return (-1); } - if (db_function_verify(targetpluginkey, function_name, &args.data.params) == -1) { + if (db_function_verify(targetpluginkey, function_name, &args) == -1) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "run() verification failed."); return (-1); } - run_params.size = 3; - run_params.obj = CALLOC(3, struct message_object); - - if (!run_params.obj) - return (-1); - - /* data refs to first run_params parameter */ - meta = &run_params.obj[0]; - - meta->type = OBJECT_TYPE_ARRAY; - - /* meta = [nil, callid] */ - meta->data.params.size = 2; - meta->data.params.obj = CALLOC(2, struct message_object); - - if (!meta->data.params.obj) - return (-1); - - /* add nil */ - data = &meta->data.params.obj[0]; - data->type = OBJECT_TYPE_NIL; - - /* add callid */ - data = &meta->data.params.obj[1]; - data->type = OBJECT_TYPE_UINT; - data->data.uinteger = callid; - - /* add function name, data refs to second run_params parameter */ - data = &run_params.obj[1]; - data->type = OBJECT_TYPE_STR; - data->data.string = cstring_copy_string(function_name.str); + ADD(meta, OBJECT_OBJ((object) OBJECT_INIT)); + ADD(meta, UINTEGER_OBJ(callid)); - /* add function parameters, data refs to third run_params parameter */ - data = &run_params.obj[2]; - - data->type = OBJECT_TYPE_ARRAY; - data->data.params = message_object_copy(args).data.params; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, STRING_OBJ(cstring_copy_string(function_name.str))); + ADD(request, copy_object(ARRAY_OBJ(args))); /* send request */ run = (string) {.str = "run", .length = sizeof("run") - 1}; - cinfo = connection_send_request(targetpluginkey, run, run_params, + result = connection_send_request(targetpluginkey, run, request, api_error); if (api_error->isset) return (-1); - if (cinfo.response.params.size != 1) { + if (result.data.array.size != 1) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API response. Either response is broken " "or it just has wrong params size."); return (-1); } - if (!(cinfo.response.params.obj[0].type == OBJECT_TYPE_UINT && - callid == cinfo.response.params.obj[0].data.uinteger)) { + if (!(result.data.array.items[0].type == OBJECT_TYPE_UINT && + callid == result.data.array.items[0].data.uinteger)) { error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API response. Invalid callid"); return (-1); } - run_response_params.size = 1; - run_response_params.obj = CALLOC(1, struct message_object); - run_response_params.obj[0].type = OBJECT_TYPE_UINT; - run_response_params.obj[0].data.uinteger = callid; - - if (connection_send_response(con_id, msgid, run_response_params, api_error) < 0) { - return (-1); - }; - - free_params(cinfo.response.params); + api_free_array(result.data.array); return (0); } diff --git a/src/api/sb-api.h b/src/api/sb-api.h index 0bc16e1..0b464a9 100644 --- a/src/api/sb-api.h +++ b/src/api/sb-api.h @@ -34,8 +34,8 @@ * @return 0 in case of success otherwise 1 */ int api_register(string name, string desc, - string author, string license, array functions, uint64_t con_id, - uint32_t msgid, char *pluginkey, struct api_error *api_error); + string author, string license, array functions, char *pluginkey, + struct api_error *api_error); /** * Run a plugin function @@ -47,8 +47,7 @@ * @return 0 in case of success otherwise -1 */ int api_run(char *targetpluginkey, string function_name, uint64_t callid, - struct message_object args, uint64_t con_id, uint32_t msgid, - struct api_error *api_error); + array args, struct api_error *api_error); /** * Generates an API key using /dev/urandom. The length of the key @@ -56,8 +55,17 @@ int api_run(char *targetpluginkey, string function_name, uint64_t callid, * @param[in] key string to store the API key. * @return 0 in case of success otherwise -1 */ + +int api_broadcast(string eventname, array args, struct api_error *api_error); +int api_subscribe(uint64_t id, string event, struct api_error *api_error); +int api_unsubscribe(uint64_t id, string event, struct api_error *api_error); int api_get_key(string key); -int api_result(char *targetpluginkey, uint64_t callid, - struct message_object args, uint64_t con_id, uint32_t msgid, +int api_result(char *targetpluginkey, uint64_t callid, array args, struct api_error *api_error); + +void api_free_string(string value); +void api_free_object(object value); +void api_free_array(array value); +void api_free_dictionary(dictionary value); +object copy_object(object obj); diff --git a/test/unit/pack-float.c b/src/api/subscribe.c similarity index 61% rename from test/unit/pack-float.c rename to src/api/subscribe.c index f590c6d..0fe02a7 100644 --- a/test/unit/pack-float.c +++ b/src/api/subscribe.c @@ -14,23 +14,25 @@ * along with this program. If not, see . */ -#include +#include +#include -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" +#include "rpc/db/sb-db.h" +#include "api/sb-api.h" +#include "api/helpers.h" -void unit_pack_float(UNUSED(void **state)) +int api_subscribe(uint64_t con_id, string event, struct api_error *api_error) { - msgpack_sbuffer sbuf; - msgpack_packer pk; + size_t length = (event.length < METHOD_MAXLEN ? event.length : METHOD_MAXLEN); + char e[METHOD_MAXLEN + 1]; - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + sbassert(api_error); - assert_int_equal(0, pack_float(&pk, 1.234)); - assert_int_not_equal(0, pack_float(NULL, 1.234)); + memcpy(e, event.str, length); + e[length] = '\000'; - msgpack_sbuffer_destroy(&sbuf); + connection_subscribe(con_id, e); + + return 0; } diff --git a/test/unit/pack-string.c b/src/api/unsubscribe.c similarity index 56% rename from test/unit/pack-string.c rename to src/api/unsubscribe.c index 7079a6e..de758dc 100644 --- a/test/unit/pack-string.c +++ b/src/api/unsubscribe.c @@ -14,25 +14,25 @@ * along with this program. If not, see . */ -#include +#include +#include -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" +#include "rpc/db/sb-db.h" +#include "api/sb-api.h" +#include "api/helpers.h" -void unit_pack_string(UNUSED(void **state)) +int api_unsubscribe(uint64_t con_id, string event, struct api_error *api_error) { - string key = cstring_copy_string("TeiwieDoowuiMeix6SooxieFievee2io3ohhu5uo5ughu8cieja4iu6chuirija"); - msgpack_sbuffer sbuf; - msgpack_packer pk; + size_t length = (event.length < METHOD_MAXLEN ? event.length : METHOD_MAXLEN); + char e[METHOD_MAXLEN + 1]; - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + sbassert(api_error); - assert_int_equal(0, pack_string(&pk, key)); - assert_int_not_equal(0, pack_string(NULL, key)); + memcpy(e, event.str, length); + e[length] = '\000'; - msgpack_sbuffer_destroy(&sbuf); - free_string(key); + connection_unsubscribe(con_id, e); + + return 0; } diff --git a/src/confparse.c b/src/confparse.c index 58939fc..c3b19d4 100644 --- a/src/confparse.c +++ b/src/confparse.c @@ -82,7 +82,7 @@ bitarray_init_zero(unsigned int n_bits) { /* round up to the next int. */ size_t sz = (n_bits+BITARRAY_MASK) >> BITARRAY_SHIFT; - return (CALLOC(sz, unsigned int)); + return calloc_or_die(sz, sizeof(unsigned int)); } /** Free the bit array ba. */ @@ -243,7 +243,7 @@ STATIC void reset(const configformat *fmt, void *options, const configvar *var, if (!use_defaults) return; /* all done */ if (var->initvalue) { - c = CALLOC(1, configline); + c = calloc_or_die(1, sizeof(configline)); c->key = box_strdup(var->name); c->value = box_strdup(var->initvalue); if (assign_value(fmt, options, c) < 0) { @@ -338,7 +338,7 @@ STATIC const char * unescape_string(const char *s, char **result, } } end_of_loop: - out = *result = MALLOC_ARRAY((size_t)(cp-s + 1), char); + out = *result = malloc_array_or_die((size_t)(cp-s + 1), sizeof(char)); cp = s+1; while (1) { switch (*cp) @@ -405,7 +405,7 @@ STATIC void line_append(configline **lst, const char *key, const char *val) { configline *newline; - newline = CALLOC(1, configline); + newline = calloc_or_die(1, sizeof(configline)); newline->key = box_strdup(key); newline->value = box_strdup(val); newline->next = NULL; @@ -668,7 +668,7 @@ int confparse_get_lines(const char *string, configline **result, int extended) /* This list can get long, so we keep a pointer to the end of it * rather than using line_append over and over and getting * n^2 performance. */ - *next = CALLOC(1, configline); + *next = calloc_or_die(1, sizeof(configline)); (*next)->key = k; (*next)->value = v; (*next)->next = NULL; diff --git a/src/filesystem.c b/src/filesystem.c index eb15693..ab21939 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -153,6 +153,9 @@ int filesystem_save_sync(const char *fn, const void *x, size_t xlen) int fd; int r; + if (!x) + return -1; + fd = filesystem_open_write(fn); if (fd == -1) @@ -174,6 +177,9 @@ int filesystem_load(const char *fn, void *x, size_t xlen) int fd; int r; + if (!x) + return -1; + fd = filesystem_open_read(fn); if (fd == -1) diff --git a/src/hashmap.c b/src/hashmap.c index 5b22b32..017c68e 100644 --- a/src/hashmap.c +++ b/src/hashmap.c @@ -59,7 +59,7 @@ \ hashmap(T, U) *hashmap_##T##_##U##_new() \ { \ - hashmap(T, U) *rv = MALLOC(hashmap(T, U)); \ + hashmap(T, U) *rv = malloc_or_die(sizeof(hashmap(T, U))); \ rv->table = kh_init(T##_##U##_map); \ return rv; \ } \ diff --git a/src/klist.h b/src/klist.h new file mode 100644 index 0000000..4da2cab --- /dev/null +++ b/src/klist.h @@ -0,0 +1,134 @@ +/* The MIT License + Copyright (c) 2008-2009, by Attractive Chaos + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef _AC_KLIST_H +#define _AC_KLIST_H + +#include +#include +#include "sb-common.h" + +#define KMEMPOOL_INIT(name, kmptype_t, kmpfree_f) \ + typedef struct { \ + size_t cnt, n, max; \ + kmptype_t **buf; \ + } kmp_##name##_t; \ + static inline kmp_##name##_t *kmp_init_##name(void) { \ + return calloc_or_die(1, sizeof(kmp_##name##_t)); \ + } \ + static inline void kmp_destroy_##name(kmp_##name##_t *mp) { \ + size_t k; \ + for (k = 0; k < mp->n; k++) { \ + kmpfree_f(mp->buf[k]); FREE(mp->buf[k]); \ + } \ + FREE(mp->buf); FREE(mp); \ + } \ + static inline kmptype_t *kmp_alloc_##name(kmp_##name##_t *mp) { \ + mp->cnt++; \ + if (mp->n == 0) { \ + return calloc_or_die(1, sizeof(kmptype_t)); \ + } \ + return mp->buf[--mp->n]; \ + } \ + static inline void kmp_free_##name(kmp_##name##_t *mp, kmptype_t *p) { \ + mp->cnt--; \ + if (mp->n == mp->max) { \ + mp->max = mp->max ? (mp->max << 1) : 16; \ + mp->buf = realloc_array_or_die(mp->buf, mp->max, sizeof(kmptype_t *)); \ + } \ + mp->buf[mp->n++] = p; \ + } + +#define kmempool_t(name) kmp_##name##_t +#define kmp_init(name) kmp_init_##name() +#define kmp_destroy(name, mp) kmp_destroy_##name(mp) +#define kmp_alloc(name, mp) kmp_alloc_##name(mp) +#define kmp_free(name, mp, p) kmp_free_##name(mp, p) + +#define KLIST_INIT(name, kltype_t, kmpfree_t) \ + struct __kl1_##name { \ + kltype_t data; \ + struct __kl1_##name *next; \ + }; \ + typedef struct __kl1_##name kl1_##name; \ + KMEMPOOL_INIT(name, kl1_##name, kmpfree_t) \ + typedef struct { \ + kl1_##name *head, *tail; \ + kmp_##name##_t *mp; \ + size_t size; \ + } kl_##name##_t; \ + static inline kl_##name##_t *kl_init_##name(void) { \ + kl_##name##_t *kl = calloc_or_die(1, sizeof(kl_##name##_t)); \ + kl->mp = kmp_init(name); \ + kl->head = kl->tail = kmp_alloc(name, kl->mp); \ + kl->head->next = 0; \ + return kl; \ + } \ + static inline void kl_destroy_##name(kl_##name##_t *kl) { \ + kl1_##name *p; \ + for (p = kl->head; p != kl->tail; p = p->next) { \ + kmp_free(name, kl->mp, p); \ + } \ + kmp_free(name, kl->mp, p); \ + kmp_destroy(name, kl->mp); \ + FREE(kl); \ + } \ + static inline void kl_push_##name(kl_##name##_t *kl, kltype_t d) { \ + kl1_##name *q, *p = kmp_alloc(name, kl->mp); \ + q = kl->tail; p->next = 0; kl->tail->next = p; kl->tail = p; \ + kl->size++; \ + q->data = d; \ + } \ + static inline kltype_t kl_shift_at_##name(kl_##name##_t *kl, \ + kl1_##name **n) { \ + assert((*n)->next); \ + kl1_##name *p; \ + kl->size--; \ + p = *n; \ + *n = (*n)->next; \ + if (p == kl->head) { \ + kl->head = *n; \ + } \ + kltype_t d = p->data; \ + kmp_free(name, kl->mp, p); \ + return d; \ + } + +#define kliter_t(name) kl1_##name +#define klist_t(name) kl_##name##_t +#define kl_val(iter) ((iter)->data) +#define kl_next(iter) ((iter)->next) +#define kl_begin(kl) ((kl)->head) +#define kl_end(kl) ((kl)->tail) + +#define kl_init(name) kl_init_##name() +#define kl_destroy(name, kl) kl_destroy_##name(kl) +#define kl_push(name, kl, d) kl_push_##name(kl, d) +#define kl_shift_at(name, kl, node) kl_shift_at_##name(kl, node) +#define kl_shift(name, kl) kl_shift_at(name, kl, &kl->head) +#define kl_empty(kl) ((kl)->size == 0) +// Iteration macros. It's ok to modify the list while iterating as long as a +// `break` statement is executed before the next iteration. +#define kl_iter(name, kl, p) kl_iter_at(name, kl, p, NULL) +#define kl_iter_at(name, kl, p, h) \ + for (kl1_##name **p = h ? h : &kl->head; *p != kl->tail; p = &(*p)->next) + +#endif diff --git a/src/kvec.h b/src/kvec.h index 31d2e14..2d087d0 100644 --- a/src/kvec.h +++ b/src/kvec.h @@ -49,42 +49,129 @@ int main() { #define AC_KVEC_H #include +#include -#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#include "sb-common.h" -#define kvec_t(type) struct { size_t size, capacity; type *obj; } -#define kv_init(v) ((v).size = (v).capacity = 0, (v).obj = 0) -#define kv_destroy(v) free((v).obj) -#define kv_A(v, i) ((v).obj[(i)]) -#define kv_pop(v) ((v).obj[--(v).size]) +#define kv_roundup32(x) \ + ((--(x)), \ + ((x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16), \ + (++(x))) + +#define KV_INITIAL_VALUE { .size = 0, .capacity = 0, .items = NULL } + +#define kvec_t(type) \ + struct { \ + size_t size; \ + size_t capacity; \ + type *items; \ + } + +#define kv_init(v) ((v).size = (v).capacity = 0, (v).items = 0) +#define kv_destroy(v) free((v).items) +#define kv_A(v, i) ((v).items[(i)]) +#define kv_pop(v) ((v).items[--(v).size]) #define kv_size(v) ((v).size) #define kv_max(v) ((v).capacity) +#define kv_last(v) kv_A(v, kv_size(v) - 1) + +#define kv_resize(v, s) \ + ((v).capacity = (s), \ + (v).items = realloc((v).items, sizeof((v).items[0]) * (v).capacity)) + +#define kv_resize_full(v) \ + kv_resize(v, (v).capacity ? (v).capacity << 1 : 8) + +#define kv_copy(v1, v0) \ + do { \ + if ((v1).capacity < (v0).size) { \ + kv_resize(v1, (v0).size); \ + } \ + (v1).size = (v0).size; \ + memcpy((v1).items, (v0).items, sizeof((v1).items[0]) * (v0).size); \ + } while (0) \ + +#define kv_pushp(v) \ + ((((v).size == (v).capacity) ? (kv_resize_full(v), 0) : 0), \ + ((v).items + ((v).size++))) + +#define kv_push(v, x) \ + (*kv_pushp(v) = (x)) + +#define kv_a(v, i) \ + (((v).capacity <= (size_t) (i) \ + ? ((v).capacity = (v).size = (i) + 1, \ + kv_roundup32((v).capacity), \ + kv_resize((v), (v).capacity), 0) \ + : ((v).size <= (size_t) (i) \ + ? (v).size = (i) + 1 \ + : 0)), \ + (v).items[(i)]) + +#define kvec_withinit_t(type, INIT_SIZE) \ + struct { \ + size_t size; \ + size_t capacity; \ + type *items; \ + type init_array[INIT_SIZE]; \ + } + +#define kvi_init(v) \ + ((v).capacity = ARRAY_SIZE((v).init_array), \ + (v).size = 0, \ + (v).items = (v).init_array) + +/// Move data to a new destination and free source +static inline void *_memcpy_free(void *const restrict dest, + void *const restrict src, + const size_t size) +{ + memcpy(dest, src, size); + free(src); + return dest; +} + -#define kv_resize(type, v, s) ((v).capacity = (s), (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity)) - -#define kv_copy(type, v1, v0) do { \ - if ((v1).capacity < (v0).size) kv_resize(type, v1, (v0).size); \ - (v1).size = (v0).size; \ - memcpy((v1).obj, (v0).obj, sizeof(type) * (v0).size); \ - } while (0) \ - -#define kv_push(type, v, x) do { \ - if ((v).size == (v).capacity) { \ - (v).capacity = (v).capacity? (v).capacity<<1 : 8; \ - (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity); \ - } \ - (v).obj[(v).size++] = (x); \ - } while (0) - -#define kv_pushp(type, v) (((v).size == (v).capacity)? \ - ((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \ - (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity), 0) \ - : 0), ((v).obj + ((v).size++)) - -#define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \ - ((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \ - (v).obj = (type*)realloc((v).obj, sizeof(type) * (v).capacity), 0) \ - : (v).size <= (size_t)(i)? (v).size = (i) + 1 \ - : 0), (v).obj[(i)]) +#define kvi_resize(v, s) \ + ((v).capacity = ((s) > ARRAY_SIZE((v).init_array) \ + ? (s) \ + : ARRAY_SIZE((v).init_array)), \ + (v).items = ((v).capacity == ARRAY_SIZE((v).init_array) \ + ? ((v).items == (v).init_array \ + ? (v).items \ + : _memcpy_free((v).init_array, (v).items, \ + (v).size * sizeof((v).items[0]))) \ + : ((v).items == (v).init_array \ + ? memcpy(xmalloc((v).capacity * sizeof((v).items[0])), \ + (v).items, \ + (v).size * sizeof((v).items[0])) \ + : realloc((v).items, \ + (v).capacity * sizeof((v).items[0]))))) + +/// Resize vector with preallocated array when it is full +/// +/// @param[out] v Vector to resize. +#define kvi_resize_full(v) \ + /* ARRAY_SIZE((v).init_array) is the minimal capacity of this vector. */ \ + /* Thus when vector is full capacity may not be zero and it is safe */ \ + /* not to bother with checking whether (v).capacity is 0. But now */ \ + /* capacity is not guaranteed to have size that is a power of 2, it is */ \ + /* hard to fix this here and is not very necessary if users will use */ \ + /* 2^x initial array size. */ \ + kvi_resize(v, (v).capacity << 1) + +#define kvi_pushp(v) \ + ((((v).size == (v).capacity) ? (kvi_resize_full(v), 0) : 0), \ + ((v).items + ((v).size++))) + +#define kvi_push(v, x) \ + (*kvi_pushp(v) = (x)) + +#define kvi_destroy(v) \ + do { \ + if ((v).items != (v).init_array) { \ + FREE((v).items); \ + } \ + } while (0) #endif diff --git a/src/main.c b/src/main.c index 86eaa07..7919003 100644 --- a/src/main.c +++ b/src/main.c @@ -22,9 +22,10 @@ #include "tweetnacl.h" #include "rpc/sb-rpc.h" #include "rpc/db/sb-db.h" +#include "main.h" int8_t verbose_level; -uv_loop_t loop; +loop main_loop; int main(int argc, char **argv) { @@ -38,7 +39,7 @@ int main(int argc, char **argv) abort(); } - uv_loop_init(&loop); + loop_init(&main_loop, NULL); crypto_init(); @@ -60,12 +61,6 @@ int main(int argc, char **argv) abort(); } - /* initialize event queue */ - if (event_initialize() == -1) { - LOG_ERROR("Failed to initialize event queue."); - abort(); - } - /* initialize connections */ if (connection_init() == -1) { LOG_ERROR("Failed to initialise connections."); @@ -85,9 +80,9 @@ int main(int argc, char **argv) server_start_pipe(globaloptions->ApiNamedPipeListen); } - uv_run(&loop, UV_RUN_DEFAULT); - - options_free(globaloptions); + for (;;) { + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, 2000, false); + } return (0); } diff --git a/test/unit/pack-nil.c b/src/main.h similarity index 62% rename from test/unit/pack-nil.c rename to src/main.h index b027c88..eacfddb 100644 --- a/test/unit/pack-nil.c +++ b/src/main.h @@ -14,23 +14,8 @@ * along with this program. If not, see . */ -#include + #pragma once -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" +#include "rpc/connection/loop.h" - -void unit_pack_nil(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - assert_int_equal(0, pack_nil(&pk)); - assert_int_not_equal(0, pack_nil(NULL)); - - msgpack_sbuffer_destroy(&sbuf); -} +extern loop main_loop; diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..b838869 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,47 @@ +#include + +#include "sb-common.h" + +void *malloc_or_die(size_t n) +{ + void *p = malloc(n); + if (!p && 0 < n) + LOG_MEMERROR(); + return p; +} + + +void *malloc_array_or_die(size_t nmemb, size_t n) +{ + void *p = reallocarray(NULL, nmemb, n); + if (!p && 0 < n) + LOG_MEMERROR(); + return p; +} + + +void *calloc_or_die(size_t nmemb, size_t n) +{ + void *p = calloc(nmemb, n); + if (!p && 0 < n) + LOG_MEMERROR(); + return p; +} + + +void *realloc_array_or_die(void *ptr, size_t nmemb, size_t n) +{ + void *p = reallocarray(ptr, nmemb, n); + if (!p && 0 < n) + LOG_MEMERROR(); + return p; +} + + +void *realloc_or_die(void *ptr, size_t n) +{ + void *p = realloc(ptr, n); + if (!p && 0 < n) + LOG_MEMERROR(); + return p; +} diff --git a/src/mem.h b/src/mem.h new file mode 100644 index 0000000..fd5ee39 --- /dev/null +++ b/src/mem.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#define FREE(p) do { \ + if (PREDICT_LIKELY((p)!=NULL)) \ + free(p); \ + p = NULL; \ + } while (0) + +#define MALLOC_TYPE(t) ((t)malloc_or_die(sizeof(t))) + +void *reallocarray(void *optr, size_t nmemb, size_t size); + +void *realloc_array_or_die(void *ptr, size_t nmemb, size_t n); +void *malloc_array_or_die(size_t nmemb, size_t n); +void *malloc_or_die(size_t n); +void *calloc_or_die(size_t nmemb, size_t n); +void *realloc_or_die(void *ptr, size_t n); + diff --git a/src/options.c b/src/options.c index a1eca04..6bc35a8 100644 --- a/src/options.c +++ b/src/options.c @@ -152,7 +152,7 @@ STATIC setoptionerror options_init_from_string(const char *cf) int retval; setoptionerror err = SETOPT_ERR_MISC; - newoptions = CALLOC(1, options); + newoptions = calloc_or_die(1, sizeof(options)); newoptions->magic_ = OR_OPTIONS_MAGIC; options_init(newoptions); @@ -212,7 +212,7 @@ STATIC char *load_boxrc_from_disk(void) return (NULL); } - string = MALLOC_ARRAY((size_t)(statbuf.st_size+1), char); + string = malloc_array_or_die((size_t)(statbuf.st_size+1), sizeof(char)); ssize_t r = filesystem_read_all(fd, string, (size_t)statbuf.st_size); if (r < 0) { diff --git a/src/queue.h b/src/queue.h index 1be9e9c..1c52ad8 100644 --- a/src/queue.h +++ b/src/queue.h @@ -1,753 +1,85 @@ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - * $FreeBSD$ - */ -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ +// Copyright (c) 2013, Ben Noordhuis +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -#include +#pragma once -/* - * This file defines four types of data structures: singly-linked lists, - * singly-linked tail queues, lists and tail queues. - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A singly-linked tail queue is headed by a pair of pointers, one to the - * head of the list and the other to the tail of the list. The elements are - * singly linked for minimum space and pointer manipulation overhead at the - * expense of O(n) removal for arbitrary elements. New elements can be added - * to the list after an existing element, at the head of the list, or at the - * end of the list. Elements being removed from the head of the tail queue - * should use the explicit macro for this purpose for optimum efficiency. - * A singly-linked tail queue may only be traversed in the forward direction. - * Singly-linked tail queues are ideal for applications with large datasets - * and few or no removals or for implementing a FIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may be traversed in either direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * For details on the use of these macros, see the queue(3) manual page. - * - * - * SLIST LIST STAILQ TAILQ - * _HEAD + + + + - * _CLASS_HEAD + + + + - * _HEAD_INITIALIZER + + + + - * _ENTRY + + + + - * _CLASS_ENTRY + + + + - * _INIT + + + + - * _EMPTY + + + + - * _FIRST + + + + - * _NEXT + + + + - * _PREV - + - + - * _LAST - - + + - * _FOREACH + + + + - * _FOREACH_FROM + + + + - * _FOREACH_SAFE + + + + - * _FOREACH_FROM_SAFE + + + + - * _FOREACH_REVERSE - - - + - * _FOREACH_REVERSE_FROM - - - + - * _FOREACH_REVERSE_SAFE - - - + - * _FOREACH_REVERSE_FROM_SAFE - - - + - * _INSERT_HEAD + + + + - * _INSERT_BEFORE - + - + - * _INSERT_AFTER + + + + - * _INSERT_TAIL - - + + - * _CONCAT - - + + - * _REMOVE_AFTER + - + - - * _REMOVE_HEAD + - + - - * _REMOVE + + + + - * _SWAP + + + + - * - */ -#ifdef QUEUE_MACRO_DEBUG -/* Store the last 2 places the queue element or head was altered */ -struct qm_trace { - unsigned long lastline; - unsigned long prevline; - const char *lastfile; - const char *prevfile; -}; +#include -#define TRACEBUF struct qm_trace trace; -#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } , -#define TRASHIT(x) do {(x) = (void *)-1;} while (0) -#define QMD_SAVELINK(name, link) void **name = (void *)&(link) +typedef struct _queue { + struct _queue *next; + struct _queue *prev; +} QUEUE; -#define QMD_TRACE_HEAD(head) do { \ - (head)->trace.prevline = (head)->trace.lastline; \ - (head)->trace.prevfile = (head)->trace.lastfile; \ - (head)->trace.lastline = __LINE__; \ - (head)->trace.lastfile = __FILE__; \ -} while (0) +// Public macros. +#define QUEUE_DATA(ptr, type, field) \ + ((type *)(void *)((char *)(ptr) - offsetof(type, field))) -#define QMD_TRACE_ELEM(elem) do { \ - (elem)->trace.prevline = (elem)->trace.lastline; \ - (elem)->trace.prevfile = (elem)->trace.lastfile; \ - (elem)->trace.lastline = __LINE__; \ - (elem)->trace.lastfile = __FILE__; \ -} while (0) +#define QUEUE_FOREACH(q, h) \ + for ( /* NOLINT(readability/braces) */ \ + (q) = (h)->next; (q) != (h); (q) = (q)->next) -#else -#define QMD_TRACE_ELEM(elem) -#define QMD_TRACE_HEAD(head) -#define QMD_SAVELINK(name, link) -#define TRACEBUF -#define TRACEBUF_INITIALIZER -#define TRASHIT(x) -#endif /* QUEUE_MACRO_DEBUG */ - -#ifdef __cplusplus -/* - * In C++ there can be structure lists and class lists: - */ -#define QUEUE_TYPEOF(type) type -#else -#define QUEUE_TYPEOF(type) struct type -#endif - -/* - * Singly-linked List declarations. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_CLASS_HEAD(name, type) \ -struct name { \ - class type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -#define SLIST_CLASS_ENTRY(type) \ -struct { \ - class type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List functions. - */ -#define SLIST_EMPTY(head) ((head)->slh_first == NULL) - -#define SLIST_FIRST(head) ((head)->slh_first) - -#define SLIST_FOREACH(var, head, field) \ - for ((var) = SLIST_FIRST((head)); \ - (var); \ - (var) = SLIST_NEXT((var), field)) - -#define SLIST_FOREACH_FROM(var, head, field) \ - for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ - (var); \ - (var) = SLIST_NEXT((var), field)) - -#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = SLIST_FIRST((head)); \ - (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ - for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ - (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != NULL; \ - (varp) = &SLIST_NEXT((var), field)) - -#define SLIST_INIT(head) do { \ - SLIST_FIRST((head)) = NULL; \ -} while (0) - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ - SLIST_NEXT((slistelm), field) = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ - SLIST_FIRST((head)) = (elm); \ -} while (0) - -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ - if (SLIST_FIRST((head)) == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } \ - else { \ - QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \ - while (SLIST_NEXT(curelm, field) != (elm)) \ - curelm = SLIST_NEXT(curelm, field); \ - SLIST_REMOVE_AFTER(curelm, field); \ - } \ - TRASHIT(*oldnext); \ -} while (0) - -#define SLIST_REMOVE_AFTER(elm, field) do { \ - SLIST_NEXT(elm, field) = \ - SLIST_NEXT(SLIST_NEXT(elm, field), field); \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ -} while (0) - -#define SLIST_SWAP(head1, head2, type) do { \ - QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \ - SLIST_FIRST(head1) = SLIST_FIRST(head2); \ - SLIST_FIRST(head2) = swap_first; \ -} while (0) - -/* - * Singly-linked Tail queue declarations. - */ -#define STAILQ_HEAD(name, type) \ -struct name { \ - struct type *stqh_first;/* first element */ \ - struct type **stqh_last;/* addr of last next element */ \ -} - -#define STAILQ_CLASS_HEAD(name, type) \ -struct name { \ - class type *stqh_first; /* first element */ \ - class type **stqh_last; /* addr of last next element */ \ -} - -#define STAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).stqh_first } - -#define STAILQ_ENTRY(type) \ -struct { \ - struct type *stqe_next; /* next element */ \ -} - -#define STAILQ_CLASS_ENTRY(type) \ -struct { \ - class type *stqe_next; /* next element */ \ -} - -/* - * Singly-linked Tail queue functions. - */ -#define STAILQ_CONCAT(head1, head2) do { \ - if (!STAILQ_EMPTY((head2))) { \ - *(head1)->stqh_last = (head2)->stqh_first; \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_INIT((head2)); \ - } \ -} while (0) - -#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) - -#define STAILQ_FIRST(head) ((head)->stqh_first) - -#define STAILQ_FOREACH(var, head, field) \ - for((var) = STAILQ_FIRST((head)); \ - (var); \ - (var) = STAILQ_NEXT((var), field)) - -#define STAILQ_FOREACH_FROM(var, head, field) \ - for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ - (var); \ - (var) = STAILQ_NEXT((var), field)) - -#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = STAILQ_FIRST((head)); \ - (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ - for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ - (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define STAILQ_INIT(head) do { \ - STAILQ_FIRST((head)) = NULL; \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) - -#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ - if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ - STAILQ_NEXT((tqelm), field) = (elm); \ -} while (0) - -#define STAILQ_INSERT_HEAD(head, elm, field) do { \ - if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ - STAILQ_FIRST((head)) = (elm); \ -} while (0) - -#define STAILQ_INSERT_TAIL(head, elm, field) do { \ - STAILQ_NEXT((elm), field) = NULL; \ - *(head)->stqh_last = (elm); \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ -} while (0) - -#define STAILQ_LAST(head, type, field) \ - (STAILQ_EMPTY((head)) ? NULL : \ - __containerof((head)->stqh_last, \ - QUEUE_TYPEOF(type), field.stqe_next)) - -#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) - -#define STAILQ_REMOVE(head, elm, type, field) do { \ - QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ - if (STAILQ_FIRST((head)) == (elm)) { \ - STAILQ_REMOVE_HEAD((head), field); \ - } \ - else { \ - QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \ - while (STAILQ_NEXT(curelm, field) != (elm)) \ - curelm = STAILQ_NEXT(curelm, field); \ - STAILQ_REMOVE_AFTER(head, curelm, field); \ - } \ - TRASHIT(*oldnext); \ -} while (0) - -#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ - if ((STAILQ_NEXT(elm, field) = \ - STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ -} while (0) - -#define STAILQ_REMOVE_HEAD(head, field) do { \ - if ((STAILQ_FIRST((head)) = \ - STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) - -#define STAILQ_SWAP(head1, head2, type) do { \ - QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \ - QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \ - STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_FIRST(head2) = swap_first; \ - (head2)->stqh_last = swap_last; \ - if (STAILQ_EMPTY(head1)) \ - (head1)->stqh_last = &STAILQ_FIRST(head1); \ - if (STAILQ_EMPTY(head2)) \ - (head2)->stqh_last = &STAILQ_FIRST(head2); \ -} while (0) - - -/* - * List declarations. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ +// ffi.cdef is unable to swallow `bool` in place of `int` here. +static inline int QUEUE_EMPTY(const QUEUE *const q) +{ + return q == q->next; } -#define LIST_CLASS_HEAD(name, type) \ -struct name { \ - class type *lh_first; /* first element */ \ -} +#define QUEUE_HEAD(q) (q)->next -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ +static inline void QUEUE_INIT(QUEUE *const q) +{ + q->next = q; + q->prev = q; } -#define LIST_CLASS_ENTRY(type) \ -struct { \ - class type *le_next; /* next element */ \ - class type **le_prev; /* address of previous next element */ \ +static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n) +{ + h->prev->next = n->next; + n->next->prev = h->prev; + h->prev = n->prev; + h->prev->next = h; } -/* - * List functions. - */ - -#if (defined(_KERNEL) && defined(INVARIANTS)) -#define QMD_LIST_CHECK_HEAD(head, field) do { \ - if (LIST_FIRST((head)) != NULL && \ - LIST_FIRST((head))->field.le_prev != \ - &LIST_FIRST((head))) \ - panic("Bad list head %p first->prev != head", (head)); \ -} while (0) - -#define QMD_LIST_CHECK_NEXT(elm, field) do { \ - if (LIST_NEXT((elm), field) != NULL && \ - LIST_NEXT((elm), field)->field.le_prev != \ - &((elm)->field.le_next)) \ - panic("Bad link elm %p next->prev != elm", (elm)); \ -} while (0) - -#define QMD_LIST_CHECK_PREV(elm, field) do { \ - if (*(elm)->field.le_prev != (elm)) \ - panic("Bad link elm %p prev->next != elm", (elm)); \ -} while (0) -#else -#define QMD_LIST_CHECK_HEAD(head, field) -#define QMD_LIST_CHECK_NEXT(elm, field) -#define QMD_LIST_CHECK_PREV(elm, field) -#endif /* (_KERNEL && INVARIANTS) */ - -#define LIST_EMPTY(head) ((head)->lh_first == NULL) - -#define LIST_FIRST(head) ((head)->lh_first) - -#define LIST_FOREACH(var, head, field) \ - for ((var) = LIST_FIRST((head)); \ - (var); \ - (var) = LIST_NEXT((var), field)) - -#define LIST_FOREACH_FROM(var, head, field) \ - for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ - (var); \ - (var) = LIST_NEXT((var), field)) - -#define LIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = LIST_FIRST((head)); \ - (var) && ((tvar) = LIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ - for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ - (var) && ((tvar) = LIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define LIST_INIT(head) do { \ - LIST_FIRST((head)) = NULL; \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - QMD_LIST_CHECK_NEXT(listelm, field); \ - if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ - LIST_NEXT((listelm), field)->field.le_prev = \ - &LIST_NEXT((elm), field); \ - LIST_NEXT((listelm), field) = (elm); \ - (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - QMD_LIST_CHECK_PREV(listelm, field); \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - LIST_NEXT((elm), field) = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - QMD_LIST_CHECK_HEAD((head), field); \ - if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ - LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ - LIST_FIRST((head)) = (elm); \ - (elm)->field.le_prev = &LIST_FIRST((head)); \ -} while (0) - -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_PREV(elm, head, type, field) \ - ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ - __containerof((elm)->field.le_prev, \ - QUEUE_TYPEOF(type), field.le_next)) - -#define LIST_REMOVE(elm, field) do { \ - QMD_SAVELINK(oldnext, (elm)->field.le_next); \ - QMD_SAVELINK(oldprev, (elm)->field.le_prev); \ - QMD_LIST_CHECK_NEXT(elm, field); \ - QMD_LIST_CHECK_PREV(elm, field); \ - if (LIST_NEXT((elm), field) != NULL) \ - LIST_NEXT((elm), field)->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = LIST_NEXT((elm), field); \ - TRASHIT(*oldnext); \ - TRASHIT(*oldprev); \ -} while (0) - -#define LIST_SWAP(head1, head2, type, field) do { \ - QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \ - LIST_FIRST((head1)) = LIST_FIRST((head2)); \ - LIST_FIRST((head2)) = swap_tmp; \ - if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ - swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ - if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ - swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ -} while (0) - -/* - * Tail queue declarations. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ - TRACEBUF \ +static inline void QUEUE_SPLIT(QUEUE *const h, QUEUE *const q, QUEUE *const n) +{ + n->prev = h->prev; + n->prev->next = n; + n->next = q; + h->prev = q->prev; + h->prev->next = h; + q->prev = n; } -#define TAILQ_CLASS_HEAD(name, type) \ -struct name { \ - class type *tqh_first; /* first element */ \ - class type **tqh_last; /* addr of last next element */ \ - TRACEBUF \ +static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q) +{ + q->next = h->next; + q->prev = h; + q->next->prev = q; + h->next = q; } -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ - TRACEBUF \ +static inline void QUEUE_INSERT_TAIL(QUEUE *const h, QUEUE *const q) +{ + q->next = h; + q->prev = h->prev; + q->prev->next = q; + h->prev = q; } -#define TAILQ_CLASS_ENTRY(type) \ -struct { \ - class type *tqe_next; /* next element */ \ - class type **tqe_prev; /* address of previous next element */ \ - TRACEBUF \ +static inline void QUEUE_REMOVE(QUEUE *const q) +{ + q->prev->next = q->next; + q->next->prev = q->prev; } - -/* - * Tail queue functions. - */ -#if (defined(_KERNEL) && defined(INVARIANTS)) -#define QMD_TAILQ_CHECK_HEAD(head, field) do { \ - if (!TAILQ_EMPTY(head) && \ - TAILQ_FIRST((head))->field.tqe_prev != \ - &TAILQ_FIRST((head))) \ - panic("Bad tailq head %p first->prev != head", (head)); \ -} while (0) - -#define QMD_TAILQ_CHECK_TAIL(head, field) do { \ - if (*(head)->tqh_last != NULL) \ - panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \ -} while (0) - -#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \ - if (TAILQ_NEXT((elm), field) != NULL && \ - TAILQ_NEXT((elm), field)->field.tqe_prev != \ - &((elm)->field.tqe_next)) \ - panic("Bad link elm %p next->prev != elm", (elm)); \ -} while (0) - -#define QMD_TAILQ_CHECK_PREV(elm, field) do { \ - if (*(elm)->field.tqe_prev != (elm)) \ - panic("Bad link elm %p prev->next != elm", (elm)); \ -} while (0) -#else -#define QMD_TAILQ_CHECK_HEAD(head, field) -#define QMD_TAILQ_CHECK_TAIL(head, headname) -#define QMD_TAILQ_CHECK_NEXT(elm, field) -#define QMD_TAILQ_CHECK_PREV(elm, field) -#endif /* (_KERNEL && INVARIANTS) */ - -#define TAILQ_CONCAT(head1, head2, field) do { \ - if (!TAILQ_EMPTY(head2)) { \ - *(head1)->tqh_last = (head2)->tqh_first; \ - (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ - (head1)->tqh_last = (head2)->tqh_last; \ - TAILQ_INIT((head2)); \ - QMD_TRACE_HEAD(head1); \ - QMD_TRACE_HEAD(head2); \ - } \ -} while (0) - -#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) - -#define TAILQ_FIRST(head) ((head)->tqh_first) - -#define TAILQ_FOREACH(var, head, field) \ - for ((var) = TAILQ_FIRST((head)); \ - (var); \ - (var) = TAILQ_NEXT((var), field)) - -#define TAILQ_FOREACH_FROM(var, head, field) \ - for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ - (var); \ - (var) = TAILQ_NEXT((var), field)) - -#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = TAILQ_FIRST((head)); \ - (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ - for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ - (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var); \ - (var) = TAILQ_PREV((var), headname, field)) - -#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ - for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ - (var); \ - (var) = TAILQ_PREV((var), headname, field)) - -#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ - (var) = (tvar)) - -#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ - for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ - (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ - (var) = (tvar)) - -#define TAILQ_INIT(head) do { \ - TAILQ_FIRST((head)) = NULL; \ - (head)->tqh_last = &TAILQ_FIRST((head)); \ - QMD_TRACE_HEAD(head); \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - QMD_TAILQ_CHECK_NEXT(listelm, field); \ - if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ - TAILQ_NEXT((elm), field)->field.tqe_prev = \ - &TAILQ_NEXT((elm), field); \ - else { \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - QMD_TRACE_HEAD(head); \ - } \ - TAILQ_NEXT((listelm), field) = (elm); \ - (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ - QMD_TRACE_ELEM(&(elm)->field); \ - QMD_TRACE_ELEM(&(listelm)->field); \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - QMD_TAILQ_CHECK_PREV(listelm, field); \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - TAILQ_NEXT((elm), field) = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ - QMD_TRACE_ELEM(&(elm)->field); \ - QMD_TRACE_ELEM(&(listelm)->field); \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - QMD_TAILQ_CHECK_HEAD(head, field); \ - if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ - TAILQ_FIRST((head))->field.tqe_prev = \ - &TAILQ_NEXT((elm), field); \ - else \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - TAILQ_FIRST((head)) = (elm); \ - (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ - QMD_TRACE_HEAD(head); \ - QMD_TRACE_ELEM(&(elm)->field); \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - QMD_TAILQ_CHECK_TAIL(head, field); \ - TAILQ_NEXT((elm), field) = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - QMD_TRACE_HEAD(head); \ - QMD_TRACE_ELEM(&(elm)->field); \ -} while (0) - -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) - -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - -#define TAILQ_REMOVE(head, elm, field) do { \ - QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \ - QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \ - QMD_TAILQ_CHECK_NEXT(elm, field); \ - QMD_TAILQ_CHECK_PREV(elm, field); \ - if ((TAILQ_NEXT((elm), field)) != NULL) \ - TAILQ_NEXT((elm), field)->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else { \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - QMD_TRACE_HEAD(head); \ - } \ - *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ - TRASHIT(*oldnext); \ - TRASHIT(*oldprev); \ - QMD_TRACE_ELEM(&(elm)->field); \ -} while (0) - -#define TAILQ_SWAP(head1, head2, type, field) do { \ - QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \ - QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \ - (head1)->tqh_first = (head2)->tqh_first; \ - (head1)->tqh_last = (head2)->tqh_last; \ - (head2)->tqh_first = swap_first; \ - (head2)->tqh_last = swap_last; \ - if ((swap_first = (head1)->tqh_first) != NULL) \ - swap_first->field.tqe_prev = &(head1)->tqh_first; \ - else \ - (head1)->tqh_last = &(head1)->tqh_first; \ - if ((swap_first = (head2)->tqh_first) != NULL) \ - swap_first->field.tqe_prev = &(head2)->tqh_first; \ - else \ - (head2)->tqh_last = &(head2)->tqh_first; \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/rpc/connection/connection.c b/src/rpc/connection/connection.c index 40267bd..8645c43 100644 --- a/src/rpc/connection/connection.c +++ b/src/rpc/connection/connection.c @@ -30,53 +30,64 @@ * limitations under the License. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tweetnacl.h" -#include "rpc/sb-rpc.h" #include "rpc/connection/connection.h" -#include "api/sb-api.h" - -STATIC int parse_cb(inputstream *istream, void *data, bool eof); +#ifdef __linux__ +#include // for strlcpy +#endif +#include // for msgpack_object, msgpack_object_union +#include // for msgpack_packer_init, msgpack_packer +#include // for msgpack_sbuffer, msgpack_sbuffer_c... +#include // for msgpack_unpacked, msgpack_unpacked... +#include // for true, bool, false +#include // for NULL, size_t +#include // for uint64_t, UINT64_MAX, uint32_t +#include // for abort, exit, realloc +#include // for strlen +#include // for uv_handle_t, uv_close, uv_timer_t +#include "api/helpers.h" // for NIL +#include "api/sb-api.h" // for api_free_array, api_free_object +#include "khash.h" // for __i, khint32_t +#include "main.h" // for main_loop +#include "rpc/connection/event.h" // for multiqueue_free, multiqueue_new_child +#include "rpc/connection/loop.h" // for loop, LOOP_PROCESS_EVENTS_UNTIL +#include "rpc/msgpack/helpers.h" // for msgpack_rpc_serialize_request, msg... +#include "rpc/sb-rpc.h" // for callinfo, crypto_context, object +#include "tweetnacl.h" // for randombytes + + +STATIC void parse_cb(inputstream *istream, void *data, bool eof); STATIC void close_cb(uv_handle_t *handle); STATIC void timer_cb(uv_timer_t *timer); -STATIC int connection_handle_request(struct connection *con, +STATIC void connection_handle_request(struct connection *con, msgpack_object *obj); STATIC void connection_handle_response(struct connection *con, msgpack_object *obj); -STATIC void connection_request_event(connection_request_event_info *info); +STATIC void connection_request_event(void **argv); STATIC void connection_close(struct connection *con); STATIC void call_set_error(struct connection *con, char *msg); STATIC int is_valid_rpc_response(msgpack_object *obj, struct connection *con); STATIC void free_connection(struct connection *con); STATIC void incref(struct connection *con); STATIC void decref(struct connection *con); +STATIC void unsubscribe(struct connection *con, char *event); +STATIC void send_delayed_notifications(struct connection *con); static uint64_t next_con_id = 1; static hashmap(uint64_t, ptr_t) *connections = NULL; static hashmap(cstr_t, uint64_t) *pluginkeys = NULL; +static hashmap(cstr_t, ptr_t) *event_strings = NULL; static msgpack_sbuffer sbuf; -equeue *equeue_root; -uv_loop_t loop; int connection_init(void) { connections = hashmap_new(uint64_t, ptr_t)(); pluginkeys = hashmap_new(cstr_t, uint64_t)(); + event_strings = hashmap_new(cstr_t, ptr_t)(); if (dispatch_table_init() == -1) return (-1); - if (!connections || !pluginkeys) + if (!connections || !pluginkeys || !event_strings) return (-1); msgpack_sbuffer_init(&sbuf); @@ -95,8 +106,10 @@ int connection_teardown(void) connection_close(con); }); + hashmap_free(uint64_t, ptr_t)(connections); hashmap_free(cstr_t, uint64_t)(pluginkeys); + hashmap_free(cstr_t, ptr_t)(event_strings); dispatch_teardown(); msgpack_sbuffer_destroy(&sbuf); @@ -110,21 +123,20 @@ int connection_create(uv_stream_t *stream) stream->data = NULL; - struct connection *con = MALLOC(struct connection); - - if (con == NULL) - return (-1); + struct connection *con = malloc_or_die(sizeof(struct connection)); con->id = next_con_id++; con->msgid = 1; con->refcount = 1; con->mpac = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); con->closed = false; - con->queue = equeue_new(equeue_root); + con->events = multiqueue_new_child(main_loop.events); con->streams.read = inputstream_new(parse_cb, STREAM_BUFFER_SIZE, con); con->streams.write = outputstream_new(1024 * 1024); con->streams.uv = stream; con->cc.nonce = (uint64_t) randommod(281474976710656LL); + con->subscribed_events = hashmap_new(cstr_t, ptr_t)(); + con->pending_requests = 0; if (ISODD(con->cc.nonce)) { con->cc.nonce++; @@ -137,7 +149,7 @@ int connection_create(uv_stream_t *stream) randombytes(con->cc.minutekey, sizeof con->cc.minutekey); randombytes(con->cc.lastminutekey, sizeof con->cc.lastminutekey); con->minutekey_timer.data = &con->cc; - r = uv_timer_init(&loop, &con->minutekey_timer); + r = uv_timer_init(&main_loop.uv, &con->minutekey_timer); sbassert(r == 0); r = uv_timer_start(&con->minutekey_timer, timer_cb, 60000, 60000); sbassert(r == 0); @@ -148,6 +160,7 @@ int connection_create(uv_stream_t *stream) con->packet.pos = 0; kv_init(con->callvector); + kv_init(con->delayed_notifications); inputstream_set(con->streams.read, stream); inputstream_start(con->streams.read); @@ -158,6 +171,90 @@ int connection_create(uv_stream_t *stream) return (0); } +void connection_subscribe(uint64_t id, char *event) +{ + struct connection *con; + + if (!(con = hashmap_get(uint64_t, ptr_t)(connections, id)) || con->closed) + abort(); + + char *event_string = hashmap_get(cstr_t, ptr_t)(event_strings, event); + + if (!event_string) { + event_string = box_strdup(event); + hashmap_put(cstr_t, ptr_t)(event_strings, event_string, event_string); + } + + hashmap_put(cstr_t, ptr_t)(con->subscribed_events, event_string, event_string); +} + +void connection_unsubscribe(uint64_t id, char *event) +{ + struct connection *con; + + if (!(con = hashmap_get(uint64_t, ptr_t)(connections, id)) || con->closed) + abort(); + + unsubscribe(con, event); +} + +STATIC void broadcast_event(char *name, array args) +{ + kvec_t(struct connection *) subscribed = KV_INITIAL_VALUE; + struct connection *con; + msgpack_packer packer; + + hashmap_foreach_value(connections, con, { + if (hashmap_has(cstr_t, ptr_t)(con->subscribed_events, name)) { + kv_push(subscribed, con); + } + }); + + if (!kv_size(subscribed)) { + api_free_array(args); + goto end; + } + + string method = {.length = strlen(name), .str = name}; + + msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write); + msgpack_rpc_serialize_request(0, method, args, &packer); + api_free_array(args); + + for (size_t i = 0; i < kv_size(subscribed); i++) { + con = kv_A(subscribed, i); + + if (con->pending_requests) { + wbuffer *rv = malloc_or_die(sizeof(wbuffer)); + rv->size = sbuf.size; + rv->data = sb_memdup_nulterm(sbuf.data, sbuf.size); + kv_push(con->delayed_notifications, rv); + } else { + crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write); + } + } + + msgpack_sbuffer_clear(&sbuf); + +end: + kv_destroy(subscribed); +} + +STATIC void unsubscribe(struct connection *con, char *event) +{ + char *event_string = hashmap_get(cstr_t, ptr_t)(event_strings, event); + hashmap_del(cstr_t, ptr_t)(con->subscribed_events, event_string); + + hashmap_foreach_value(connections, con, { + if (hashmap_has(cstr_t, ptr_t)(con->subscribed_events, event_string)) { + return; + } + }); + + hashmap_del(cstr_t, ptr_t)(event_strings, event_string); + FREE(event_string); +} + STATIC void incref(struct connection *con) { con->refcount++; @@ -171,12 +268,12 @@ STATIC void decref(struct connection *con) } -int connection_hashmap_put(uint64_t id, struct connection *con) +void connection_hashmap_put(uint64_t id, struct connection *con) { hashmap_put(uint64_t, ptr_t)(connections, id, con); } -int pluginkeys_hashmap_put(char *pluginkey, uint64_t id) +void pluginkeys_hashmap_put(char *pluginkey, uint64_t id) { hashmap_put(cstr_t, uint64_t)(pluginkeys, pluginkey, id); } @@ -186,8 +283,16 @@ STATIC void free_connection(struct connection *con) hashmap_del(uint64_t, ptr_t)(connections, con->id); hashmap_del(cstr_t, uint64_t)(pluginkeys, con->cc.pluginkeystring); msgpack_unpacker_free(con->mpac); + + char *event_string; + hashmap_foreach_value(con->subscribed_events, event_string, { + unsubscribe(con, event_string); + }); + + hashmap_free(cstr_t, ptr_t)(con->subscribed_events); kv_destroy(con->callvector); - equeue_free(con->queue); + kv_destroy(con->delayed_notifications); + multiqueue_free(con->events); if (con->packet.data) FREE(con->packet.data); @@ -203,17 +308,18 @@ STATIC void timer_cb(uv_timer_t *timer) STATIC void connection_close(struct connection *con) { - int is_closing; uv_handle_t *handle; uv_handle_t *timer_handle; if (con->closed) return; + con->closed = true; + timer_handle = (uv_handle_t*) &con->minutekey_timer; if (timer_handle) { uv_close(timer_handle, NULL); - uv_run(&loop, UV_RUN_ONCE); + uv_run(&main_loop.uv, UV_RUN_ONCE); } inputstream_free(con->streams.read); @@ -223,10 +329,6 @@ STATIC void connection_close(struct connection *con) if (handle) uv_close(handle, close_cb); - kv_destroy(con->callvector); - - con->closed = 0; - decref(con); } @@ -250,7 +352,31 @@ STATIC void reset_parser(struct connection *con) reset_packet(con); } -STATIC int parse_cb(inputstream *istream, void *data, bool eof) +STATIC bool is_rpc_response(msgpack_object *obj) +{ + return obj->type == MSGPACK_OBJECT_ARRAY + && obj->via.array.size == 4 + && obj->via.array.ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER + && obj->via.array.ptr[0].via.u64 == 1 + && obj->via.array.ptr[1].type == MSGPACK_OBJECT_POSITIVE_INTEGER; +} + +STATIC void send_error(struct connection *con, uint64_t id, char *err) +{ + struct api_error e = ERROR_INIT; + + error_set(&e, API_ERROR_TYPE_VALIDATION, "%s", err); + + msgpack_packer pac; + msgpack_packer_init(&pac, &sbuf, msgpack_sbuffer_write); + msgpack_rpc_serialize_response(id, &e, NIL, &pac); + + crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write); + + msgpack_sbuffer_clear(&sbuf); +} + +STATIC void parse_cb(inputstream *istream, void *data, bool eof) { unsigned char *packet; unsigned char hellopacket[192]; @@ -269,7 +395,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) if (eof) { connection_close(con); - goto fail; + goto end; } if (con->cc.state == TUNNEL_INITIAL) { @@ -278,7 +404,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) con->streams.write) != 0) LOG_WARNING("establishing crypto tunnel failed at hello-cookie packet"); - goto fail; + goto end; } else if (con->cc.state == TUNNEL_COOKIE_SENT) { size = inputstream_read(istream, initiatepacket, 256); if (crypto_recv_initiate(&con->cc, initiatepacket) != 0) { @@ -286,6 +412,15 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) con->cc.state = TUNNEL_INITIAL; } + if (hashmap_has(cstr_t, uint64_t)(pluginkeys, + con->cc.pluginkeystring)) { + LOG_WARNING("pluginkey already registered, closing connection"); + sbmemzero(con->cc.pluginkeystring, + sizeof con->cc.pluginkeystring); + connection_close(con); + goto end; + } + hashmap_put(cstr_t, uint64_t)(pluginkeys, con->cc.pluginkeystring, con->id); } @@ -293,7 +428,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) pending = inputstream_pending(istream); if (pending <= 0 || con->cc.state != TUNNEL_ESTABLISHED) - goto fail; + goto end; if (con->packet.end <= 0) { packet = inputstream_get_read(istream, &read); @@ -301,21 +436,16 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) /* read the packet length */ if (crypto_verify_header(&con->cc, packet, &con->packet.length)) { reset_packet(con); - goto fail; + goto end; } con->packet.end = con->packet.length; - con->packet.data = MALLOC_ARRAY(MAX(con->packet.end, read), unsigned char); - - if (!con->packet.data) { - LOG_ERROR("Failed to alloc mem for con packet."); - goto fail; - } + con->packet.data = malloc_array_or_die(MAX(con->packet.end, read), sizeof(unsigned char)); if (msgpack_unpacker_reserve_buffer(con->mpac, MAX(read, con->packet.end)) == false) { LOG_ERROR("Failed to reserve mem msgpack buffer."); - goto fail; + goto end; }; /* get decrypted message start position */ @@ -333,7 +463,7 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) if (crypto_read(&con->cc, con->packet.data, con->unpackbuf + consumedlen, con->packet.length, &plaintextlen) != 0) { reset_parser(con); - goto fail; + goto end; } consumedlen += plaintextlen; @@ -341,12 +471,12 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) if (packet == NULL) { reset_parser(con); - goto fail; + goto end; } if (crypto_verify_header(&con->cc, packet, &con->packet.length)) { reset_parser(con); - goto fail; + goto end; } con->packet.end = con->packet.length; @@ -355,19 +485,17 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) } if (con->packet.end > 0 && read == 0) { - decref(con); - return (0); + goto end; } if (crypto_read(&con->cc, con->packet.data, con->unpackbuf + consumedlen, con->packet.length, &plaintextlen) != 0) { reset_parser(con); - goto fail; + goto end; } consumedlen += plaintextlen; - reset_packet(con); - FREE(con->packet.data); + reset_parser(con); } msgpack_unpacker_buffer_consumed(con->mpac, consumedlen); @@ -377,9 +505,9 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) /* deserialize objects, one by one */ while ((ret = msgpack_unpacker_next(con->mpac, &result)) == MSGPACK_UNPACK_SUCCESS) { - if (message_is_request(&result.data)) - connection_handle_request(con, &result.data); - else if (message_is_response(&result.data)) { + bool is_response = is_rpc_response(&result.data); + + if (is_response) { if (is_valid_rpc_response(&result.data, con)) { connection_handle_response(con, &result.data); } else { @@ -387,35 +515,77 @@ STATIC int parse_cb(inputstream *istream, void *data, bool eof) "request id. Ensure the client is properly " "synchronized"); } - } else { - LOG_WARNING("invalid msgpack object"); - msgpack_object_print(stdout, result.data); + + msgpack_unpacked_destroy(&result); + goto end; } + + connection_handle_request(con, &result.data); } - decref(con); + if (ret == MSGPACK_UNPACK_NOMEM_ERROR) { + decref(con); + exit(2); + } - return (0); + if (ret == MSGPACK_UNPACK_PARSE_ERROR) { + send_error(con, 0, "Invalid msgpack payload. " + "This error can also happen when deserializing " + "an object with high level of nesting"); + } -fail: +end: decref(con); - return (-1); } -struct callinfo connection_send_request(char *pluginkey, string method, - array params, struct api_error *api_error) +bool connection_send_event(uint64_t id, char *name, array args) +{ + msgpack_packer packer; + struct connection *con = NULL; + + if (id && (!(con = hashmap_get(uint64_t, ptr_t)(connections, id)) + || con->closed)) { + api_free_array(args); + return false; + } + + if (con) { + string method = cstring_to_string(name); + msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write); + msgpack_rpc_serialize_request(0, method, args, &packer); + api_free_array(args); + + if (con->pending_requests) { + wbuffer *rv = malloc_or_die(sizeof(wbuffer)); + rv->size = sbuf.size; + rv->data = sb_memdup_nulterm(sbuf.data, sbuf.size); + kv_push(con->delayed_notifications, rv); + } else { + crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write); + } + + msgpack_sbuffer_clear(&sbuf); + } else { + broadcast_event(name, args); + } + + return true; +} + + +object connection_send_request(char *pluginkey, string method, + array args, struct api_error *err) { uint64_t id; struct connection *con; msgpack_packer packer; - struct message_request request; id = hashmap_get(cstr_t, uint64_t)(pluginkeys, pluginkey); if (id == 0) { - free_params(params); - error_set(api_error, API_ERROR_TYPE_VALIDATION, "plugin not registered"); - return CALLINFO_INIT; + api_free_array(args); + error_set(err, API_ERROR_TYPE_VALIDATION, "plugin not registered"); + return NIL; } con = hashmap_get(uint64_t, ptr_t)(connections, id); @@ -425,44 +595,80 @@ struct callinfo connection_send_request(char *pluginkey, string method, * the initial connection from the sender. */ if (!con) { - free_params(params); - error_set(api_error, API_ERROR_TYPE_VALIDATION, "plugin not registered"); - return CALLINFO_INIT; + api_free_array(args); + error_set(err, API_ERROR_TYPE_VALIDATION, "plugin not registered"); + return NIL; } incref(con); - request.msgid = con->msgid++; - request.method = method; - request.params = params; + + uint64_t msgid = con->msgid++; msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write); - message_serialize_request(&request, &packer); - free_params(params); + msgpack_rpc_serialize_request(msgid, method, args, &packer); + + api_free_array(args); LOG_VERBOSE(VERBOSE_LEVEL_0, "sending request: method = %s, callinfo id = %u\n", - method, request.msgid); + method.str, con->msgid); if (crypto_write(&con->cc, sbuf.data, sbuf.size, con->streams.write) != 0) - return CALLINFO_INIT; + return NIL; + + msgpack_sbuffer_clear(&sbuf); - struct callinfo cinfo = (struct callinfo) {request.msgid, false, false,((struct message_response) {0, ARRAY_INIT})}; + struct callinfo cinfo = (struct callinfo) { msgid, false, false, NIL }; - loop_wait_for_response(con, &cinfo); + loop_process_events_until(&main_loop, con, &cinfo); - msgpack_sbuffer_clear(&sbuf); + if (cinfo.errored) { + if (cinfo.result.type == OBJECT_TYPE_STR) { + error_set(err, API_ERROR_TYPE_EXCEPTION, "%s", + cinfo.result.data.string.str); + } else if (cinfo.result.type == OBJECT_TYPE_ARRAY) { + array array = cinfo.result.data.array; + + if (array.size == 2 && array.items[0].type == OBJECT_TYPE_INT + && (array.items[0].data.integer == API_ERROR_TYPE_EXCEPTION + || array.items[0].data.integer == API_ERROR_TYPE_VALIDATION) + && array.items[1].type == OBJECT_TYPE_STR) { + err->type = (api_error_type) array.items[0].data.integer; + strlcpy(err->msg, array.items[1].data.string.str, sizeof(err->msg)); + err->isset = true; + } else { + error_set(err, API_ERROR_TYPE_EXCEPTION, "%s", "unknown error"); + } + } else { + error_set(err, API_ERROR_TYPE_EXCEPTION, "%s", "unknown error"); + } + + api_free_object(cinfo.result); + } + + if (!con->pending_requests) { + send_delayed_notifications(con); + } decref(con); - if (cinfo.errorresponse) - return CALLINFO_INIT; + return cinfo.errored ? NIL : cinfo.result; +} + +STATIC void send_delayed_notifications(struct connection *con) +{ + for (size_t i = 0; i < kv_size(con->delayed_notifications); i++) { + wbuffer *buffer = kv_A(con->delayed_notifications, i); + crypto_write(&con->cc, buffer->data, buffer->size, con->streams.write); + FREE(buffer->data); + FREE(buffer); + } - return cinfo; + kv_size(con->delayed_notifications) = 0; } int connection_send_response(uint64_t con_id, uint32_t msgid, - array params, struct api_error *api_error) + object arg, struct api_error *api_error) { msgpack_packer packer; - struct message_response response; struct connection *con; con = hashmap_get(uint64_t, ptr_t)(connections, con_id); @@ -476,11 +682,8 @@ int connection_send_response(uint64_t con_id, uint32_t msgid, return (-1); } - response.msgid = msgid; - response.params = params; - msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write); - message_serialize_response(&response, &packer); + msgpack_rpc_serialize_response(msgid, api_error, arg, &packer); if (api_error->isset) { return (-1); @@ -491,91 +694,92 @@ int connection_send_response(uint64_t con_id, uint32_t msgid, } msgpack_sbuffer_clear(&sbuf); - free_params(params); + api_free_object(arg); return 0; } -STATIC int connection_handle_request(struct connection *con, +STATIC void connection_handle_request(struct connection *con, msgpack_object *obj) { + array args = ARRAY_DICT_INIT; + uint64_t msgid; dispatch_info dispatcher; + msgpack_object *method; struct api_error api_error = ERROR_INIT; - connection_request_event_info eventinfo; - api_event event; - if (!obj || !con) - return (-1); + msgpack_rpc_validate(&msgid, obj, &api_error); - if (message_deserialize_request(&eventinfo.request, obj, &api_error) != 0) { - /* request wasn't parsed correctly, send error with pseudo RESPONSE ID*/ - eventinfo.request.msgid = MESSAGE_RESPONSE_UNKNOWN; - eventinfo.request.method = cstring_copy_string("error"); + if (api_error.isset) { + send_error(con, msgid, "Invalid message from connection, closed"); } - LOG_VERBOSE(VERBOSE_LEVEL_0, "received request: method = %s\n", - eventinfo.request.method.str); + method = msgpack_rpc_method(obj); - dispatcher = dispatch_table_get(eventinfo.request.method); + if (method) { + dispatcher = msgpack_rpc_get_handler_for(method->via.bin.ptr, + method->via.bin.size); + } else { + dispatcher.func = msgpack_rpc_handle_missing_method; + dispatcher.async = true; + } - if (dispatcher.func == NULL) { - LOG_VERBOSE(VERBOSE_LEVEL_0, "could not dispatch method\n"); - error_set(&api_error, API_ERROR_TYPE_VALIDATION, "could not dispatch method"); - dispatcher.func = handle_error; + if (!msgpack_rpc_to_array(msgpack_rpc_args(obj), &args)) { + dispatcher.func = msgpack_rpc_handle_invalid_arguments; dispatcher.async = true; } - eventinfo.con = con; - eventinfo.api_error = api_error; - eventinfo.dispatcher = dispatcher; + connection_request_event_info *eventinfo = malloc_or_die(sizeof(connection_request_event_info)); + eventinfo->con = con; + eventinfo->dispatcher = dispatcher; + eventinfo->args = args; + eventinfo->msgid = msgid; incref(con); if (dispatcher.async) - connection_request_event(&eventinfo); + connection_request_event((void**)&eventinfo); else { - event.handler = connection_request_event; - event.info = eventinfo; - equeue_put(con->queue, event); - /* TODO: move this call to a suitable place (main?) */ - equeue_run_events(equeue_root); + multiqueue_put(con->events, connection_request_event, 1, eventinfo); } - - return (0); } -STATIC void connection_request_event(connection_request_event_info *eventinfo) +STATIC void connection_request_event(void **argv) { + connection_request_event_info *eventinfo = argv[0]; + object result; msgpack_packer packer; struct connection *con; + struct api_error error = ERROR_INIT; con = eventinfo->con; + array args = eventinfo->args; + uint64_t msgid = eventinfo->msgid; + dispatch_info handler = eventinfo->dispatcher; - eventinfo->dispatcher.func(con->id, &eventinfo->request, - con->cc.pluginkeystring, &eventinfo->api_error); + result = handler.func(con->id, msgid, con->cc.pluginkeystring, args, &error); - if (eventinfo->api_error.isset) { + if (eventinfo->msgid != UINT64_MAX) { msgpack_packer_init(&packer, &sbuf, msgpack_sbuffer_write); - message_serialize_error_response(&packer, &eventinfo->api_error, - eventinfo->request.msgid); - + msgpack_rpc_serialize_response(msgid, &error, result, &packer); crypto_write(&eventinfo->con->cc, sbuf.data, sbuf.size, eventinfo->con->streams.write); - msgpack_sbuffer_clear(&sbuf); + } else { + api_free_object(result); } - free_params(eventinfo->request.params); - free_string(eventinfo->request.method); + api_free_array(args); decref(con); + FREE(eventinfo); } STATIC int is_valid_rpc_response(msgpack_object *obj, struct connection *con) { - uint64_t msg_id = message_get_id(obj); + uint64_t msg_id = obj->via.array.ptr[1].via.u64; return kv_size(con->callvector) && msg_id == kv_A(con->callvector, kv_size(con->callvector) - 1)->msgid; @@ -586,20 +790,19 @@ STATIC void connection_handle_response(struct connection *con, msgpack_object *obj) { struct callinfo *cinfo; - struct api_error api_error = { .isset = false }; cinfo = kv_A(con->callvector, kv_size(con->callvector) - 1); - LOG_VERBOSE(VERBOSE_LEVEL_0, "received response: callinfo id = %u\n", + LOG_VERBOSE(VERBOSE_LEVEL_0, "received response: callinfo id = %lu\n", cinfo->msgid); - cinfo->hasresponse = true; - cinfo->errorresponse = message_is_error_response(obj); + cinfo->returned = true; + cinfo->errored = (obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL); - if (cinfo->errorresponse) { - message_deserialize_error_response(&cinfo->response, obj, &api_error); + if (cinfo->errored) { + msgpack_rpc_to_object(&obj->via.array.ptr[2], &cinfo->result); } else { - message_deserialize_response(&cinfo->response, obj, &api_error); + msgpack_rpc_to_object(&obj->via.array.ptr[3], &cinfo->result); } } @@ -609,8 +812,8 @@ STATIC void call_set_error(struct connection *con, UNUSED(char *msg)) for (size_t i = 0; i < kv_size(con->callvector); i++) { cinfo = kv_A(con->callvector, i); - cinfo->errorresponse = true; - cinfo->hasresponse = true; + cinfo->errored = true; + cinfo->returned = true; } connection_close(con); diff --git a/src/rpc/connection/connection.h b/src/rpc/connection/connection.h index 9d0a414..4a8b485 100644 --- a/src/rpc/connection/connection.h +++ b/src/rpc/connection/connection.h @@ -16,15 +16,42 @@ #pragma once -#include "rpc/sb-rpc.h" +#include // for msgpack_sbuffer +#include // for msgpack_unpacker +#include // for bool +#include // for size_t +#include // for uint64_t, uint32_t +#include // for uv_stream_t, uv_timer_t +#include "kvec.h" // for kvec_t +#include "rpc/connection/event.h" // for multiqueue +#include "rpc/sb-rpc.h" // for crypto_context, hashmap_cstr_t_ptr_t +#include "sb-common.h" // for hashmap -STATIC void close_cb(uv_handle_t *handle); -STATIC int connection_handle_request(struct connection *con, - msgpack_object *obj); -STATIC void connection_handle_response(struct connection *con, - msgpack_object *obj); -STATIC void connection_request_event(connection_request_event_info *info); -STATIC void connection_close(struct connection *con); -STATIC int parse_cb(inputstream *istream, void *data, bool eof); -STATIC int is_valid_rpc_response(msgpack_object *obj, struct connection *con); -STATIC void call_set_error(struct connection *con, char *msg); +struct connection { + uint64_t id; + uint32_t msgid; + size_t pending_requests; + size_t refcount; + msgpack_unpacker *mpac; + msgpack_sbuffer *sbuf; + char *unpackbuf; + bool closed; + multiqueue *events; + struct { + inputstream *read; + outputstream *write; + uv_stream_t *uv; + } streams; + kvec_t(struct callinfo *) callvector; + kvec_t(wbuffer *) delayed_notifications; + struct crypto_context cc; + struct { + uint64_t start; + uint64_t end; + uint64_t pos; + uint64_t length; + unsigned char *data; + } packet; + uv_timer_t minutekey_timer; + hashmap(cstr_t, ptr_t) *subscribed_events; +}; diff --git a/src/rpc/connection/crypto.c b/src/rpc/connection/crypto.c index f45c1b5..8562496 100644 --- a/src/rpc/connection/crypto.c +++ b/src/rpc/connection/crypto.c @@ -1,10 +1,12 @@ -#include -#include -#include "sb-common.h" -#include "rpc/sb-rpc.h" -#include "rpc/db/sb-db.h" #include "rpc/connection/crypto.h" -#include "tweetnacl.h" +#include // for uint64_t +#include // for exit +#include // for memcpy, NULL, size_t +#include // for close +#include "rpc/db/sb-db.h" // for db_authorized_verify, db_authorized_whitel... +#include "rpc/sb-rpc.h" // for crypto_context, outputstream_write, output... +#include "sb-common.h" // for sbmemzero, sbassert, FREE, ISODD, STATIC +#include "tweetnacl.h" // for crypto_box_NONCEBYTES, crypto_box_open_aft... #define CRYPTO_PREFIX_SPLONEBOXCLIENT "splonebox-client" #define CRYPTO_PREFIX_SPLONEBOXSERVER "splonebox-server" @@ -498,10 +500,7 @@ int crypto_write(struct crypto_context *cc, char *data, * (crypto_box_ZEROBYTES) */ packetlen = length + 56; - packet = MALLOC_ARRAY(packetlen, unsigned char); - - if (packet == NULL) - return -1; + packet = malloc_array_or_die(packetlen, sizeof(unsigned char)); /* update nonce */ nonce_update(cc); @@ -525,11 +524,8 @@ int crypto_write(struct crypto_context *cc, char *data, memcpy(packet + 16, lengthbox + 16, 24); blocklen = length + 32; - block = CALLOC(blocklen, unsigned char); - ciphertext = MALLOC_ARRAY(blocklen, unsigned char); - - if ((block == NULL) || (ciphertext == NULL)) - return -1; + block = calloc_or_die(blocklen, sizeof(unsigned char)); + ciphertext = malloc_array_or_die(blocklen, sizeof(unsigned char)); memcpy(block + 32, data, length); @@ -591,11 +587,8 @@ int crypto_read(struct crypto_context *cc, unsigned char *in, char *out, /* ciphertextlen = length - 8 (id) - 72 (length) - 8 (nonce) + 16 (padding) */ ciphertextlen = length - 24; - block = MALLOC_ARRAY(ciphertextlen, unsigned char); - ciphertextpadded = CALLOC(ciphertextlen, unsigned char); - - if ((block == NULL) || (ciphertextpadded == NULL)) - return -1; + block = malloc_array_or_die(ciphertextlen, sizeof(unsigned char)); + ciphertextpadded = calloc_or_die(ciphertextlen, sizeof(unsigned char)); memcpy(ciphertextpadded + 16, in + 40, blocklen); diff --git a/src/rpc/connection/dispatch.c b/src/rpc/connection/dispatch.c index ce6bdd1..65b90df 100644 --- a/src/rpc/connection/dispatch.c +++ b/src/rpc/connection/dispatch.c @@ -30,20 +30,36 @@ * limitations under the License. */ -#include - -#include "rpc/sb-rpc.h" -#include "api/sb-api.h" -#include "sb-common.h" +#include // for msgpack_sbuffer_init, msgpack_sbuffer +#include // for false, true +#include // for uint64_t +#include // for snprintf +#include // for NULL, size_t +#include "api/helpers.h" // for ARRAY_OBJ, ADD, NIL, UINTEGER_OBJ +#include "api/sb-api.h" // for api_broadcast, api_register, api_result +#include "rpc/sb-rpc.h" // for object, array, object::(anonymous), dis... +#include "sb-common.h" // for ::API_ERROR_TYPE_VALIDATION, error_set static msgpack_sbuffer sbuf; static hashmap(string, dispatch_info) *dispatch_table = NULL; static hashmap(uint64_t, ptr_t) *callids = NULL; -int handle_error(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error) +object msgpack_rpc_handle_missing_method(UNUSED(uint64_t channel_id), + UNUSED(uint64_t msgid), UNUSED(char *pluginkey), UNUSED(array args), + struct api_error *error) { - return (0); + snprintf(error->msg, sizeof(error->msg), "Invalid method name"); + error->isset = true; + return NIL; +} + +object msgpack_rpc_handle_invalid_arguments(UNUSED(uint64_t channel_id), + UNUSED(uint64_t msgid), UNUSED(char *pluginkey), UNUSED(array args), + struct api_error *error) +{ + snprintf(error->msg, sizeof(error->msg), "Invalid method arguments"); + error->isset = true; + return NIL; } /* @@ -53,29 +69,31 @@ int handle_error(uint64_t con_id, struct message_request *request, * @param api_error `struct api_error` error object-instance * @return 0 if success, -1 otherwise */ -int handle_register(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error) +object handle_register(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid), + char *pluginkey, array args, struct api_error *error) { - array *meta = NULL; - array functions; + array rv = ARRAY_DICT_INIT; + object ret = ARRAY_OBJ(rv); + array meta = ARRAY_DICT_INIT; + array functions = ARRAY_DICT_INIT; string name, description, author, license; - if (!error || !request) - return (-1); + if (!error) + goto end; /* check params size */ - if (request->params.size != 2) { + if (args.size != 2) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching register API request. Invalid params size"); - return (-1); + goto end; } - if (request->params.obj[0].type == OBJECT_TYPE_ARRAY) - meta = &request->params.obj[0].data.params; + if (args.items[0].type == OBJECT_TYPE_ARRAY) + meta = args.items[0].data.array; else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching register API request. meta params has wrong type"); - return (-1); + goto end; } /* @@ -83,240 +101,347 @@ int handle_register(uint64_t con_id, struct message_request *request, * [name, description, author, license] */ - if (!meta) { - error_set(error, API_ERROR_TYPE_VALIDATION, - "Error dispatching register API request. meta params is NULL"); - return (-1); - } - - if (meta->size != 4) { + if (meta.size != 4) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching register API request. Invalid meta params size"); - return (-1); + goto end; } /* extract meta information */ - if ((meta->obj[0].type != OBJECT_TYPE_STR) || - (meta->obj[1].type != OBJECT_TYPE_STR) || - (meta->obj[2].type != OBJECT_TYPE_STR) || - (meta->obj[3].type != OBJECT_TYPE_STR)) { + if ((meta.items[0].type != OBJECT_TYPE_STR) || + (meta.items[1].type != OBJECT_TYPE_STR) || + (meta.items[2].type != OBJECT_TYPE_STR) || + (meta.items[3].type != OBJECT_TYPE_STR)) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching register API request. meta element has wrong type"); - return (-1); + goto end; } - if (!meta->obj[0].data.string.str || !meta->obj[1].data.string.str || - !meta->obj[2].data.string.str || !meta->obj[3].data.string.str) { + if (!meta.items[0].data.string.str || !meta.items[1].data.string.str || + !meta.items[2].data.string.str || !meta.items[3].data.string.str) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching register API request. Invalid meta params size"); - return (-1); + goto end; } - name = meta->obj[0].data.string; - description = meta->obj[1].data.string; - author = meta->obj[2].data.string; - license = meta->obj[3].data.string; + name = meta.items[0].data.string; + description = meta.items[1].data.string; + author = meta.items[2].data.string; + license = meta.items[3].data.string; - if (request->params.obj[1].type != OBJECT_TYPE_ARRAY) { + if (args.items[1].type == OBJECT_TYPE_ARRAY) { + functions = args.items[1].data.array; + } else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching register API request. functions has wrong type"); - return (-1); + goto end; } - functions = request->params.obj[1].data.params; - - if (api_register(name, description, author, license, - functions, con_id, request->msgid, pluginkey, error) == -1) { + if (api_register(name, description, author, license, functions, pluginkey, + error) == -1) { if (!error->isset) error_set(error, API_ERROR_TYPE_VALIDATION, "Error running register API request."); - return (-1); + goto end; } - return (0); +end: + return ret; } -int handle_run(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error) +object handle_run(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid), + char *pluginkey, array args, struct api_error *error) { + array rv = ARRAY_DICT_INIT; + array meta = ARRAY_DICT_INIT; + array runargs = ARRAY_DICT_INIT; + string function_name = STRING_INIT; + object ret = ARRAY_OBJ(rv); uint64_t callid; - array *meta = NULL; - string function_name; char *targetpluginkey; - struct message_object args_object; - - if (!error || !request) - return (-1); + if (!error) + goto end; /* check params size */ - if (request->params.size != 3) { + if (args.size != 3) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. Invalid params size"); - return (-1); + goto end; } - if (request->params.obj[0].type == OBJECT_TYPE_ARRAY) - meta = &request->params.obj[0].data.params; + if (args.items[0].type == OBJECT_TYPE_ARRAY) + meta = args.items[0].data.array; else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. meta params has wrong type"); - return (-1); - } - - if (!meta) { - error_set(error, API_ERROR_TYPE_VALIDATION, - "Error dispatching run API request. meta params is NULL"); - return (-1); + goto end; } /* meta = [targetpluginkey, nil]*/ - if (meta->size != 2) { + if (meta.size != 2) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. Invalid meta params size"); - return (-1); + goto end; } /* extract meta information */ - if (meta->obj[0].type != OBJECT_TYPE_STR) { + if (meta.items[0].type != OBJECT_TYPE_STR) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. meta elements have wrong type"); - return (-1); + goto end; } - if (!meta->obj[0].data.string.str || - meta->obj[0].data.string.length+1 != PLUGINKEY_STRING_SIZE) { + if (!meta.items[0].data.string.str || + meta.items[0].data.string.length + 1 != PLUGINKEY_STRING_SIZE) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. Invalid meta params size"); - return (-1); + goto end; } - targetpluginkey = meta->obj[0].data.string.str; + targetpluginkey = meta.items[0].data.string.str; to_upper(targetpluginkey); - if (meta->obj[1].type != OBJECT_TYPE_NIL) { + if (meta.items[1].type != OBJECT_TYPE_NIL) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. meta elements have wrong type"); - return (-1); + goto end; } - if (request->params.obj[1].type != OBJECT_TYPE_STR) { + if (args.items[1].type == OBJECT_TYPE_STR) { + function_name = args.items[1].data.string; + } else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. function string has wrong type"); - return (-1); - } - - if (!request->params.obj[1].data.string.str) { - error_set(error, API_ERROR_TYPE_VALIDATION, - "Error dispatching register API request. Invalid meta params size"); - return (-1); + goto end; } - function_name = request->params.obj[1].data.string; - - if (request->params.obj[2].type != OBJECT_TYPE_ARRAY) { + if (args.items[2].type == OBJECT_TYPE_ARRAY) { + runargs = args.items[2].data.array; + } else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching run API request. function string has wrong type"); - return (-1); + goto end; } - args_object = request->params.obj[2]; callid = (uint64_t) randommod(281474976710656LL); LOG_VERBOSE(VERBOSE_LEVEL_1, "generated callid %lu\n", callid); hashmap_put(uint64_t, ptr_t)(callids, callid, pluginkey); - if (api_run(targetpluginkey, function_name, callid, args_object, con_id, - request->msgid, error) == -1) { + if (api_run(targetpluginkey, function_name, callid, runargs, error) == -1) { if (false == error->isset) error_set(error, API_ERROR_TYPE_VALIDATION, "Error executing run API request."); - return (-1); + goto end; } - return (0); + ADD(rv, UINTEGER_OBJ(callid)); + ret = ARRAY_OBJ(rv); + +end: + return ret; } -int handle_result(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error) +object handle_result(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid), + UNUSED(char *pluginkey), array args, struct api_error *error) { + array rv = ARRAY_DICT_INIT; + array meta = ARRAY_DICT_INIT; + array resultargs = ARRAY_DICT_INIT; + object ret = ARRAY_OBJ(rv); uint64_t callid; - array *meta = NULL; - struct message_object args_object; char * targetpluginkey; - if (!error || !request) - return (-1); + if (!error) + goto end; /* check params size */ - if (request->params.size != 2) { + if (args.size != 2) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching result API request. Invalid params size"); - return (-1); + goto end; } - if (request->params.obj[0].type == OBJECT_TYPE_ARRAY) - meta = &request->params.obj[0].data.params; + if (args.items[0].type == OBJECT_TYPE_ARRAY) + meta = args.items[0].data.array; else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching result API request. meta params has wrong type"); - return (-1); - } - - if (!meta) { - error_set(error, API_ERROR_TYPE_VALIDATION, - "Error dispatching result API request. meta params is NULL"); - return (-1); + goto end; } /* meta = [callid]*/ - if (meta->size != 1) { + if (meta.size != 1) { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching result API request. Invalid meta params size"); - return (-1); + goto end; } /* extract meta information */ - if (meta->obj[0].type != OBJECT_TYPE_UINT) { + if (meta.items[0].type == OBJECT_TYPE_UINT) { + callid = meta.items[0].data.uinteger; + } else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching result API request. meta elements have wrong type"); - return (-1); + goto end; } - callid = meta->obj[0].data.uinteger; - - if (request->params.obj[1].type != OBJECT_TYPE_ARRAY) { + if (args.items[1].type == OBJECT_TYPE_ARRAY) { + resultargs = args.items[1].data.array; + } else { error_set(error, API_ERROR_TYPE_VALIDATION, "Error dispatching result API request. function string has wrong type"); - return (-1); + goto end; } - args_object = request->params.obj[1]; - targetpluginkey = hashmap_get(uint64_t, ptr_t)(callids, callid); if (!targetpluginkey) { error_set(error, API_ERROR_TYPE_VALIDATION, "Failed to find target's key associated with given callid."); - return (-1); + goto end; } - if (api_result(targetpluginkey, callid, args_object, con_id, request->msgid, - error) == -1) { + if (api_result(targetpluginkey, callid, resultargs, error) == -1) { if (false == error->isset) error_set(error, API_ERROR_TYPE_VALIDATION, "Error executing result API request."); - return (-1); + goto end; } hashmap_del(uint64_t, ptr_t)(callids, callid); - return (0); + ADD(rv, UINTEGER_OBJ(callid)); + ret = ARRAY_OBJ(rv); + +end: + return ret; +} + + +object handle_broadcast(UNUSED(uint64_t con_id), UNUSED(uint64_t msgid), + UNUSED(char *pluginkey), array args, struct api_error *error) +{ + array rv = ARRAY_DICT_INIT; + array eventargs = ARRAY_DICT_INIT; + object ret = ARRAY_OBJ(rv); + string eventname = STRING_INIT; + + if (!error) + goto end; + + /* check params size */ + if (args.size != 2) { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. Invalid params size"); + goto end; + } + + if (args.items[0].type == OBJECT_TYPE_STR) + eventname = args.items[0].data.string; + else { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. event name has wrong type"); + goto end; + } + + if (args.items[1].type == OBJECT_TYPE_ARRAY) + eventargs = args.items[1].data.array; + else { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. event args has wrong type"); + goto end; + } + + if (api_broadcast(eventname, eventargs, error) == -1) { + if (false == error->isset) + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error executing broadcast API request."); + goto end; + } + +end: + return ret; } + +object handle_subscribe(uint64_t con_id, UNUSED(uint64_t msgid), + UNUSED(char *pluginkey), array args, struct api_error *error) +{ + array rv = ARRAY_DICT_INIT; + object ret = ARRAY_OBJ(rv); + string eventname = STRING_INIT; + + if (!error) + goto end; + + /* check params size */ + if (args.size != 1) { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. Invalid params size"); + goto end; + } + + if (args.items[0].type == OBJECT_TYPE_STR) + eventname = args.items[0].data.string; + else { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. event name has wrong type"); + goto end; + } + + if (api_subscribe(con_id, eventname, error) == -1) { + if (false == error->isset) + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error executing broadcast API request."); + goto end; + } + +end: + return ret; +} + +object handle_unsubscribe(uint64_t con_id, UNUSED(uint64_t msgid), + UNUSED(char *pluginkey), array args, struct api_error *error) +{ + array rv = ARRAY_DICT_INIT; + object ret = ARRAY_OBJ(rv); + string eventname = STRING_INIT; + + if (!error) + goto end; + + /* check params size */ + if (args.size != 1) { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. Invalid params size"); + goto end; + } + + if (args.items[0].type == OBJECT_TYPE_STR) + eventname = args.items[0].data.string; + else { + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error dispatching broadcast API request. event name has wrong type"); + goto end; + } + + if (api_unsubscribe(con_id, eventname, error) == -1) { + if (false == error->isset) + error_set(error, API_ERROR_TYPE_VALIDATION, + "Error executing broadcast API request."); + goto end; + } + +end: + return ret; +} + + void dispatch_table_put(string method, dispatch_info info) { hashmap_put(string, dispatch_info)(dispatch_table, method, info); @@ -337,17 +462,35 @@ int dispatch_teardown(void) return (0); } +dispatch_info msgpack_rpc_get_handler_for(const char *name, size_t name_len) +{ + string m = { .str = (char *)name, .length = name_len }; + dispatch_info rv = + hashmap_get(string, dispatch_info)(dispatch_table, m); + + if (!rv.func) { + rv.func = msgpack_rpc_handle_missing_method; + } + + return rv; +} + int dispatch_table_init(void) { - dispatch_info register_info = {.func = handle_register, .async = true, + dispatch_info register_info = {.func = handle_register, .async = false, .name = (string) {.str = "register", .length = sizeof("register") - 1}}; - dispatch_info run_info = {.func = handle_run, .async = true, + dispatch_info run_info = {.func = handle_run, .async = false, .name = (string) {.str = "run", .length = sizeof("run") - 1}}; - dispatch_info error_info = {.func = handle_error, .async = true, - .name = (string) {.str = "error", .length = sizeof("error") - 1}}; - dispatch_info result_info = {.func = handle_result, .async = true, + dispatch_info result_info = {.func = handle_result, .async = false, .name = (string) {.str = "result", .length = sizeof("result") - 1,}}; + dispatch_info broadcast_info = {.func = handle_broadcast, .async = true, + .name = (string) {.str = "broadcast", .length = sizeof("broadcast") - 1,}}; + dispatch_info subscribe_info = {.func = handle_subscribe, .async = false, + .name = (string) {.str = "subscribe", .length = sizeof("subscribe") - 1,}}; + dispatch_info unsubscribe_info = {.func = handle_unsubscribe, .async = false, + .name = (string) {.str = "unsubscribe", .length = sizeof("unsubscribe") - 1,}}; + msgpack_sbuffer_init(&sbuf); @@ -359,9 +502,10 @@ int dispatch_table_init(void) dispatch_table_put(register_info.name, register_info); dispatch_table_put(run_info.name, run_info); - dispatch_table_put(error_info.name, error_info); dispatch_table_put(result_info.name, result_info); - + dispatch_table_put(broadcast_info.name, broadcast_info); + dispatch_table_put(subscribe_info.name, subscribe_info); + dispatch_table_put(unsubscribe_info.name, unsubscribe_info); return (0); } diff --git a/src/rpc/connection/event-defs.h b/src/rpc/connection/event-defs.h new file mode 100644 index 0000000..1e5b0de --- /dev/null +++ b/src/rpc/connection/event-defs.h @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2015 splone UG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * This file incorporates code covered by the following terms: + * + * Copyright Neovim contributors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include "sb-common.h" + +#define EVENT_HANDLER_MAX_ARGC 6 + +typedef void (*argv_callback)(void **argv); +typedef struct message { + int priority; + argv_callback handler; + void *argv[EVENT_HANDLER_MAX_ARGC]; +} event; + +typedef void(*event_scheduler)(event e, void *data); + +#define VA_EVENT_INIT(event, p, h, a) \ + do { \ + sbassert(a <= EVENT_HANDLER_MAX_ARGC); \ + (event)->priority = p; \ + (event)->handler = h; \ + if (a) { \ + va_list args; \ + va_start(args, a); \ + for (int i = 0; i < a; i++) { \ + (event)->argv[i] = va_arg(args, void *); \ + } \ + va_end(args); \ + } \ + } while (0) + +static inline event event_create(int priority, argv_callback cb, int argc, ...) +{ + sbassert(argc <= EVENT_HANDLER_MAX_ARGC); + event e; + VA_EVENT_INIT(&e, priority, cb, argc); + return e; +} diff --git a/src/rpc/connection/event.c b/src/rpc/connection/event.c index 6d997d3..8a4fa33 100644 --- a/src/rpc/connection/event.c +++ b/src/rpc/connection/event.c @@ -29,156 +29,164 @@ * limitations under the License. */ -#include - -#include "sb-common.h" -#include "rpc/sb-rpc.h" #include "rpc/connection/event.h" - -equeue *equeue_root; - -int event_initialize(void) +#include // for bool, false, true +#include // for NULL +#include "queue.h" // for QUEUE_REMOVE, QUEUE, QUEUE_EMPTY, QUEUE_INSER... +#include "sb-common.h" // for sbassert, FREE, MALLOC + +typedef struct multiqueue_item multiqueueitem; + +struct multiqueue_item { + union { + multiqueue *queue; + struct { + event event; + multiqueueitem *parent; + } item; + } data; + bool link; // true: current item is just a link to a node in a child queue + QUEUE node; +}; + +struct multiqueue { + multiqueue *parent; + QUEUE headtail; + put_callback put_cb; + void *data; +}; + +static multiqueue *multiqueue_new(multiqueue *parent, put_callback put_cb, void *data); +static event multiqueue_remove(multiqueue *this); +static void multiqueue_push(multiqueue *this, event e); +static multiqueueitem *multiqueue_node_data(QUEUE *q); + +static event NILEVENT = { .handler = NULL, .argv = {NULL} }; + +multiqueue *multiqueue_new_parent(put_callback put_cb, void *data) { - /* initialize event root queue */ - equeue_root = equeue_new(NULL); - - if (!equeue_root) - return (-1); - - return 0; + return multiqueue_new(NULL, put_cb, data); } - -equeue *equeue_new(equeue *root) +multiqueue *multiqueue_new_child(multiqueue *parent) { - equeue *queue = MALLOC(equeue); - - if (!queue) - return (NULL); - - queue->root = root; - - TAILQ_INIT(&queue->head); - - return (queue); + sbassert(!parent->parent); + return multiqueue_new(parent, NULL, NULL); } - -/** - * check if queue is empty - * - * @params queue queue instance - */ -bool equeue_empty(equeue *queue) +static multiqueue *multiqueue_new(multiqueue *parent, put_callback put_cb, + void *data) { - return (TAILQ_EMPTY(&queue->head)); + multiqueue *rv = malloc_or_die(sizeof(multiqueue)); + QUEUE_INIT(&rv->headtail); + rv->parent = parent; + rv->put_cb = put_cb; + rv->data = data; + return rv; } - -int equeue_put(equeue *queue, api_event event) +void multiqueue_free(multiqueue *this) { - if (!queue) - return (-1); - - /* disallow `put` to root queue */ - if (!queue->root) - return (-1); - - queue_entry *entry = MALLOC(queue_entry); - entry->data.entry.event = event; - TAILQ_INSERT_TAIL(&queue->head, entry, node); - - entry->data.entry.root = MALLOC(queue_entry); - entry->data.entry.root->data.queue = queue; - TAILQ_INSERT_TAIL(&queue->root->head, entry->data.entry.root, node); + sbassert(this); + while (!QUEUE_EMPTY(&this->headtail)) { + QUEUE *q = QUEUE_HEAD(&this->headtail); + multiqueueitem *item = multiqueue_node_data(q); + if (this->parent) { + QUEUE_REMOVE(&item->data.item.parent->node); + FREE(item->data.item.parent); + } + QUEUE_REMOVE(q); + FREE(item); + } - return (0); + FREE(this); } - -api_event equeue_get(equeue *queue) +event multiqueue_get(multiqueue *this) { - api_event event; - queue_entry *entry; + return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this); +} - if (equeue_empty(queue)) { - event.handler = NULL; - return (event); +void multiqueue_put_event(multiqueue *this, event e) +{ + sbassert(this); + multiqueue_push(this, e); + if (this->parent && this->parent->put_cb) { + this->parent->put_cb(this->parent, this->parent->data); } +} - if (queue->root) { - entry = dequeue_child(queue); - } else { - entry = dequeue_child_from_root(queue); +void multiqueue_process_events(multiqueue *this) +{ + sbassert(this); + while (!multiqueue_empty(this)) { + event e = multiqueue_get(this); + if (e.handler) { + e.handler(e.argv); + } } - - event = entry->data.entry.event; - - FREE(entry); - - return (event); } - -STATIC queue_entry *dequeue_child(equeue *queue) +bool multiqueue_empty(multiqueue *this) { - queue_entry *entry; - - entry = TAILQ_FIRST(&queue->head); - TAILQ_REMOVE(&queue->head, entry, node); - TAILQ_REMOVE(&queue->root->head, entry->data.entry.root, node); - - FREE(entry->data.entry.root); - - return (entry); + sbassert(this); + return QUEUE_EMPTY(&this->headtail); } - -STATIC queue_entry *dequeue_child_from_root(equeue *queue) +void multiqueue_replace_parent(multiqueue *this, multiqueue *new_parent) { - queue_entry *entry, *centry; - equeue *cqueue; - - entry = TAILQ_FIRST(&queue->head); - TAILQ_REMOVE(&queue->head, entry, node); - cqueue = entry->data.queue; - centry = TAILQ_FIRST(&cqueue->head); - TAILQ_REMOVE(&cqueue->head, centry, node); - - FREE(entry); - - return (centry); + sbassert(multiqueue_empty(this)); + this->parent = new_parent; } - -void equeue_free(equeue *queue) +static event multiqueue_remove(multiqueue *this) { - queue_entry *entry; - - if (queue->root) { - while (!equeue_empty(queue)) { - entry = dequeue_child(queue); - FREE(entry); - } + sbassert(!multiqueue_empty(this)); + QUEUE *h = QUEUE_HEAD(&this->headtail); + QUEUE_REMOVE(h); + multiqueueitem *item = multiqueue_node_data(h); + event rv; + + if (item->link) { + sbassert(!this->parent); + // remove the next node in the linked queue + multiqueue *linked = item->data.queue; + sbassert(!multiqueue_empty(linked)); + multiqueueitem *child = + multiqueue_node_data(QUEUE_HEAD(&linked->headtail)); + QUEUE_REMOVE(&child->node); + rv = child->data.item.event; + FREE(child); } else { - while (!equeue_empty(queue)) { - entry = dequeue_child_from_root(queue); - FREE(entry); + if (this->parent) { + // remove the corresponding link node in the parent queue + QUEUE_REMOVE(&item->data.item.parent->node); + FREE(item->data.item.parent); } + rv = item->data.item.event; } - FREE(queue); + FREE(item); + return rv; } - -void equeue_run_events(equeue *queue) +static void multiqueue_push(multiqueue *this, event e) { - api_event event; - - while (!equeue_empty(queue)) { - event = equeue_get(queue); - - if (event.handler) - event.handler(&event.info); + multiqueueitem *item = malloc_or_die(sizeof(multiqueueitem)); + item->link = false; + item->data.item.event = e; + QUEUE_INSERT_TAIL(&this->headtail, &item->node); + + if (this->parent) { + // push link node to the parent queue + item->data.item.parent = malloc_or_die(sizeof(multiqueueitem)); + item->data.item.parent->link = true; + item->data.item.parent->data.queue = this; + QUEUE_INSERT_TAIL(&this->parent->headtail, &item->data.item.parent->node); } } + +static multiqueueitem *multiqueue_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, multiqueueitem, node); +} diff --git a/src/rpc/connection/event.h b/src/rpc/connection/event.h index 9b09f3a..a09351d 100644 --- a/src/rpc/connection/event.h +++ b/src/rpc/connection/event.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2016 splone UG + * Copyright (C) 2015 splone UG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, @@ -12,11 +12,41 @@ * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . + * + * This file incorporates code covered by the following terms: + * + * Copyright Neovim contributors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ #pragma once -#include "rpc/sb-rpc.h" +#include // for bool +#include "rpc/connection/event-defs.h" + + +typedef struct multiqueue multiqueue; +typedef void (*put_callback)(multiqueue *multiq, void *data); + +multiqueue *multiqueue_new_parent(put_callback put_cb, void *data); +multiqueue *multiqueue_new_child(multiqueue *parent); +void multiqueue_free(multiqueue *this); +event multiqueue_get(multiqueue *this); +void multiqueue_put_event(multiqueue *this, event e); +void multiqueue_process_events(multiqueue *this); +bool multiqueue_empty(multiqueue *this); +void multiqueue_replace_parent(multiqueue *this, multiqueue *new_parent); -STATIC queue_entry *dequeue_child(equeue *queue); -STATIC queue_entry *dequeue_child_from_root(equeue *queue); +#define multiqueue_put(q, h, ...) \ + multiqueue_put_event(q, event_create(1, h, __VA_ARGS__)); diff --git a/src/rpc/connection/inputstream.c b/src/rpc/connection/inputstream.c index 43df7e7..f05c443 100644 --- a/src/rpc/connection/inputstream.c +++ b/src/rpc/connection/inputstream.c @@ -42,10 +42,7 @@ inputstream *inputstream_new(inputstream_cb cb, uint32_t buffer_size, void *data) { - inputstream *rs = MALLOC(inputstream); - - if (rs == NULL) - return (NULL); + inputstream *rs = malloc_or_die(sizeof(inputstream)); rs->data = data; rs->size = 0; @@ -54,7 +51,7 @@ inputstream *inputstream_new(inputstream_cb cb, uint32_t buffer_size, rs->free_handle = false; /* initialize circular buffer */ - rs->circbuf_start = MALLOC_ARRAY(buffer_size, unsigned char); + rs->circbuf_start = malloc_array_or_die(buffer_size, sizeof(unsigned char)); rs->circbuf_read_pos = rs->circbuf_start; rs->circbuf_write_pos = rs->circbuf_start; rs->circbuf_end = rs->circbuf_start + buffer_size; diff --git a/src/rpc/connection/loop.c b/src/rpc/connection/loop.c index cd008fa..e5ca1e9 100644 --- a/src/rpc/connection/loop.c +++ b/src/rpc/connection/loop.c @@ -30,42 +30,117 @@ * limitations under the License. */ -#include +#include "rpc/connection/loop.h" +#include // for bool +#include // for NULL, abort +#include "rpc/sb-rpc.h" // for event -#include "rpc/sb-rpc.h" -#include "sb-common.h" +static void async_cb(uv_async_t *handle); +static void timer_cb(uv_timer_t *handle); -static inline void loop_poll_events_until(struct connection *con, - bool *condition); -equeue *equeue_root; -uv_loop_t loop; +void loop_init(UNUSED(loop *loop), UNUSED(void *data)) +{ + uv_loop_init(&loop->uv); + loop->recursive = 0; + loop->uv.data = loop; + loop->children = kl_init(WatcherPtr); + loop->children_stop_requests = 0; + loop->events = multiqueue_new_parent(loop_on_put, loop); + loop->fast_events = multiqueue_new_child(loop->events); + loop->thread_events = multiqueue_new_parent(NULL, NULL); + uv_mutex_init(&loop->mutex); + uv_async_init(&loop->uv, &loop->async, async_cb); + uv_signal_init(&loop->uv, &loop->children_watcher); + uv_timer_init(&loop->uv, &loop->children_kill_timer); + uv_timer_init(&loop->uv, &loop->poll_timer); +} -static inline void loop_poll_events_until(struct connection *con, - bool *condition) +void loop_poll_events(loop *loop, int ms) { - while (!*condition) { - if (con->queue && !equeue_empty(con->queue)) { - equeue_run_events(con->queue); - } else { - uv_run(&loop, UV_RUN_ONCE); - equeue_run_events(equeue_root); - } + if (loop->recursive++) { + abort(); } + + uv_run_mode mode = UV_RUN_ONCE; + + if (ms > 0) { + uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms); + } else if (ms == 0) { + mode = UV_RUN_NOWAIT; + } + + uv_run(&loop->uv, mode); + + if (ms > 0) { + uv_timer_stop(&loop->poll_timer); + } + + loop->recursive--; // Can re-enter uv_run now + multiqueue_process_events(loop->fast_events); } +// Schedule an event from another thread +void loop_schedule(loop *loop, event e) +{ + uv_mutex_lock(&loop->mutex); + multiqueue_put_event(loop->thread_events, e); + uv_async_send(&loop->async); + uv_mutex_unlock(&loop->mutex); +} -void loop_wait_for_response(struct connection *con, - struct callinfo *cinfo) +void loop_on_put(UNUSED(multiqueue *queue), void *data) { + loop *loop = data; + // Sometimes libuv will run pending callbacks(timer for example) before + // blocking for a poll. If this happens and the callback pushes a event to one + // of the queues, the event would only be processed after the poll + // returns(user hits a key for example). To avoid this scenario, we call + // uv_stop when a event is enqueued. + uv_stop(&loop->uv); +} +void loop_close(loop *loop, bool wait) +{ + uv_mutex_destroy(&loop->mutex); + uv_close((uv_handle_t *)&loop->children_watcher, NULL); + uv_close((uv_handle_t *)&loop->children_kill_timer, NULL); + uv_close((uv_handle_t *)&loop->poll_timer, NULL); + uv_close((uv_handle_t *)&loop->async, NULL); + do { + uv_run(&loop->uv, wait ? UV_RUN_DEFAULT : UV_RUN_NOWAIT); + } while (uv_loop_close(&loop->uv) && wait); + multiqueue_free(loop->fast_events); + multiqueue_free(loop->thread_events); + multiqueue_free(loop->events); + kl_destroy(WatcherPtr, loop->children); +} + +static void async_cb(uv_async_t *handle) +{ + loop *l = handle->loop->data; + uv_mutex_lock(&l->mutex); + while (!multiqueue_empty(l->thread_events)) { + event ev = multiqueue_get(l->thread_events); + multiqueue_put_event(l->fast_events, ev); + } + uv_mutex_unlock(&l->mutex); +} + +static void timer_cb(UNUSED(uv_timer_t *handle)) +{ +} + +void loop_process_events_until(loop *loop, struct connection *con, + struct callinfo *cinfo) +{ /* push callinfo to connection callinfo vector */ - kv_push(struct callinfo *, con->callvector, cinfo); - con->pendingcalls++; + kv_push(con->callvector, cinfo); + con->pending_requests++; /* wait until requestinfo returned, in time process events */ - loop_poll_events_until(con, &cinfo->hasresponse); + LOOP_PROCESS_EVENTS_UNTIL(loop, con->events, -1, cinfo->returned); /* delete last from callinfo vector */ - kv_pop(con->callvector); - con->pendingcalls--; + (void)kv_pop(con->callvector); + con->pending_requests--; } diff --git a/src/rpc/connection/loop.h b/src/rpc/connection/loop.h new file mode 100644 index 0000000..997f357 --- /dev/null +++ b/src/rpc/connection/loop.h @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2015 splone UG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * This file incorporates code covered by the following terms: + * + * Copyright Neovim contributors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + #pragma once + +#include // for bool +#include // for size_t +#include // for uint64_t +#include // for uv_mutex_t +#include // for uv_hrtime, uv_timer_t, uv_asy... +#include "klist.h" // for mp, kl, k, p, KLIST_INIT, kli... +#include "kvec.h" // for connection::(anonymous), kv_pop +#include "rpc/connection/connection.h" // for connection +#include "rpc/connection/event.h" // for multiqueue, multiqueue_empty +#include "rpc/connection/event-defs.h" +#include "rpc/sb-rpc.h" // for callinfo, event + + +typedef void * WatcherPtr; + +#define _noop(x) +KLIST_INIT(WatcherPtr, WatcherPtr, _noop) + +typedef struct loop { + uv_loop_t uv; + multiqueue *events, *fast_events, *thread_events; + klist_t(WatcherPtr) *children; + uv_signal_t children_watcher; + uv_timer_t children_kill_timer, poll_timer; + size_t children_stop_requests; + uv_async_t async; + uv_mutex_t mutex; + int recursive; +} loop; + +#define CREATE_EVENT(multiqueue, handler, argc, ...) \ + do { \ + if (multiqueue) { \ + multiqueue_put((multiqueue), (handler), argc, __VA_ARGS__); \ + } else { \ + void *argv[argc] = { __VA_ARGS__ }; \ + (handler)(argv); \ + } \ + } while (0) + +// Poll for events until a condition or timeout +#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \ + do { \ + int remaining = timeout; \ + uint64_t before = (remaining > 0) ? uv_hrtime() : 0; \ + while (!(condition)) { \ + LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \ + if (remaining == 0) { \ + break; \ + } else if (remaining > 0) { \ + uint64_t now = uv_hrtime(); \ + remaining -= (int) ((now - before) / 1000000); \ + before = now; \ + if (remaining <= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LOOP_PROCESS_EVENTS(loop, multiqueue, timeout) \ + do { \ + if (multiqueue && !multiqueue_empty(multiqueue)) { \ + multiqueue_process_events(multiqueue); \ + } else { \ + loop_poll_events(loop, timeout); \ + } \ + } while (0) + +void loop_init(loop *loop, void *data); +void loop_poll_events(loop *loop, int ms); +void loop_schedule(loop *loop, event e); +void loop_on_put(multiqueue *queue, void *data); +void loop_close(loop *loop, bool wait); +void loop_process_events_until(loop *loop, struct connection *con, + struct callinfo *cinfo); diff --git a/src/rpc/connection/outputstream.c b/src/rpc/connection/outputstream.c index a4a0b2e..03078aa 100644 --- a/src/rpc/connection/outputstream.c +++ b/src/rpc/connection/outputstream.c @@ -40,10 +40,7 @@ outputstream *outputstream_new(uint32_t maxmem) { - outputstream *ws = MALLOC(outputstream); - - if (ws == NULL) - return (NULL); + outputstream *ws = malloc_or_die(sizeof(outputstream)); ws->maxmem = maxmem; ws->stream = NULL; @@ -78,18 +75,12 @@ int outputstream_write(outputstream *ostream, char *buffer, size_t len) buf.base = buffer; buf.len = len; - data = MALLOC(struct write_request_data); - - if (data == NULL) - return (-1); + data = malloc_or_die(sizeof(struct write_request_data)); data->ostream = ostream; data->buffer = buffer; data->len = len; - req = MALLOC(uv_write_t); - - if (req == NULL) - return (-1); + req = malloc_or_die(sizeof(uv_write_t)); req->data = data; ostream->curmem += len; diff --git a/src/rpc/connection/server.c b/src/rpc/connection/server.c index 8d53cf8..f44956f 100644 --- a/src/rpc/connection/server.c +++ b/src/rpc/connection/server.c @@ -14,18 +14,21 @@ * along with this program. If not, see . */ -#include -#include +#include "rpc/connection/server.h" #ifdef __linux__ -#include +#include // for strlcpy #endif -#include - -#include "sb-common.h" -#include "rpc/sb-rpc.h" -#include "rpc/db/sb-db.h" -#include "rpc/connection/server.h" - +#include // for getnameinfo, NI_MAXHOST, NI_MAXSERV +#include // for sockaddr_in +#include // for NULL, size_t +#include // for uint16_t +#include // for sockaddr +#include "khash.h" // for __i, khint32_t +#include "main.h" // for main_loop +#include "rpc/connection/loop.h" // for loop +#include "rpc/db/sb-db.h" // for db_authorized_set_whitelist_all +#include "rpc/sb-rpc.h" // for hashmap_cstr_t_ptr_t, hashmap_cstr_... +#include "sb-common.h" // for fmt_addr, ::SERVER_TYPE_TCP, LOG_ERROR #define ADDRESS_MAX_SIZE 256 #define MAX_CONNECTIONS 16 @@ -48,8 +51,6 @@ struct server { static hashmap(cstr_t, ptr_t) *servers = NULL; -uv_loop_t loop; - int server_init(void) { servers = hashmap_new(cstr_t, ptr_t)(); @@ -71,22 +72,19 @@ int server_start_tcp(boxaddr *addr, uint16_t port) sbassert(addr); - server = MALLOC(struct server); + server = malloc_or_die(sizeof(struct server)); if (hashmap_has(cstr_t, ptr_t)(servers, fmt_addr(addr))) { LOG("Already listening on %s", fmt_addr(addr)); return (-1); } - if (server == NULL) - return (-1); - uv_stream_t *stream = NULL; box_addr_to_sockaddr(addr, port, &server->socket.tcp.addr, sizeof(struct sockaddr_in)); - uv_tcp_init(&loop, &server->socket.tcp.handle); + uv_tcp_init(&main_loop.uv, &server->socket.tcp.handle); result = uv_tcp_bind(&server->socket.tcp.handle, (const struct sockaddr *)&server->socket.tcp.addr, 0); @@ -120,16 +118,13 @@ int server_start_pipe(char *name) sbassert(name); - server = MALLOC(struct server); + server = malloc_or_die(sizeof(struct server)); if (hashmap_has(cstr_t, ptr_t)(servers, name)) { LOG("Already listening on %s", name); return (-1); } - if (server == NULL) - return (-1); - uv_stream_t *stream = NULL; if (strlcpy(server->socket.pipe.addr, name, sizeof(server->socket.pipe.addr)) @@ -138,7 +133,7 @@ int server_start_pipe(char *name) return (-1); } - uv_pipe_init(&loop, &server->socket.pipe.handle, 0); + uv_pipe_init(&main_loop.uv, &server->socket.pipe.handle, 0); result = uv_pipe_bind(&server->socket.pipe.handle, server->socket.pipe.addr); if (result) { @@ -214,15 +209,12 @@ STATIC void connection_cb(uv_stream_t *server_stream, int status) hbuflen = sizeof(hbuf); server = server_stream->data; - client = MALLOC(uv_stream_t); - - if (client == NULL) - return; + client = malloc_or_die(sizeof(uv_stream_t)); if (server->type == SERVER_TYPE_TCP) - uv_tcp_init(&loop, (uv_tcp_t *)client); + uv_tcp_init(&main_loop.uv, (uv_tcp_t *)client); else - uv_pipe_init(&loop, (uv_pipe_t *)client, 0); + uv_pipe_init(&main_loop.uv, (uv_pipe_t *)client, 0); result = uv_accept(server_stream, client); diff --git a/src/rpc/connection/server.h b/src/rpc/connection/server.h index e79eaa6..77c8590 100644 --- a/src/rpc/connection/server.h +++ b/src/rpc/connection/server.h @@ -16,7 +16,8 @@ #pragma once -#include "rpc/sb-rpc.h" +#include // for uv_handle_t, uv_stream_t +#include "sb-common.h" // for STATIC STATIC void connection_cb(uv_stream_t *server_stream, int status); STATIC void client_free_cb(uv_handle_t *handle); diff --git a/src/rpc/connection/streamhandle.c b/src/rpc/connection/streamhandle.c index b087adb..bb3478a 100644 --- a/src/rpc/connection/streamhandle.c +++ b/src/rpc/connection/streamhandle.c @@ -74,10 +74,7 @@ STATIC struct streamhandle *init_streamhandle(uv_handle_t *handle) struct streamhandle *hd; if (handle->data == NULL) { - hd = MALLOC(struct streamhandle); - - if (hd == NULL) - return (NULL); + hd = malloc_or_die(sizeof(struct streamhandle)); hd->istream = NULL; hd->ostream = NULL; diff --git a/src/rpc/db/function.c b/src/rpc/db/function.c index d5e5714..264ccfd 100644 --- a/src/rpc/db/function.c +++ b/src/rpc/db/function.c @@ -52,7 +52,7 @@ static int db_function_add_meta(char *pluginkey, string name, string desc) { redisReply *reply; - if (!rc) + if (!rc || !name.str || !desc.str) return (-1); reply = redisCommand(rc, "HSET %s:func:%s:meta desc %s", pluginkey, @@ -71,7 +71,7 @@ static int db_function_add_meta(char *pluginkey, string name, string desc) static int db_function_add_args(char *pluginkey, string name, - message_object_type type) + object_type type) { redisReply *reply; @@ -103,6 +103,9 @@ static int db_function_flush_args(char *pluginkey, string name) /* if start > end, the result will be an empty list (which causes * key to be removed) */ + printf("name: %s\n", name.str); + printf("pluginkey: %s\n", pluginkey); + reply = redisCommand(rc, "LTRIM %s:func:%s:args %i %i", pluginkey, name.str, start, end); @@ -122,13 +125,13 @@ static int db_function_flush_args(char *pluginkey, string name) int db_function_add(char *pluginkey, array *func) { - struct message_object *name_elem, *desc_elem, *args, *arg; + object *name_elem, *desc_elem, *args, *arg; string name, desc; if (!func || !rc || (func->size <= 2)) return (-1); - name_elem = &func->obj[0]; + name_elem = &func->items[0]; if (!name_elem) { LOG_WARNING("Illegal function name pointer."); @@ -144,7 +147,7 @@ int db_function_add(char *pluginkey, array *func) return (-1); } - desc_elem = &func->obj[1]; + desc_elem = &func->items[1]; if (!desc_elem) { LOG_WARNING("Illegal function description pointer."); @@ -164,12 +167,12 @@ int db_function_add(char *pluginkey, array *func) if (db_function_add_meta(pluginkey, name, desc) == -1) return (-1); - args = &func->obj[2]; + args = &func->items[2]; db_function_flush_args(pluginkey, name); - for (size_t i = 0; i < args->data.params.size; i++) { - arg = &args->data.params.obj[i]; + for (size_t i = 0; i < args->data.array.size; i++) { + arg = &args->data.array.items[i]; if (db_function_add_args(pluginkey, name, arg->type) == -1) { LOG_WARNING("Failed to add function arguments!"); @@ -284,15 +287,15 @@ static int db_function_typecheck(char *pluginkey, string name, return (-1); } - if(val == OBJECT_TYPE_INT && args->obj[k].type == OBJECT_TYPE_UINT){ + if(val == OBJECT_TYPE_INT && args->items[k].type == OBJECT_TYPE_UINT){ /* Any positive integer will be treated as an unsigned int * (see unpack/pack.c) and might be a valid signed integer */ - if(args->obj[k].data.uinteger > INT64_MAX){ + if(args->items[k].data.uinteger > INT64_MAX){ LOG_WARNING("run() function argument has wrong type."); freeReplyObject(reply); return (-1); } - } else if (val != args->obj[k].type){ + } else if (val != args->items[k].type){ LOG_WARNING("run() function argument has wrong type."); freeReplyObject(reply); return (-1); diff --git a/src/rpc/msgpack/helpers.c b/src/rpc/msgpack/helpers.c new file mode 100644 index 0000000..99c775c --- /dev/null +++ b/src/rpc/msgpack/helpers.c @@ -0,0 +1,496 @@ +#include "rpc/sb-rpc.h" +#include "api/helpers.h" +#include "rpc/msgpack/helpers.h" +#include "sb-common.h" + +STATIC bool msgpack_rpc_to_string(const msgpack_object *const obj, + string *const arg); +STATIC bool msgpack_rpc_is_notification(msgpack_object *req); +STATIC msgpack_object *msgpack_rpc_msg_id(msgpack_object *req); + +typedef struct { + const msgpack_object *mobj; + object *aobj; + bool container; + size_t idx; +} msgpack_to_api_object_stack_item; + +bool msgpack_rpc_to_object(const msgpack_object *const obj, object *const arg) +{ + bool ret = true; + kvec_t(msgpack_to_api_object_stack_item) stack = KV_INITIAL_VALUE; + kv_push(stack, ((msgpack_to_api_object_stack_item) { obj, arg, false, 0 })); + + while (ret && kv_size(stack)) { + msgpack_to_api_object_stack_item cur = kv_last(stack); + + if (!cur.container) { + *cur.aobj = NIL; + } + + switch (cur.mobj->type) { + case MSGPACK_OBJECT_NIL: { + break; + } + case MSGPACK_OBJECT_BOOLEAN: { + *cur.aobj = BOOLEAN_OBJ(cur.mobj->via.boolean); + break; + } + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + sbassert(sizeof(int64_t) == sizeof(cur.mobj->via.i64)); + *cur.aobj = INTEGER_OBJ(cur.mobj->via.i64); + break; + } + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + sbassert(sizeof(uint64_t) == sizeof(cur.mobj->via.u64)); + *cur.aobj = UINTEGER_OBJ(cur.mobj->via.u64); + break; + } + case MSGPACK_OBJECT_FLOAT: { + sbassert(sizeof(double) == sizeof(cur.mobj->via.f64)); + *cur.aobj = FLOATING_OBJ(cur.mobj->via.f64); + break; + } + case MSGPACK_OBJECT_STR: { + *cur.aobj = STRING_OBJ(((string) { + .length = cur.mobj->via.str.size, + .str = (cur.mobj->via.str.ptr == NULL || cur.mobj->via.str.size == 0 + ? NULL : box_strndup(cur.mobj->via.str.ptr, + cur.mobj->via.str.size)), + })); + break; + } + case MSGPACK_OBJECT_BIN: { + *cur.aobj = STRING_OBJ(((string) { + .length = cur.mobj->via.bin.size, + .str = (cur.mobj->via.bin.ptr == NULL || cur.mobj->via.bin.size == 0 + ? NULL : box_strndup(cur.mobj->via.bin.ptr, cur.mobj->via.bin.size)), + })); + break; + } + + case MSGPACK_OBJECT_ARRAY: { + const size_t size = cur.mobj->via.array.size; + + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + kv_push(stack, ((msgpack_to_api_object_stack_item) { + .mobj = &cur.mobj->via.array.ptr[idx], + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); + } + } else { + *cur.aobj = ARRAY_OBJ(((array) { + .size = size, + .capacity = size, + .items = (size > 0 + ? calloc(size, sizeof(*cur.aobj->data.array.items)) : NULL), + })); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case MSGPACK_OBJECT_MAP: { + const size_t size = cur.mobj->via.map.size; + + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key; + switch (key->type) { + case MSGPACK_OBJECT_STR: { + cur.aobj->data.dictionary.items[idx].key = ((string) { + .length = key->via.str.size, + .str = (key->via.str.ptr == NULL || key->via.str.size == 0 + ? NULL : box_strndup(key->via.str.ptr, key->via.str.size)), + }); + break; + } + case MSGPACK_OBJECT_BIN: { + cur.aobj->data.dictionary.items[idx].key = ((string) { + .length = key->via.bin.size, + .str = (key->via.bin.ptr == NULL || key->via.bin.size == 0 + ? NULL : box_strndup(key->via.bin.ptr, key->via.bin.size)), + }); + break; + } + case MSGPACK_OBJECT_NIL: + case MSGPACK_OBJECT_BOOLEAN: + case MSGPACK_OBJECT_POSITIVE_INTEGER: + case MSGPACK_OBJECT_NEGATIVE_INTEGER: + case MSGPACK_OBJECT_FLOAT: + case MSGPACK_OBJECT_EXT: + case MSGPACK_OBJECT_MAP: + case MSGPACK_OBJECT_ARRAY: { + ret = false; + break; + } + } + if (ret) { + kv_push(stack, ((msgpack_to_api_object_stack_item) { + .mobj = &cur.mobj->via.map.ptr[idx].val, + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); + } + } + } else { + *cur.aobj = DICTIONARY_OBJ(((dictionary) { + .size = size, + .capacity = size, + .items = (size > 0 + ? calloc(size, sizeof(*cur.aobj->data.dictionary.items)) + : NULL), + })); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case MSGPACK_OBJECT_EXT: { + break; + } + } + if (!cur.container) { + (void)kv_pop(stack); + } + } + + kv_destroy(stack); + + return ret; +} + +STATIC bool msgpack_rpc_to_string(const msgpack_object *const obj, + string *const arg) +{ + if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { + arg->str = obj->via.bin.ptr != NULL + ? box_strndup(obj->via.bin.ptr, obj->via.bin.size) + : NULL; + arg->length = obj->via.bin.size; + return true; + } + return false; +} + +bool msgpack_rpc_to_array(const msgpack_object *const obj, array *const arg) +{ + if (obj->type != MSGPACK_OBJECT_ARRAY) { + return false; + } + + arg->size = obj->via.array.size; + arg->items = calloc_or_die(obj->via.array.size, sizeof(object)); + + for (uint32_t i = 0; i < obj->via.array.size; i++) { + if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) { + return false; + } + } + + return true; +} + +bool msgpack_rpc_to_dictionary(const msgpack_object *const obj, + dictionary *const arg) +{ + if (obj->type != MSGPACK_OBJECT_MAP) { + return false; + } + + arg->size = obj->via.array.size; + arg->items = calloc_or_die(obj->via.map.size, sizeof(key_value_pair)); + + + for (uint32_t i = 0; i < obj->via.map.size; i++) { + if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key, + &arg->items[i].key)) { + return false; + } + + if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val, + &arg->items[i].value)) { + return false; + } + } + + return true; +} + +void msgpack_rpc_from_boolean(bool result, msgpack_packer *res) +{ + if (result) { + msgpack_pack_true(res); + } else { + msgpack_pack_false(res); + } +} + +void msgpack_rpc_from_integer(int64_t result, msgpack_packer *res) +{ + msgpack_pack_int64(res, result); +} + +void msgpack_rpc_from_uinteger(uint64_t result, msgpack_packer *res) +{ + msgpack_pack_uint64(res, result); +} + +void msgpack_rpc_from_float(double result, msgpack_packer *res) +{ + msgpack_pack_double(res, result); +} + +void msgpack_rpc_from_string(string result, msgpack_packer *res) +{ + msgpack_pack_str(res, result.length); + msgpack_pack_str_body(res, result.str, result.length); +} + +typedef struct { + const object *obj; + bool container; + size_t idx; +} api_to_msgpack_object_stack_item; + +void msgpack_rpc_from_object(const object result, msgpack_packer *const res) +{ + kvec_t(api_to_msgpack_object_stack_item) stack = KV_INITIAL_VALUE; + kv_push(stack, ((api_to_msgpack_object_stack_item) {&result, false, 0})); + + while (kv_size(stack)) { + api_to_msgpack_object_stack_item cur = kv_last(stack); + + switch (cur.obj->type) { + case OBJECT_TYPE_NIL: { + msgpack_pack_nil(res); + break; + } + case OBJECT_TYPE_BOOL: { + msgpack_rpc_from_boolean(cur.obj->data.boolean, res); + break; + } + case OBJECT_TYPE_INT: { + msgpack_rpc_from_integer(cur.obj->data.integer, res); + break; + } + case OBJECT_TYPE_UINT: { + msgpack_rpc_from_uinteger(cur.obj->data.uinteger, res); + break; + } + case OBJECT_TYPE_FLOAT: { + msgpack_rpc_from_float(cur.obj->data.floating, res); + break; + } + case OBJECT_TYPE_STR: { + msgpack_rpc_from_string(cur.obj->data.string, res); + break; + } + case OBJECT_TYPE_ARRAY: { + const size_t size = cur.obj->data.array.size; + + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + kv_push(stack, ((api_to_msgpack_object_stack_item) { + .obj = &cur.obj->data.array.items[idx], + .container = false, + })); + } + } else { + msgpack_pack_array(res, size); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + case OBJECT_TYPE_DICTIONARY: { + const size_t size = cur.obj->data.dictionary.size; + if (cur.container) { + if (cur.idx >= size) { + (void)kv_pop(stack); + } else { + const size_t idx = cur.idx; + cur.idx++; + kv_last(stack) = cur; + msgpack_rpc_from_string(cur.obj->data.dictionary.items[idx].key, + res); + kv_push(stack, ((api_to_msgpack_object_stack_item) { + .obj = &cur.obj->data.dictionary.items[idx].value, + .container = false, + })); + } + } else { + msgpack_pack_map(res, size); + cur.container = true; + kv_last(stack) = cur; + } + break; + } + } + if (!cur.container) { + (void)kv_pop(stack); + } + } + kv_destroy(stack); +} + +void msgpack_rpc_from_array(array result, msgpack_packer *res) +{ + msgpack_pack_array(res, result.size); + + for (size_t i = 0; i < result.size; i++) { + msgpack_rpc_from_object(result.items[i], res); + } +} + +void msgpack_rpc_from_dictionary(dictionary result, msgpack_packer *res) +{ + msgpack_pack_map(res, result.size); + + for (size_t i = 0; i < result.size; i++) { + msgpack_rpc_from_string(result.items[i].key, res); + msgpack_rpc_from_object(result.items[i].value, res); + } +} + +void msgpack_rpc_serialize_request(uint64_t request_id, + string method, + array args, + msgpack_packer *pac) +{ + msgpack_pack_array(pac, request_id ? 4 : 3); + msgpack_pack_int(pac, request_id ? 0 : 2); + + if (request_id) { + msgpack_pack_uint64(pac, request_id); + } + + msgpack_rpc_from_string(method, pac); + msgpack_rpc_from_array(args, pac); +} + +void msgpack_rpc_serialize_response(uint64_t response_id, + struct api_error *err, + object arg, + msgpack_packer *pac) +{ + msgpack_pack_array(pac, 4); + msgpack_pack_int(pac, 1); + msgpack_pack_uint64(pac, response_id); + + if (err->isset) { + // error represented by a [type, message] array + msgpack_pack_array(pac, 2); + msgpack_rpc_from_integer(err->type, pac); + msgpack_rpc_from_string(cstring_to_string(err->msg), pac); + // Nil result + msgpack_pack_nil(pac); + } else { + // Nil error + msgpack_pack_nil(pac); + // Return value + msgpack_rpc_from_object(arg, pac); + } +} + +STATIC bool msgpack_rpc_is_notification(msgpack_object *req) +{ + return req->via.array.ptr[0].via.u64 == 2; +} + +msgpack_object *msgpack_rpc_method(msgpack_object *req) +{ + msgpack_object *obj = req->via.array.ptr + + (msgpack_rpc_is_notification(req) ? 1 : 2); + return obj->type == MSGPACK_OBJECT_STR || obj->type == MSGPACK_OBJECT_BIN ? + obj : NULL; +} + +msgpack_object *msgpack_rpc_args(msgpack_object *req) +{ + msgpack_object *obj = req->via.array.ptr + + (msgpack_rpc_is_notification(req) ? 2 : 3); + return obj->type == MSGPACK_OBJECT_ARRAY ? obj : NULL; +} + +STATIC msgpack_object *msgpack_rpc_msg_id(msgpack_object *req) +{ + if (msgpack_rpc_is_notification(req)) { + return NULL; + } + msgpack_object *obj = &req->via.array.ptr[1]; + return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER ? obj : NULL; +} + +void msgpack_rpc_validate(uint64_t *response_id, + msgpack_object *req, + struct api_error *err) +{ + // response id not known yet + + *response_id = UINT64_MAX; + // Validate the basic structure of the msgpack-rpc payload + if (req->type != MSGPACK_OBJECT_ARRAY) { + error_set(err, API_ERROR_TYPE_VALIDATION, "Message is not an array"); + return; + } + + if (req->via.array.size == 0) { + error_set(err, API_ERROR_TYPE_VALIDATION, "Message is empty"); + return; + } + + if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + error_set(err, API_ERROR_TYPE_VALIDATION, "Message type must be an integer"); + return; + } + + uint64_t type = req->via.array.ptr[0].via.u64; + if (type != MESSAGE_TYPE_REQUEST && type != MESSAGE_TYPE_NOTIFICATION) { + error_set(err, API_ERROR_TYPE_VALIDATION, "Unknown message type"); + return; + } + + if ((type == MESSAGE_TYPE_REQUEST && req->via.array.size != 4) + || (type == MESSAGE_TYPE_NOTIFICATION && req->via.array.size != 3)) { + error_set(err, API_ERROR_TYPE_VALIDATION, + "Request array size should be 4 (request) or 3 (notification)"); + return; + } + + if (type == MESSAGE_TYPE_REQUEST) { + msgpack_object *id_obj = msgpack_rpc_msg_id(req); + if (!id_obj) { + error_set(err, API_ERROR_TYPE_VALIDATION, "ID must be a positive integer"); + return; + } + *response_id = id_obj->via.u64; + } + + if (!msgpack_rpc_method(req)) { + error_set(err, API_ERROR_TYPE_VALIDATION, "Method must be a string"); + return; + } + + if (!msgpack_rpc_args(req)) { + error_set(err, API_ERROR_TYPE_VALIDATION, "Parameters must be an array"); + return; + } +} diff --git a/src/rpc/msgpack/helpers.h b/src/rpc/msgpack/helpers.h new file mode 100644 index 0000000..7cb7d7d --- /dev/null +++ b/src/rpc/msgpack/helpers.h @@ -0,0 +1,45 @@ +#pragma once + +#include "rpc/sb-rpc.h" + +bool msgpack_rpc_to_object(const msgpack_object *const obj, object *const arg); + +//STATIC bool msgpack_rpc_to_string(const msgpack_object *const obj, +// string *const arg); + +bool msgpack_rpc_to_array(const msgpack_object *const obj, array *const arg); + +bool msgpack_rpc_to_dictionary(const msgpack_object *const obj, + dictionary *const arg); + +void msgpack_rpc_from_boolean(bool result, msgpack_packer *res); + +void msgpack_rpc_from_integer(int64_t result, msgpack_packer *res); +void msgpack_rpc_from_uinteger(uint64_t result, msgpack_packer *res); + +void msgpack_rpc_from_float(double result, msgpack_packer *res); + +void msgpack_rpc_from_string(string result, msgpack_packer *res); + +void msgpack_rpc_from_object(const object result, msgpack_packer *const res); + +void msgpack_rpc_from_array(array result, msgpack_packer *res); + +void msgpack_rpc_from_dictionary(dictionary result, msgpack_packer *res); + +void msgpack_rpc_serialize_request(uint64_t request_id, string method, + array args, msgpack_packer *pac); + +void msgpack_rpc_serialize_response(uint64_t response_id, struct api_error *err, + object arg, msgpack_packer *pac); + +//STATIC bool msgpack_rpc_is_notification(msgpack_object *req); + +msgpack_object *msgpack_rpc_method(msgpack_object *req); + +msgpack_object *msgpack_rpc_args(msgpack_object *req); + +//STATIC msgpack_object *msgpack_rpc_msg_id(msgpack_object *req); + +void msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req, + struct api_error *err); diff --git a/src/rpc/msgpack/message.c b/src/rpc/msgpack/message.c deleted file mode 100644 index cfadb58..0000000 --- a/src/rpc/msgpack/message.c +++ /dev/null @@ -1,469 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "sb-common.h" - -bool message_is_request(msgpack_object *obj) -{ - if (!obj) - return (false); - - /* check if type is mspack_object_params */ - if (obj->type != MSGPACK_OBJECT_ARRAY) - return (false); - - msgpack_object_array *params = &obj->via.array; - - return (params->size == MESSAGE_REQUEST_ARRAY_SIZE && - params->ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER && - params->ptr[0].via.u64 == MESSAGE_TYPE_REQUEST); -} - - -bool message_is_response(msgpack_object *obj) -{ - if (!obj) - return (false); - - /* check if type is mspack_object_array */ - if (obj->type != MSGPACK_OBJECT_ARRAY) - return (false); - - msgpack_object_array *array = &obj->via.array; - - /* check if message is a response */ - return (array->size == MESSAGE_RESPONSE_ARRAY_SIZE && - array->ptr[0].type == MSGPACK_OBJECT_POSITIVE_INTEGER && - array->ptr[0].via.u64 == MESSAGE_TYPE_RESPONSE); -} - - -int message_serialize_error_response(msgpack_packer *pk, - struct api_error *api_error, uint32_t msgid) -{ - struct message_object *err_data; - array err_array; - - if (!pk || !api_error || !api_error->isset) - return (-1); - - msgpack_pack_array(pk, 4); - - pack_uint8(pk, MESSAGE_TYPE_RESPONSE); - pack_uint32(pk, msgid); - - err_array.size = 2; - err_array.obj = CALLOC(2, struct message_object); - - if (!err_array.obj) { - LOG_WARNING("Couldn't allocate memory for err_array object"); - return (-1); - } - - err_data = &err_array.obj[0]; - err_data->type = OBJECT_TYPE_INT; - err_data->data.integer = api_error->type; - - err_data = &err_array.obj[1]; - err_data->type = OBJECT_TYPE_STR; - err_data->data.string = cstring_to_string(api_error->msg); - - if (!err_data->data.string.str) { - LOG_WARNING("Couldn't allocate memory for string in err_array object"); - return (-1); - } - - if (pack_params(pk, err_array) == -1) - return (-1); - - pack_nil(pk); - - FREE(err_array.obj); - - return (0); -} - - -int message_serialize_response(struct message_response *res, - msgpack_packer *pk) -{ - if (!pk || !res) - return (-1); - - msgpack_pack_array(pk, 4); - pack_uint8(pk, MESSAGE_TYPE_RESPONSE); - pack_uint32(pk, res->msgid); - pack_nil(pk); - - if (pack_params(pk, res->params) == -1) - return (-1); - - return (0); -} - - -int message_serialize_request(struct message_request *req, - msgpack_packer *pk) -{ - if (!pk || !req) - return (-1); - - msgpack_pack_array(pk, 4); - pack_uint8(pk, MESSAGE_TYPE_REQUEST); - pack_uint32(pk, req->msgid); - - if (req->method.str == NULL || (pack_string(pk, req->method) == -1)) - return (-1); - - if (pack_params(pk, req->params) == -1) - return (-1); - - return (0); -} - - -int message_deserialize_request(struct message_request *req, - msgpack_object *obj, struct api_error *api_error) -{ - msgpack_object *type, *msgid, *method, *params; - uint64_t tmp_type; - uint64_t tmp_msgid; - - if (!api_error) - return (-1); - - if (!req || !obj) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error"); - return (-1); - } - - /* type */ - if (obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "type field has wrong type"); - return (-1); - } - - type = &obj->via.array.ptr[0]; - if (!type) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack type failed"); - return (-1); - } - - tmp_type = unpack_uint(type); - - if (tmp_type != MESSAGE_TYPE_REQUEST) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "type must be 0 or 1"); - return (-1); - } - - /* message id */ - msgid = &obj->via.array.ptr[1]; - - if (!msgid || msgid->type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "illegal msgid"); - return (-1); - } - - tmp_msgid = unpack_uint(msgid); - - if (tmp_msgid >= UINT32_MAX) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "invalid msgid"); - return (-1); - } - - req->msgid = (uint32_t)tmp_msgid; - - /* method */ - if (obj->via.array.ptr[2].type != MSGPACK_OBJECT_STR) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "method field has wrong type"); - return (-1); - } - - method = &obj->via.array.ptr[2]; - - if (!method) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack method failed"); - return (-1); - } - - req->method = unpack_string(method); - - if (!req->method.str) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking method"); - return (-1); - } - - /* params */ - if (obj->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { - free_string(req->method); - error_set(api_error, API_ERROR_TYPE_VALIDATION, "params field has wrong type"); - return (-1); - } - - params = &obj->via.array.ptr[3]; - - if (!params) { - free_string(req->method); - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack params failed"); - return (-1); - } - - if (unpack_params(params, &req->params) == -1) { - free_string(req->method); - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking params"); - return (-1); - } - - return (0); -} - -bool message_is_error_response(msgpack_object *obj) -{ - return (obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL); -} - -uint64_t message_get_id(msgpack_object *obj) -{ - return (obj->via.array.ptr[1].via.u64); -} - -int message_deserialize_response(struct message_response *res, - msgpack_object *obj, struct api_error *api_error) -{ - msgpack_object *type, *msgid, *params; - uint64_t tmp_msgid; - - if (!api_error) - return (-1); - - if (!res || !obj) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error"); - return (-1); - } - - /* type */ - if (obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "type field has wrong type"); - return (-1); - } - - type = &obj->via.array.ptr[0]; - - if (!type) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack type failed"); - return (-1); - } - - if (unpack_uint(type) != MESSAGE_TYPE_RESPONSE) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "type must be 1"); - return (-1); - } - - /* message id */ - msgid = &obj->via.array.ptr[1]; - - if (!msgid || msgid->type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "illegal msgid"); - return (-1); - } - - tmp_msgid = unpack_uint(msgid); - - if (tmp_msgid >= UINT32_MAX) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "invalid msgid"); - return (-1); - } - - res->msgid = (uint32_t)tmp_msgid; - - /* nil */ - if (obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "nil field has wrong type"); - return (-1); - } - - /* params */ - if (obj->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "params field has wrong type"); - return (-1); - } - - params = &obj->via.array.ptr[3]; - - if (!params) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack params failed"); - return (-1); - } - - if (unpack_params(params, &res->params) == -1) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking params"); - return (-1); - } - - return (0); -} - -int message_deserialize_error_response(struct message_response *res, - msgpack_object *obj, struct api_error *api_error) -{ - msgpack_object *type, *msgid, *params; - uint64_t tmp_msgid; - - if (!api_error) - return (-1); - - if (!res || !obj) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error"); - return (-1); - } - - /* type */ - if (obj->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "type field has wrong type"); - return (-1); - } - - type = &obj->via.array.ptr[0]; - - if (!type) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack type failed"); - return (-1); - } - - if (unpack_uint(type) != MESSAGE_TYPE_RESPONSE) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "type must be 1"); - return (-1); - } - - /* message id */ - msgid = &obj->via.array.ptr[1]; - - if (!msgid || msgid->type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "illegal msgid"); - return (-1); - } - - tmp_msgid = unpack_uint(msgid); - - if (tmp_msgid >= UINT32_MAX) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "invalid msgid"); - return (-1); - } - - res->msgid = (uint32_t)tmp_msgid; - - /* params */ - if (obj->via.array.ptr[2].type != MSGPACK_OBJECT_ARRAY) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "params field has wrong type"); - return (-1); - } - - params = &obj->via.array.ptr[2]; - - if (!params) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "unpack params failed"); - return (-1); - } - - if (unpack_params(params, &res->params) == -1) { - error_set(api_error, API_ERROR_TYPE_VALIDATION, "Error unpacking params"); - return (-1); - } - - /* nil */ - if (obj->via.array.ptr[3].type != MSGPACK_OBJECT_NIL) { - free_params(res->params); - error_set(api_error, API_ERROR_TYPE_VALIDATION, "nil field has wrong type"); - return (-1); - } - - return (0); -} - - -static void free_message_object(message_object obj) -{ - switch (obj.type) { - case OBJECT_TYPE_NIL: - break; - case OBJECT_TYPE_INT: - break; - case OBJECT_TYPE_UINT: - break; - case OBJECT_TYPE_BIN: - /* FALLTHROUGH */ - case OBJECT_TYPE_STR: - free_string(obj.data.string); - break; - case OBJECT_TYPE_BOOL: - break; - case OBJECT_TYPE_FLOAT: - break; - case OBJECT_TYPE_ARRAY: - free_params(obj.data.params); - break; - default: - return; - } -} - -struct message_object message_object_copy(struct message_object obj) -{ - switch (obj.type) { - case OBJECT_TYPE_NIL: - /* FALLTHROUGH */ - case OBJECT_TYPE_BOOL: - /* FALLTHROUGH */ - case OBJECT_TYPE_INT: - /* FALLTHROUGH */ - case OBJECT_TYPE_UINT: - /* FALLTHROUGH */ - case OBJECT_TYPE_FLOAT: - return obj; - case OBJECT_TYPE_BIN: - /* FALLTHROUGH */ - case OBJECT_TYPE_STR: - return (struct message_object) {.type = OBJECT_TYPE_STR, .data.string = - cstring_copy_string(obj.data.string.str) }; - case OBJECT_TYPE_ARRAY: { - array array = ARRAY_INIT; - - for (size_t i = 0; i < obj.data.params.size; i++) { - kv_push(struct message_object, array, - message_object_copy(obj.data.params.obj[i])); - } - - return (struct message_object) {.type = OBJECT_TYPE_ARRAY, - .data.params = array}; - } - default: - abort(); - } -} - - -void free_params(array params) -{ - for (size_t i = 0; i < params.size; i++) - free_message_object(params.obj[i]); - - if (params.obj) - FREE(params.obj); -} diff --git a/src/rpc/msgpack/pack.c b/src/rpc/msgpack/pack.c deleted file mode 100644 index a967fe5..0000000 --- a/src/rpc/msgpack/pack.c +++ /dev/null @@ -1,208 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "sb-common.h" - - -int pack_string(msgpack_packer *pk, string str) -{ - if (!pk) - return (-1); - - msgpack_pack_bin(pk, str.length); - msgpack_pack_bin_body(pk, str.str, str.length); - - return (0); -} - - -int pack_uint8(msgpack_packer *pk, uint8_t uinteger) -{ - if (!pk) - return (-1); - - msgpack_pack_uint8(pk, uinteger); - - return (0); -} - - -int pack_uint16(msgpack_packer *pk, uint16_t uinteger) -{ - if (!pk) - return (-1); - - msgpack_pack_uint16(pk, uinteger); - - return (0); -} - - -int pack_uint32(msgpack_packer *pk, uint32_t uinteger) -{ - if (!pk) - return (-1); - - msgpack_pack_uint32(pk, uinteger); - - return (0); -} - - -int pack_uint64(msgpack_packer *pk, uint64_t uinteger) -{ - if (!pk) - return (-1); - - msgpack_pack_uint64(pk, uinteger); - - return (0); -} - - -int pack_int8(msgpack_packer *pk, int8_t integer) -{ - if (!pk) - return (-1); - - msgpack_pack_int8(pk, integer); - - return (0); -} - - -int pack_int16(msgpack_packer *pk, int16_t integer) -{ - if (!pk) - return (-1); - - msgpack_pack_int16(pk, integer); - - return (0); -} - - -int pack_int32(msgpack_packer *pk, int32_t integer) -{ - if (!pk) - return (-1); - - msgpack_pack_int32(pk, integer); - - return (0); -} - - -int pack_int64(msgpack_packer *pk, int64_t integer) -{ - if (!pk) - return (-1); - - msgpack_pack_int64(pk, integer); - - return (0); -} - - -int pack_bool(msgpack_packer *pk, bool boolean) -{ - if (!pk) - return (-1); - - if (boolean) - msgpack_pack_true(pk); - else - msgpack_pack_false(pk); - - return (0); -} - - -int pack_float(msgpack_packer *pk, double floating) -{ - if (!pk) - return (-1); - - msgpack_pack_double(pk, floating); - - return (0); -} - - -int pack_nil(msgpack_packer *pk) -{ - if (!pk) - return (-1); - - msgpack_pack_nil(pk); - - return (0); -} - - -int pack_params(msgpack_packer *pk, array params) -{ - size_t i; - - if (!pk) - return (-1); - - msgpack_pack_array(pk, params.size); - - for (i = 0; i < params.size; i++) { - message_object *object = ¶ms.obj[i]; - message_object_type type = object->type; - - switch (type) { - case (OBJECT_TYPE_NIL): - pack_nil(pk); - continue; - case (OBJECT_TYPE_INT): - pack_int64(pk, object->data.integer); - continue; - case (OBJECT_TYPE_UINT): - pack_uint64(pk, object->data.uinteger); - continue; - case (OBJECT_TYPE_BOOL): - pack_bool(pk, object->data.boolean); - continue; - case (OBJECT_TYPE_FLOAT): - pack_float(pk, object->data.floating); - continue; - case (OBJECT_TYPE_ARRAY): - pack_params(pk, object->data.params); - continue; - case (OBJECT_TYPE_STR): - /* FALLTHROUGH */ - case (OBJECT_TYPE_BIN): - pack_string(pk, object->data.string); - continue; - default: - return (-1); - } - } - - return (0); -} diff --git a/src/rpc/msgpack/sb-msgpack-rpc.h b/src/rpc/msgpack/sb-msgpack-rpc.h deleted file mode 100644 index ea776b0..0000000 --- a/src/rpc/msgpack/sb-msgpack-rpc.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "rpc/sb-rpc.h" -#include "sb-common.h" - - -/* - * Message Format: - * - * - Request - - * - * msgpack - Array - * ---------------------------------------------------- - * | 0 (type) | id (request_id) | method | arguments | - * ---------------------------------------------------- - * - * - Response - - * - * msgpack - Array - * -------------------------------------------------------------------- - * | 1 (type) | id (request_id) | Error Code or Null | result or Null | - * -------------------------------------------------------------------- - */ - -/* Functions */ - -int message_unpack_type(msgpack_object *obj, struct message_request *req, - struct api_error *api_error); -int message_unpack_validate_type(msgpack_object *ptr, - struct api_error *api_error); -int message_unpack_validate_msgid(msgpack_object *ptr, - struct api_error *api_error); -int message_unpack_validate_status(msgpack_object *ptr, - struct api_error *api_error); -int message_unpack_validate_result(msgpack_object *ptr, - struct api_error *api_error); -int message_unpack_msgid(msgpack_object *obj, struct message_request *req, - struct api_error *api_error); -int message_unpack_status(msgpack_object *obj, struct message_response *res, - struct api_error *api_error); -int message_unpack_result(msgpack_object *obj, struct message_request *req, - struct api_error *api_error); -int message_unpack_method(msgpack_object *obj, struct message_request *req, - struct api_error *api_error); -int message_unpack_validate_method(msgpack_object *ptr, - struct api_error *api_error); -int message_unpack_validate_args(msgpack_object *ptr, - struct api_error *api_error); -int message_unpack_args(msgpack_object *obj, struct message_request *req, - array *params, struct api_error *api_error); -int message_pack_type(msgpack_packer *pk, uint8_t type); -int message_pack_method(msgpack_packer *pk, char *method); -int message_pack_params(msgpack_packer *pk, - array *params); -int message_pack_msgid(msgpack_packer *pk, uint32_t msgid); - - - -string unpack_string(msgpack_object *obj); -char * unpack_bin(msgpack_object *obj); -int64_t unpack_int(msgpack_object *obj); -uint64_t unpack_uint(msgpack_object *obj); -bool unpack_boolean(msgpack_object *obj); -double unpack_float(msgpack_object *obj); -int unpack_params(msgpack_object *obj, array *params); - - - -int pack_string(msgpack_packer *pk, string str); -int pack_uint8(msgpack_packer *pk, uint8_t uinteger); -int pack_uint16(msgpack_packer *pk, uint16_t uinteger); -int pack_uint32(msgpack_packer *pk, uint32_t uinteger); -int pack_uint64(msgpack_packer *pk, uint64_t uinteger); -int pack_int8(msgpack_packer *pk, int8_t integer); -int pack_int16(msgpack_packer *pk, int16_t integer); -int pack_int32(msgpack_packer *pk, int32_t integer); -int pack_int64(msgpack_packer *pk, int64_t integer); -int pack_nil(msgpack_packer *pk); -int pack_bool(msgpack_packer *pk, bool boolean); -int pack_float(msgpack_packer *pk, double floating); -int pack_params(msgpack_packer *pk, array params); diff --git a/src/rpc/msgpack/unpack.c b/src/rpc/msgpack/unpack.c deleted file mode 100644 index 98e0082..0000000 --- a/src/rpc/msgpack/unpack.c +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "sb-common.h" - -string unpack_string(msgpack_object *obj) -{ - string str; - char *ret = MALLOC_ARRAY(obj->via.bin.size + 1, char); - - if (!ret || !obj->via.bin.ptr) { - str = (string) { - .str = NULL, .length = 0 - }; - return (str); - } - - ret[obj->via.bin.size] = '\0'; - memcpy(ret, obj->via.bin.ptr, obj->via.bin.size); - - str = (string) { - .str = ret, .length = obj->via.bin.size - }; - - return (str); -} - - -int64_t unpack_int(msgpack_object *obj) -{ - return (obj->via.i64); -} - - -uint64_t unpack_uint(msgpack_object *obj) -{ - return (obj->via.u64); -} - - -bool unpack_boolean(msgpack_object *obj) -{ - return (obj->via.boolean); -} - - -double unpack_float(msgpack_object *obj) -{ - return (obj->via.f64); -} - - -int unpack_params(msgpack_object *obj, array *params) -{ - struct message_object *elem; - msgpack_object *tmp; - - if (!params) - return (-1); - - /* if array is empty return success */ - if (obj->via.array.size <= 0) { - params->obj = NULL; - params->size = 0; - return (0); - } - - params->obj = CALLOC(obj->via.array.size, struct message_object); - params->size = obj->via.array.size; - - if (!params->obj) - return (-1); - - for (size_t i = 0; i < params->size; i++) { - tmp = &obj->via.array.ptr[i]; - elem = ¶ms->obj[i]; - - switch (tmp->type) { - case MSGPACK_OBJECT_POSITIVE_INTEGER: - elem->type = OBJECT_TYPE_UINT; - elem->data.uinteger = unpack_uint(tmp); - continue; - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - elem->type = OBJECT_TYPE_INT; - elem->data.integer = unpack_int(tmp); - continue; - case MSGPACK_OBJECT_STR: - /* FALLTHROUGH */ - case MSGPACK_OBJECT_BIN: - elem->type = OBJECT_TYPE_STR; - elem->data.string = unpack_string(tmp); - continue; - case MSGPACK_OBJECT_BOOLEAN: - elem->type = OBJECT_TYPE_BOOL; - elem->data.boolean = unpack_boolean(tmp); - continue; - case MSGPACK_OBJECT_NIL: - elem->type = OBJECT_TYPE_NIL; - continue; - case MSGPACK_OBJECT_FLOAT: - elem->type = OBJECT_TYPE_FLOAT; - elem->data.floating = unpack_float(tmp); - continue; - case MSGPACK_OBJECT_ARRAY: - elem->type = OBJECT_TYPE_ARRAY; - if (unpack_params(tmp, &elem->data.params) == -1) - return (-1); - continue; - case MSGPACK_OBJECT_MAP: - /* FALLTHROUGH */ - case MSGPACK_OBJECT_EXT: - return (-1); - default: - return (-1); - } - } - - return (0); -} diff --git a/src/rpc/sb-rpc.h b/src/rpc/sb-rpc.h index 8e1c9fc..e9cd89d 100644 --- a/src/rpc/sb-rpc.h +++ b/src/rpc/sb-rpc.h @@ -43,23 +43,21 @@ /* Typedefs */ typedef struct outputstream outputstream; typedef struct inputstream inputstream; -typedef int (*inputstream_cb)(inputstream *inputstream, void *data, bool eof); +typedef void (*inputstream_cb)(inputstream *inputstream, void *data, bool eof); typedef struct api_event api_event; -typedef struct equeue equeue; -typedef struct queue_entry queue_entry; typedef struct message_object message_object; typedef struct connection_request_event_info connection_request_event_info; #define MESSAGE_REQUEST_ARRAY_SIZE 4 #define MESSAGE_RESPONSE_ARRAY_SIZE 4 -#define MESSAGE_TYPE_REQUEST 0 -#define MESSAGE_TYPE_RESPONSE 1 #define MESSAGE_RESPONSE_UNKNOWN UINT32_MAX +#define METHOD_MAXLEN 512 + #define STREAM_BUFFER_SIZE 0xffff -#define CALLINFO_INIT (struct callinfo) {0, false, false,((struct message_response) {0, ARRAY_INIT})} +#define CALLINFO_INIT (struct callinfo) {0, false, false, NIL} /* @@ -73,6 +71,14 @@ typedef struct connection_request_event_info connection_request_event_info; /* Enums */ +typedef enum { + MESSAGE_TYPE_REQUEST, + MESSAGE_TYPE_RESPONSE, + MESSAGE_TYPE_NOTIFICATION +} message_type; + +typedef struct object object; + typedef enum { OBJECT_TYPE_NIL, OBJECT_TYPE_INT, @@ -80,9 +86,9 @@ typedef enum { OBJECT_TYPE_BOOL, OBJECT_TYPE_FLOAT, OBJECT_TYPE_STR, - OBJECT_TYPE_BIN, OBJECT_TYPE_ARRAY, -} message_object_type; + OBJECT_TYPE_DICTIONARY +} object_type; typedef enum { TUNNEL_INITIAL, @@ -92,14 +98,22 @@ typedef enum { /* Structs */ +typedef struct key_value_pair key_value_pair; + typedef struct { - message_object *obj; + object *items; size_t size; size_t capacity; } array; -struct message_object { - message_object_type type; +typedef struct { + key_value_pair *items; + size_t size; + size_t capacity; +} dictionary; + +struct object { + object_type type; union { int64_t integer; uint64_t uinteger; @@ -107,7 +121,8 @@ struct message_object { char *bin; bool boolean; double floating; - array params; + array array; + dictionary dictionary; } data; }; @@ -122,6 +137,12 @@ struct message_response { array params; }; +struct key_value_pair { + string key; + object value; +}; + + /***************************************************************************** * The following crypto_context structure should be considered PRIVATE to * * the rpc connection layer. No non-rpc connection layer code should be * @@ -131,6 +152,23 @@ struct message_response { #define PLUGINKEY_STRING_SIZE ((PLUGINKEY_SIZE * 2) + 1) #define CLIENTLONGTERMPK_ARRAY_SIZE 32 +typedef object (*apidispatchwrapper)(uint64_t con_id, uint64_t msgid, + char *pluginkey, array args, struct api_error *error); + +typedef struct { + apidispatchwrapper func; + bool async; + string name; +} dispatch_info; + +/* hashmap declarations */ +MAP_DECLS(uint64_t, string) +MAP_DECLS(string, ptr_t) +MAP_DECLS(uint64_t, ptr_t) /* maps callid <> pluginkey */ +MAP_DECLS(cstr_t, ptr_t) /* maps pluginkey <> connection */ +MAP_DECLS(string, dispatch_info) +MAP_DECLS(cstr_t, uint64_t) + struct crypto_context { crypto_state state; uint64_t nonce; @@ -144,50 +182,21 @@ struct crypto_context { char pluginkeystring[PLUGINKEY_STRING_SIZE]; }; -struct connection { - uint64_t id; - uint32_t msgid; - uint32_t pendingcalls; +typedef struct wbuffer wbuffer; + +struct wbuffer { + size_t size; size_t refcount; - msgpack_unpacker *mpac; - msgpack_sbuffer *sbuf; - char *unpackbuf; - bool closed; - equeue *queue; - struct { - inputstream *read; - outputstream *write; - uv_stream_t *uv; - } streams; - kvec_t(struct callinfo *) callvector; - struct crypto_context cc; - struct { - uint64_t start; - uint64_t end; - uint64_t pos; - uint64_t length; - unsigned char *data; - } packet; - uv_timer_t minutekey_timer; + char *data; }; struct callinfo { - uint32_t msgid; - bool hasresponse; - bool errorresponse; - struct message_response response; + uint64_t msgid; + bool returned; + bool errored; + object result; }; -typedef int (*apidispatchwrapper)(uint64_t con_id, - struct message_request *request, char *pluginkey, - struct api_error *error); - -typedef struct { - apidispatchwrapper func; - bool async; - string name; -} dispatch_info; - struct outputstream { uv_stream_t *stream; size_t curmem; @@ -216,48 +225,11 @@ struct inputstream { /* this structure holds a request and all information to send a response */ struct connection_request_event_info { struct connection *con; - struct message_request request; dispatch_info dispatcher; - struct api_error api_error; -}; - -/* this structure holds the request event information and a callback to a - handler function */ -struct api_event { - connection_request_event_info info; - void (*handler)(connection_request_event_info *info); -}; - -TAILQ_HEAD(queue, queue_entry); - -struct equeue { - struct queue head; - equeue *root; -}; - -struct queue_entry { - union { - equeue *queue; - struct { - queue_entry *root; - api_event event; - } entry; - } data; - TAILQ_ENTRY(queue_entry) node; + array args; + uint64_t msgid; }; -/* hashmap declarations */ -MAP_DECLS(uint64_t, string) -MAP_DECLS(string, ptr_t) -MAP_DECLS(uint64_t, ptr_t) /* maps callid <> pluginkey */ -MAP_DECLS(cstr_t, ptr_t) /* maps pluginkey <> connection */ -MAP_DECLS(string, dispatch_info) -MAP_DECLS(cstr_t, uint64_t) - -/* define global root event queue */ -extern equeue *equeue_root; - - /* Functions */ /** @@ -276,15 +248,18 @@ int connection_init(void); */ int connection_create(uv_stream_t *stream); -struct callinfo connection_send_request(char *pluginkey, string method, - array params, struct api_error *api_error); +object connection_send_request(char *pluginkey, string method, + array args, struct api_error *err); int connection_send_response(uint64_t con_id, uint32_t msgid, - array params, struct api_error *api_error); -int connection_hashmap_put(uint64_t id, struct connection *con); -int pluginkeys_hashmap_put(char *pluginkey, uint64_t id); + object arg, struct api_error *api_error); +void connection_hashmap_put(uint64_t id, struct connection *con); +void pluginkeys_hashmap_put(char *pluginkey, uint64_t id); void loop_wait_for_response(struct connection *con, struct callinfo *cinfo); int connection_teardown(void); +void connection_subscribe(uint64_t id, char *event); +void connection_unsubscribe(uint64_t id, char *event); +bool connection_send_event(uint64_t id, char *name, array args); /** * Create a new `outputstream` instance. A `outputstream` instance contains the @@ -413,61 +388,6 @@ int server_start_pipe(char *name); int server_stop(char * endpoint); int server_close(void); -int event_initialize(void); - -/** - * create a new queue or child queue - * - * @params root if NULL a root queue is created, if not NULL a child queue for - * `root` is created - * @returns queue instance - */ -equeue * equeue_new(equeue *root); - -/** - * get a queue element - * - * this function first checks if the passed queue is empty, if yes it return - * an empty event object. if it's not empty the first queue entry will be - * returned and removed from the queue. - * - * if the queue entry is a child, the first child queue entry is returned and - * removed from the child queue. after that, the child queue entry associated - * event is returned. - * - * if the queue entry is no child. the queue entry associated event is returned - * - * @param queue queue instance - */ -api_event equeue_get(equeue *queue); - -/** - * put an event in a queue - * - * @param queue queue instance - * @param event event to enqueue - */ -int equeue_put(equeue *queue, api_event event); - -/** - * run all events of a queue - * - * @params queue queue instance - */ -void equeue_run_events(equeue *queue); - -/** - * free queue - * - * @params queue queue instance - */ -void equeue_free(equeue *queue); - -bool equeue_empty(equeue *queue); - - - - void streamhandle_set_inputstream(uv_handle_t *handle, inputstream *inputstream); void streamhandle_set_outputstream(uv_handle_t *handle, @@ -478,16 +398,24 @@ inputstream * streamhandle_get_inputstream(uv_handle_t *handle); int dispatch_table_init(void); int dispatch_teardown(void); dispatch_info dispatch_table_get(string method); +dispatch_info msgpack_rpc_get_handler_for(const char *name, size_t name_len); void dispatch_table_put(string method, dispatch_info info); -int handle_run(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error); -int handle_result(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error); -int handle_register(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error); -int handle_error(uint64_t con_id, struct message_request *request, - char *pluginkey, struct api_error *error); - +object handle_run(uint64_t con_id, uint64_t msgid, char *pluginkey, + array args, struct api_error *error); +object handle_result(uint64_t con_id, uint64_t msgid, char *pluginkey, + array args, struct api_error *error); +object handle_register(uint64_t con_id, uint64_t msgid, char *pluginkey, + array args, struct api_error *error); +object handle_subscribe(uint64_t con_id, uint64_t msgid, char *pluginkey, + array args, struct api_error *error); +object handle_unsubscribe(uint64_t con_id, uint64_t msgid, char *pluginkey, + array args, struct api_error *error); +object handle_broadcast(uint64_t con_id, uint64_t msgid, char *pluginkey, + array args, struct api_error *error); +object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, + uint64_t msgid, char *pluginkey, array args, struct api_error *error); +object msgpack_rpc_handle_missing_method(uint64_t channel_id, + uint64_t msgid, char *pluginkey, array args, struct api_error *error); /* Message Functions */ void free_params(array params); @@ -508,6 +436,7 @@ void message_dispatch(msgpack_object *req, msgpack_packer *res); uint64_t message_get_id(msgpack_object *obj); bool message_is_error_response(msgpack_object *obj); struct message_object message_object_copy(struct message_object obj); +object copy_object(object obj); @@ -601,3 +530,9 @@ uint64_t uint64_unpack(const unsigned char *x); * returns -1 in case of error otherwise 0 */ int byte_isequal(const void *yv, long long ylen, const void *xv); + +void msgpack_rpc_serialize_response(uint64_t response_id, struct api_error *err, + object arg, msgpack_packer *pac); + +void msgpack_rpc_serialize_request(uint64_t request_id, string method, + array args, msgpack_packer *pac); diff --git a/src/sb-common.h b/src/sb-common.h index a2284cd..95c186c 100644 --- a/src/sb-common.h +++ b/src/sb-common.h @@ -78,12 +78,14 @@ #include "queue.h" #include "khash.h" #include "kvec.h" +#include "mem.h" /* Structs */ #define API_ERROR_MESSAGE_LEN 512 typedef enum { - API_ERROR_TYPE_VALIDATION, + API_ERROR_TYPE_EXCEPTION, + API_ERROR_TYPE_VALIDATION } api_error_type; typedef struct { @@ -235,9 +237,6 @@ typedef const char * cstr_t; /* verbosity global */ extern int8_t verbose_level; -/* uv loop global */ -extern uv_loop_t loop; - /* address parsing helper inline functions */ /** Helper: given a hex digit, return its value, or -1 if it isn't hex. */ static inline int hex_decode_digit(char c) @@ -385,22 +384,6 @@ define UNUSED(x) x #define PREDICT_UNLIKELY(exp) (exp) #endif -#define MALLOC(type) ((type *)reallocarray(NULL, 1, sizeof(type))) - -#define MALLOC_ARRAY(number, type) \ - ((type *)reallocarray(NULL, number, sizeof(type))) - -#define CALLOC(number, type) \ - ((type *)calloc(number, sizeof(type))) - -#define REALLOC_ARRAY(pointer, number, type) \ - ((type *)reallocarray(pointer, number, sizeof(type))) - -#define FREE(pointer) do { \ - free(pointer); \ - pointer = NULL; \ - } while (0) - #define LOG(...) \ do { \ fprintf(stdout, __VA_ARGS__); \ @@ -424,6 +407,12 @@ define UNUSED(x) x } \ } while (0) +#define LOG_MEMERROR() \ + do { \ + LOG_ERROR("[%s] %s (%d):\tFailed to alloc memory.\n", \ + __FILE__, __func__, __LINE__); \ + } while (0) \ + #if 8 == 8 #define ARCH_64 #elif 8 == 4 @@ -442,7 +431,8 @@ define UNUSED(x) x #define BOX_ADDR_BUF_LEN 48 -#define ARRAY_INIT {.size = 0, .capacity = 0, .obj = NULL} +#define ARRAY_INIT {.size = 0, .capacity = 0, .items = NULL} +#define ARRAY_DICT_INIT {.size = 0, .capacity = 0, .items = NULL} #define STRING_INIT {.str = NULL, .length = 0} #define ERROR_INIT {.isset = false} @@ -526,6 +516,8 @@ uint64_t parse_units(const char *val, struct unit_table_t *u, int *ok); const char * eat_whitespace(const char *s); char * box_strdup(const char *s); char * box_strndup(const char *s, size_t n); +void * sb_memdup(const void *mem, size_t len); +void * sb_memdup_nulterm(const void *mem, size_t len); int box_sscanf(const char *buf, const char *pattern, ...); int box_vsscanf(const char *buf, const char *pattern, va_list ap); diff --git a/src/sb-pluginkey.c b/src/sb-pluginkey.c index 62b984d..e3aeedb 100644 --- a/src/sb-pluginkey.c +++ b/src/sb-pluginkey.c @@ -48,7 +48,7 @@ int main(int argc, char **argv) goto fail; } - pluginkey = MALLOC_ARRAY((PLUGINKEY_SIZE*2) + 1, char); + pluginkey = malloc_array_or_die((PLUGINKEY_SIZE*2) + 1, sizeof(char)); if (!pluginkey) { LOG("Failed to alloc mem for pluginkey_base16_encoded.\n"); goto fail; diff --git a/src/signal.c b/src/signal.c index b362072..1268c04 100644 --- a/src/signal.c +++ b/src/signal.c @@ -15,6 +15,7 @@ */ #include "sb-common.h" +#include "main.h" static void signal_sigint_cb(uv_signal_t *uvhandle, int signum); @@ -23,7 +24,7 @@ uv_signal_t sigint; int signal_init(void) { /* SIGINT */ - if (uv_signal_init(&loop, &sigint) != 0) { + if (uv_signal_init(&main_loop.uv, &sigint) != 0) { return (-1); } diff --git a/src/string.c b/src/string.c index bfa27c5..e1a5d41 100644 --- a/src/string.c +++ b/src/string.c @@ -39,7 +39,7 @@ string cstring_copy_string(const char *str) return (string) STRING_INIT; length = strlen(str); - ret = MALLOC_ARRAY(length + 1, char); + ret = malloc_array_or_die(length + 1, sizeof(char)); strlcpy(ret, str, length + 1); diff --git a/src/util.c b/src/util.c index f8a383f..b4fc31a 100644 --- a/src/util.c +++ b/src/util.c @@ -242,7 +242,7 @@ char * box_strndup(const char *s, size_t n) sbassert(s); - dup = MALLOC_ARRAY((n+1), char); + dup = malloc_array_or_die((n+1), sizeof(char)); strncpy(dup, s, n); dup[n]='\0'; @@ -250,6 +250,33 @@ char * box_strndup(const char *s, size_t n) return (dup); } +/** Allocate a chunk of len bytes, with the same contents as the + * len bytes starting at mem. */ +void * sb_memdup(const void *mem, size_t len) +{ + char *duplicate; + sbassert(mem); + + duplicate = malloc_array_or_die(len, sizeof(char)); + memcpy(duplicate, mem, len); + + return duplicate; +} + +/** As sb_memdup(), but add an extra 0 byte at the end of the resulting + * memory. */ +void * sb_memdup_nulterm(const void *mem, size_t len) +{ + char *duplicate; + + sbassert(mem); + duplicate = malloc_array_or_die((len + 1), sizeof(char)); + memcpy(duplicate, mem, len); + duplicate[len] = '\0'; + + return duplicate; +} + /** Minimal sscanf replacement: parse buf according to pattern * and store the results in the corresponding argument fields. Differs from * sscanf in that: diff --git a/src/version.h b/src/version.h index 124db07..3ca89b3 100644 --- a/src/version.h +++ b/src/version.h @@ -21,7 +21,7 @@ #define VERSION_MAJOR 0 #define VERSION_MINOR 1 -#define VERSION_REVISION 5 +#define VERSION_REVISION 7 #define VERSION STRINGIFY(VERSION_MAJOR) "." \ STRINGIFY(VERSION_MINOR) "." \ diff --git a/test/functional/crypto.c b/test/functional/crypto.c index e5d1c43..0d527ea 100644 --- a/test/functional/crypto.c +++ b/test/functional/crypto.c @@ -46,11 +46,8 @@ int validate_crypto_cookie_packet(unsigned char *buffer, ciphertextlen = 160; blocklen = 144; - block = MALLOC_ARRAY(ciphertextlen, unsigned char); - ciphertextpadded = CALLOC(ciphertextlen, unsigned char); - - if (block == NULL || ciphertextpadded == NULL) - return (-1); + block = malloc_array_or_die(ciphertextlen, sizeof(unsigned char)); + ciphertextpadded = calloc_or_die(ciphertextlen, sizeof(unsigned char)); memcpy(ciphertextpadded + 16, buffer + 24, blocklen); @@ -100,11 +97,8 @@ int validate_crypto_write(unsigned char *buffer, UNUSED(uint64_t length)) ciphertextlen = unpackedlen - 24; blocklen = unpackedlen - 40; - block = MALLOC_ARRAY(ciphertextlen, unsigned char); - ciphertextpadded = CALLOC(ciphertextlen, unsigned char); - - if (block == NULL || ciphertextpadded == NULL) - return (-1); + block = malloc_array_or_die(ciphertextlen, sizeof(unsigned char)); + ciphertextpadded = calloc_or_die(ciphertextlen, sizeof(unsigned char)); memcpy(ciphertextpadded + 16, buffer + 40, blocklen); diff --git a/test/functional/db-function-flush-args.c b/test/functional/db-function-flush-args.c index 298e7c4..4ec3a1c 100644 --- a/test/functional/db-function-flush-args.c +++ b/test/functional/db-function-flush-args.c @@ -20,6 +20,7 @@ #include "helper-all.h" #include "rpc/db/sb-db.h" #include "sb-common.h" +#include "api/sb-api.h" #include "helper-unix.h" @@ -59,23 +60,23 @@ void functional_db_function_flush_args(UNUSED(void **state)) ssize_t argc = 0; params.size = 4; - params.obj = CALLOC(params.size, struct message_object); + params.items = calloc_or_die(params.size, sizeof(object)); - params.obj[0].type = OBJECT_TYPE_STR; - params.obj[0].data.string = name; + params.items[0].type = OBJECT_TYPE_STR; + params.items[0].data.string = name; - params.obj[1].type = OBJECT_TYPE_STR; - params.obj[1].data.string = desc; + params.items[1].type = OBJECT_TYPE_STR; + params.items[1].data.string = desc; - params.obj[2].type = OBJECT_TYPE_ARRAY; - params.obj[2].data.params.size = 1; - params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size, - struct message_object); - params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT; - params.obj[2].data.params.obj[0].data.uinteger = 6; + params.items[2].type = OBJECT_TYPE_ARRAY; + params.items[2].data.array.size = 1; + params.items[2].data.array.items = calloc_or_die( + params.items[2].data.array.size, sizeof(object)); + params.items[2].data.array.items[0].type = OBJECT_TYPE_INT; + params.items[2].data.array.items[0].data.uinteger = 6; connect_and_create(pluginkey); - args = ¶ms.obj[2].data.params; + args = ¶ms.items[2].data.array; /* register function */ assert_int_equal(0, db_function_add(pluginkey, ¶ms)); @@ -96,14 +97,15 @@ void functional_db_function_flush_args(UNUSED(void **state)) /* now test with two arguments */ - free_params(params.obj[2].data.params); - params.obj[2].data.params.size = 2; - params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size, - struct message_object); - params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT; - params.obj[2].data.params.obj[0].data.uinteger = 6; - params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT; - params.obj[2].data.params.obj[0].data.uinteger = 12; + api_free_array(params.items[2].data.array); + + params.items[2].data.array.size = 2; + params.items[2].data.array.items = calloc_or_die( + params.items[2].data.array.size, sizeof(object)); + params.items[2].data.array.items[0].type = OBJECT_TYPE_INT; + params.items[2].data.array.items[0].data.uinteger = 6; + params.items[2].data.array.items[0].type = OBJECT_TYPE_INT; + params.items[2].data.array.items[0].data.uinteger = 12; assert_int_equal(0, db_function_add(pluginkey, ¶ms)); argc = db_function_get_argc(pluginkey, name); @@ -117,5 +119,5 @@ void functional_db_function_flush_args(UNUSED(void **state)) db_close(); - free_params(params); + api_free_array(params); } diff --git a/test/functional/db-function-register.c b/test/functional/db-function-register.c index 0c2a522..d551319 100644 --- a/test/functional/db-function-register.c +++ b/test/functional/db-function-register.c @@ -19,6 +19,7 @@ #include "helper-all.h" #include "rpc/sb-rpc.h" +#include "api/sb-api.h" #include "sb-common.h" #include "helper-unix.h" @@ -33,20 +34,20 @@ void functional_db_function_add(UNUSED(void **state)) array params; params.size = 4; - params.obj = CALLOC(params.size, struct message_object); + params.items = calloc_or_die(params.size, sizeof(object)); - params.obj[0].type = OBJECT_TYPE_STR; - params.obj[0].data.string = name; + params.items[0].type = OBJECT_TYPE_STR; + params.items[0].data.string = name; - params.obj[1].type = OBJECT_TYPE_STR; - params.obj[1].data.string = desc; + params.items[1].type = OBJECT_TYPE_STR; + params.items[1].data.string = desc; - params.obj[2].type = OBJECT_TYPE_ARRAY; - params.obj[2].data.params.size = 1; - params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size, - struct message_object); - params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT; - params.obj[2].data.params.obj[0].data.uinteger = 6; + params.items[2].type = OBJECT_TYPE_ARRAY; + params.items[2].data.array.size = 1; + params.items[2].data.array.items = calloc_or_die( + params.items[2].data.array.size, sizeof(object)); + params.items[2].data.array.items[0].type = OBJECT_TYPE_INT; + params.items[2].data.array.items[0].data.uinteger = 6; connect_and_create(pluginkey); @@ -59,23 +60,23 @@ void functional_db_function_add(UNUSED(void **state)) /* function without arguments should work */ params.size = 4; - params.obj[2].data.params.size = 0; + params.items[2].data.array.size = 0; assert_int_equal(0, db_function_add(pluginkey, ¶ms)); /* function without name should not work */ params.size = 4; - params.obj[2].data.params.size = 1; - params.obj[0].data.string = cstring_copy_string(""); + params.items[2].data.array.size = 1; + params.items[0].data.string = cstring_copy_string(""); assert_int_not_equal(0, db_function_add(pluginkey, ¶ms)); - free_string(params.obj[0].data.string); + free_string(params.items[0].data.string); /* function without description should work */ - params.obj[0].data.string = name; - params.obj[1].data.string = cstring_copy_string(""); + params.items[0].data.string = name; + params.items[1].data.string = cstring_copy_string(""); assert_int_equal(0, db_function_add(pluginkey, ¶ms)); db_close(); - free_params(params); - free_string(desc); + api_free_array(params); + api_free_string(desc); } diff --git a/test/functional/db-function-verify.c b/test/functional/db-function-verify.c index 280bd6c..9eba55d 100644 --- a/test/functional/db-function-verify.c +++ b/test/functional/db-function-verify.c @@ -20,6 +20,7 @@ #include "helper-all.h" #include "rpc/sb-rpc.h" +#include "api/sb-api.h" #include "sb-common.h" #include "helper-unix.h" @@ -33,22 +34,22 @@ void functional_db_function_verify(UNUSED(void **state)) array params, *args; params.size = 4; - params.obj = CALLOC(params.size, struct message_object); + params.items = calloc_or_die(params.size, sizeof(object)); - params.obj[0].type = OBJECT_TYPE_STR; - params.obj[0].data.string = name; + params.items[0].type = OBJECT_TYPE_STR; + params.items[0].data.string = name; - params.obj[1].type = OBJECT_TYPE_STR; - params.obj[1].data.string = desc; + params.items[1].type = OBJECT_TYPE_STR; + params.items[1].data.string = desc; - params.obj[2].type = OBJECT_TYPE_ARRAY; - params.obj[2].data.params.size = 1; - params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size, - struct message_object); - params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT; - params.obj[2].data.params.obj[0].data.uinteger = 6; + params.items[2].type = OBJECT_TYPE_ARRAY; + params.items[2].data.array.size = 1; + params.items[2].data.array.items = calloc_or_die( + params.items[2].data.array.size, sizeof(object)); + params.items[2].data.array.items[0].type = OBJECT_TYPE_INT; + params.items[2].data.array.items[0].data.uinteger = 6; - args = ¶ms.obj[2].data.params; + args = ¶ms.items[2].data.array; connect_and_create(pluginkey); @@ -73,11 +74,11 @@ void functional_db_function_verify(UNUSED(void **state)) /* verifying an existing function with wrong arguments' type */ args->size = 1; - args->obj[0].type = OBJECT_TYPE_UINT; - args->obj[0].data.integer = -1; + args->items[0].type = OBJECT_TYPE_UINT; + args->items[0].data.integer = -1; assert_int_not_equal(0, db_function_verify(pluginkey, name, args)); - free_params(params); - free_string(invalid_name); + api_free_array(params); + api_free_string(invalid_name); db_close(); } diff --git a/test/functional/dispatch-handle-broadcast.c b/test/functional/dispatch-handle-broadcast.c new file mode 100644 index 0000000..2970a52 --- /dev/null +++ b/test/functional/dispatch-handle-broadcast.c @@ -0,0 +1,198 @@ +/** + * Copyright (C) 2015 splone UG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include "sb-common.h" +#include "rpc/sb-rpc.h" +#include "api/helpers.h" +#include "rpc/msgpack/helpers.h" +#include "rpc/connection/connection.h" +#include "api/sb-api.h" +#ifdef __linux__ +#include +#endif + +#include "helper-unix.h" +#include "helper-all.h" +#include "helper-validate.h" + +static array api_broadcast_valid(void) +{ + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string("abc"))); + ADD(args, STRING_OBJ(cstring_copy_string("def"))); + + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("testname"))); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_broadcast_wrong_args_size(void) +{ + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string("abc"))); + ADD(args, STRING_OBJ(cstring_copy_string("def"))); + + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("testname"))); + ADD(request, ARRAY_OBJ(args)); + ADD(request, STRING_OBJ(cstring_copy_string("wrong"))); + + return request; +} + +static array api_broadcast_wrong_args_type(void) +{ + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string("abc"))); + ADD(args, STRING_OBJ(cstring_copy_string("def"))); + + array request = ARRAY_DICT_INIT; + ADD(request, OBJECT_OBJ((object) OBJECT_INIT)); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_subscribe_valid(void) +{ + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("testname"))); + + return request; +} + +int validate_broadcast(const unsigned long data1, + UNUSED(const unsigned long data2)) +{ + struct msgpack_object *deserialized = (struct msgpack_object *) data1; + array message; + + msgpack_rpc_to_array(deserialized, &message); + + assert_true(message.items[0].type == OBJECT_TYPE_UINT); + assert_int_equal(2, message.items[0].data.uinteger); + + assert_true(message.items[1].type == OBJECT_TYPE_STR); + assert_string_equal(message.items[1].data.string.str, "testname"); + + assert_true(message.items[2].type == OBJECT_TYPE_ARRAY); + assert_int_equal(2, message.items[2].data.array.size); + + assert_true(message.items[2].data.array.items[0].type == OBJECT_TYPE_STR); + assert_string_equal(message.items[2].data.array.items[0].data.string.str, + "abc"); + + assert_true(message.items[2].data.array.items[1].type == OBJECT_TYPE_STR); + assert_string_equal(message.items[2].data.array.items[1].data.string.str, + "def"); + + api_free_array(message); + + return 1; +} + + +void functional_dispatch_handle_broadcast(UNUSED(void **state)) +{ + struct plugin *plugin = helper_get_example_plugin(); + struct connection *con1; + struct connection *con2; + struct api_error error = ERROR_INIT; + array request; + + con1 = calloc_or_die(1, sizeof(struct connection)); + con1->id = (uint64_t) randommod(281474976710656LL); + con1->msgid = 4321; + con1->subscribed_events = hashmap_new(cstr_t, ptr_t)(); + + con2 = calloc_or_die(1, sizeof(struct connection)); + con2->id = (uint64_t) randommod(281474976710656LL); + con2->msgid = 4321; + con2->subscribed_events = hashmap_new(cstr_t, ptr_t)(); + + assert_non_null(con1); + assert_non_null(con2); + + strlcpy(con1->cc.pluginkeystring, "ABCD", sizeof("ABCD") - 1); + strlcpy(con2->cc.pluginkeystring, "EFGH", sizeof("EFGH") - 1); + + connect_to_db(); + assert_int_equal(0, connection_init()); + + con1->refcount++; + con2->refcount++; + + connection_hashmap_put(con1->id, con1); + connection_hashmap_put(con2->id, con2); + pluginkeys_hashmap_put(con1->cc.pluginkeystring, con1->id); + pluginkeys_hashmap_put(con2->cc.pluginkeystring, con2->id); + + expect_check(__wrap_crypto_write, &deserialized, validate_broadcast, NULL); + expect_check(__wrap_crypto_write, &deserialized, validate_broadcast, NULL); + + request = api_subscribe_valid(); + handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_subscribe_valid(); + handle_subscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_broadcast_valid(); + handle_broadcast(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_broadcast_wrong_args_size(); + handle_broadcast(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_true(error.isset); + error.isset = false; + api_free_array(request); + + request = api_broadcast_wrong_args_type(); + handle_broadcast(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_true(error.isset); + error.isset = false; + api_free_array(request); + + request = api_subscribe_valid(); + handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_subscribe_valid(); + handle_unsubscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + con1->closed = true; + con2->closed = true; + + hashmap_free(cstr_t, ptr_t)(con1->subscribed_events); + hashmap_free(cstr_t, ptr_t)(con2->subscribed_events); + + helper_free_plugin(plugin); + connection_teardown(); + FREE(con1); + FREE(con2); + db_close(); +} diff --git a/test/functional/dispatch-handle-register.c b/test/functional/dispatch-handle-register.c index 89faeb5..dc6912e 100644 --- a/test/functional/dispatch-handle-register.c +++ b/test/functional/dispatch-handle-register.c @@ -19,209 +19,233 @@ #include "sb-common.h" #include "tweetnacl.h" #include "rpc/sb-rpc.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" +#include "rpc/connection/connection.h" +#include "api/helpers.h" +#include "api/sb-api.h" +#ifdef __linux__ +#include +#endif #include "helper-unix.h" #include "helper-all.h" #include "helper-validate.h" -void functional_dispatch_handle_register(UNUSED(void **state)) +static array api_register_valid(void) { - connection_request_event_info info; - struct message_request *request; - array *meta, *functions, *func1, *func2, *args; - struct api_error *err = MALLOC(struct api_error); - char pluginkey[PLUGINKEY_STRING_SIZE] = "012345789ABCDEFH"; + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string("register"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("stze"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("none"))); + + array registerfunc1args = ARRAY_DICT_INIT; + ADD(registerfunc1args, INTEGER_OBJ(1234)); + ADD(registerfunc1args, STRING_OBJ(cstring_copy_string("func1 arg"))); + + array registerfunc1 = ARRAY_DICT_INIT; + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name"))); + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function description"))); + ADD(registerfunc1, ARRAY_OBJ(registerfunc1args)); + + array registerfunc2 = ARRAY_DICT_INIT; + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name"))); + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description"))); + ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT)); + + array registerfunctions = ARRAY_DICT_INIT; + ADD(registerfunctions, ARRAY_OBJ(registerfunc1)); + ADD(registerfunctions, ARRAY_OBJ(registerfunc2)); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(registermeta)); + ADD(request, ARRAY_OBJ(registerfunctions)); + + return request; +} + +static array api_register_wrong_args_size(void) +{ + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string("register"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("stze"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("none"))); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(registermeta)); + + return request; +} - string name = cstring_copy_string("register"); - string description = cstring_copy_string("register a plugin"); - string author = cstring_copy_string("test"); - string license = cstring_copy_string("none"); +static array api_register_wrong_args_type(void) +{ + array registerfunc1 = ARRAY_DICT_INIT; + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name"))); + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function description"))); + ADD(registerfunc1, ARRAY_OBJ(ARRAY_DICT_INIT)); - info.request.msgid = 1; + array registerfunc2 = ARRAY_DICT_INIT; + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name"))); + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description"))); + ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT)); - request = &info.request; - assert_non_null(request); + array registerfunctions = ARRAY_DICT_INIT; + ADD(registerfunctions, ARRAY_OBJ(registerfunc1)); + ADD(registerfunctions, ARRAY_OBJ(registerfunc2)); - info.api_error = *err; - assert_non_null(err); + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("wrong"))); + ADD(request, ARRAY_OBJ(registerfunctions)); - info.api_error.isset = false; + return request; +} - info.con = CALLOC(1, struct connection); - info.con->closed = true; - info.con->id = 1234; - strlcpy(info.con->cc.pluginkeystring, pluginkey, PLUGINKEY_STRING_SIZE+1); - assert_non_null(info.con); +static array api_register_wrong_meta_size(void) +{ + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string("register"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("stze"))); + + array registerfunc1 = ARRAY_DICT_INIT; + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name"))); + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function description"))); + ADD(registerfunc1, ARRAY_OBJ(ARRAY_DICT_INIT)); + + array registerfunc2 = ARRAY_DICT_INIT; + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name"))); + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description"))); + ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT)); + + array registerfunctions = ARRAY_DICT_INIT; + ADD(registerfunctions, ARRAY_OBJ(registerfunc1)); + ADD(registerfunctions, ARRAY_OBJ(registerfunc2)); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(registermeta)); + ADD(request, ARRAY_OBJ(registerfunctions)); + + return request; +} - connect_and_create(info.con->cc.pluginkeystring); +static array api_register_missing_function_description(void) +{ + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string("register"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("stze"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("none"))); + + array registerfunc1 = ARRAY_DICT_INIT; + ADD(registerfunc1, STRING_OBJ(cstring_copy_string("my function name"))); + ADD(registerfunc1, OBJECT_OBJ((object) OBJECT_INIT)); + ADD(registerfunc1, ARRAY_OBJ(ARRAY_DICT_INIT)); + + array registerfunc2 = ARRAY_DICT_INIT; + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function name"))); + ADD(registerfunc2, STRING_OBJ(cstring_copy_string("my sec function description"))); + ADD(registerfunc2, ARRAY_OBJ(ARRAY_DICT_INIT)); + + array registerfunctions = ARRAY_DICT_INIT; + ADD(registerfunctions, ARRAY_OBJ(registerfunc1)); + ADD(registerfunctions, ARRAY_OBJ(registerfunc2)); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(registermeta)); + ADD(request, ARRAY_OBJ(registerfunctions)); + + return request; +} + +static array api_register_empty_functions(void) +{ + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string("register"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("register a plugin"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("stze"))); + ADD(registermeta, STRING_OBJ(cstring_copy_string("none"))); + + array registerfunctions = ARRAY_DICT_INIT; + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(registermeta)); + ADD(request, ARRAY_OBJ(registerfunctions)); + + return request; +} + +void functional_dispatch_handle_register(UNUSED(void **state)) +{ + struct connection *con; + array request; + struct api_error error = ERROR_INIT; + char pluginkey[PLUGINKEY_STRING_SIZE] = "012345789ABCDEFH"; + + con = calloc_or_die(1, sizeof(struct connection)); + con->closed = true; + con->id = 1234; + con->msgid = 4321; + strlcpy(con->cc.pluginkeystring, pluginkey, PLUGINKEY_STRING_SIZE+1); + assert_non_null(con); + + connect_and_create(con->cc.pluginkeystring); assert_int_equal(0, connection_init()); - connection_hashmap_put(info.con->id, info.con); - - /* first level arrays: - * - * [meta] args->obj[0] - * [functions] args->obj[1] - */ - request->params.size = 2; - request->params.obj = CALLOC(2, struct message_object); - request->params.obj[0].type = OBJECT_TYPE_ARRAY; - request->params.obj[1].type = OBJECT_TYPE_ARRAY; - - /* meta array: - * - * [name] - * [desciption] - * [author] - * [license] - */ - meta = &request->params.obj[0].data.params; - meta->size = 4; - meta->obj = CALLOC(meta->size, struct message_object); - meta->obj[0].type = OBJECT_TYPE_STR; - meta->obj[0].data.string = name; - meta->obj[1].type = OBJECT_TYPE_STR; - meta->obj[1].data.string = description; - meta->obj[2].type = OBJECT_TYPE_STR; - meta->obj[2].data.string = author; - meta->obj[3].type = OBJECT_TYPE_STR; - meta->obj[3].data.string = license; - - functions = &request->params.obj[1].data.params; - functions->size = 2; - functions->obj = CALLOC(functions->size, struct message_object); - functions->obj[0].type = OBJECT_TYPE_ARRAY; - functions->obj[1].type = OBJECT_TYPE_ARRAY; - - func1 = &functions->obj[0].data.params; - func1->size = 3; - func1->obj = CALLOC(3, struct message_object); - func1->obj[0].type = OBJECT_TYPE_STR; - func1->obj[0].data.string = cstring_copy_string("my function name"); - func1->obj[1].type = OBJECT_TYPE_STR; - func1->obj[1].data.string = cstring_copy_string("my function description"); - func1->obj[2].type = OBJECT_TYPE_ARRAY; - func1->obj[2].data.params.size = 0; - - func2 = &functions->obj[1].data.params; - func2->size = 3; - func2->obj = CALLOC(3, struct message_object); - func2->obj[0].type = OBJECT_TYPE_STR; - func2->obj[0].data.string = cstring_copy_string("my snd function name"); - func2->obj[1].type = OBJECT_TYPE_STR; - func2->obj[1].data.string = cstring_copy_string("my snd function description"); - func2->obj[2].type = OBJECT_TYPE_ARRAY; - func2->obj[2].data.params.size = 0; + connection_hashmap_put(con->id, con); /* a valid request has to trigger the corresponding response */ - assert_false(info.api_error.isset); - expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL); - assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); + request = api_register_valid(); + assert_false(error.isset); + handle_register(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); /* payload does not consist of two arrays, meta and func */ - request->params.size = 1; - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; - request->params.size = 2; + request = api_register_wrong_args_size(); + assert_false(error.isset); + handle_register(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); /* object has wrong type */ - request->params.obj[0].type = OBJECT_TYPE_STR; - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; - request->params.obj[0].type = OBJECT_TYPE_ARRAY; + request = api_register_wrong_args_type(); + assert_false(error.isset); + handle_register(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); /* meta has wrong size */ - meta->size = 3; - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; - meta->size = 4; - - /* plugin name is very long and contains same random bytes. since - * there are no real constrains for the plugin name, this should work. - */ - size_t len = 8096; - unsigned char *buf = MALLOC_ARRAY(len, unsigned char); - assert_non_null(buf); - randombytes(buf, len); - meta->obj[0].data.string.str = (char*) buf; - meta->obj[0].data.string.length = len; - assert_false(info.api_error.isset); - expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL); - assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_false(info.api_error.isset); - meta->obj[0].data.string = name; - FREE(buf); + request = api_register_wrong_meta_size(); + assert_false(error.isset); + handle_register(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); /* no function description field should fail */ - func1->obj[1].type = OBJECT_TYPE_NIL; - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - func1->obj[1].type = OBJECT_TYPE_STR; - info.api_error.isset = false; - - /* very long function description should work so far */ - string func_desc = func1->obj[1].data.string; - len = 8096; - buf = MALLOC_ARRAY(len, unsigned char); - assert_non_null(buf); - randombytes(buf, len); - func1->obj[1].data.string.str = (char*) buf; - func1->obj[1].data.string.length = len; - assert_false(info.api_error.isset); - expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL); - assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_false(info.api_error.isset); - func1->obj[1].data.string = func_desc; - FREE(buf); + request = api_register_missing_function_description(); + assert_false(error.isset); + handle_register(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); /* registering w/o function, should not work */ - functions->size = 0; - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; - functions->size = 1; - - /* registering function with arguments, should work. the arguments - * remain for the following tests. */ - args = &func1->obj[2].data.params; - args->size = 2; - args->obj = CALLOC(args->size, struct message_object); - args->obj[0].type = OBJECT_TYPE_INT; - args->obj[0].data.integer = 5; - args->obj[1].type = OBJECT_TYPE_STR; - args->obj[1].data.string = cstring_copy_string("foobar"); - assert_false(info.api_error.isset); - expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL); - assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_false(info.api_error.isset); - info.api_error.isset = false; - - /* registering only one function must work */ - functions->size = 1; - assert_false(info.api_error.isset); - expect_check(__wrap_crypto_write, &deserialized, validate_register_response, NULL); - assert_int_equal(0, handle_register(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_false(info.api_error.isset); - info.api_error.isset = false; - functions->size = 2; - - free_params(request->params); + request = api_register_empty_functions(); + assert_false(error.isset); + handle_register(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); + connection_teardown(); - FREE(err); - FREE(info.con); + FREE(con); db_close(); } diff --git a/test/functional/dispatch-handle-result.c b/test/functional/dispatch-handle-result.c index 3918a9b..105ce31 100644 --- a/test/functional/dispatch-handle-result.c +++ b/test/functional/dispatch-handle-result.c @@ -18,155 +18,239 @@ #include "helper-all.h" #include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" #include "rpc/sb-rpc.h" +#include "rpc/connection/connection.h" +#include "api/helpers.h" +#include "api/sb-api.h" +#ifdef __linux__ +#include +#endif #include "helper-all.h" #include "helper-unix.h" #include "helper-validate.h" -static struct plugin *prepare_test(connection_request_event_info *info) +static array api_result_valid(struct plugin * plugin) { - struct api_error err = ERROR_INIT; - info->api_error = err; + array meta = ARRAY_DICT_INIT; + ADD(meta, UINTEGER_OBJ(plugin->callid)); - /* create test plugin that is used for the tests */ - struct plugin *plugin = helper_get_example_plugin(); - helper_register_plugin(plugin); + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); - /* establish fake connection to plugin */ - info->con = CALLOC(1, struct connection); - info->con->closed = true; - connection_hashmap_put(info->con->id, info->con); - strlcpy(info->con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1); - assert_non_null(info->con); + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, ARRAY_OBJ(args)); - expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin); - expect_check(__wrap_crypto_write, &deserialized, validate_run_response, plugin); + return request; +} + +static array api_result_wrong_args_size_small(struct plugin * plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, UINTEGER_OBJ(plugin->callid)); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + + return request; +} + +static array api_result_wrong_args_size_big(struct plugin * plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, UINTEGER_OBJ(plugin->callid)); + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array wrong = ARRAY_DICT_INIT; + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, ARRAY_OBJ(args)); + ADD(request, ARRAY_OBJ(wrong)); + + return request; +} + +static array api_result_wrong_meta_size(struct plugin * plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, UINTEGER_OBJ(plugin->callid)); + ADD(meta, STRING_OBJ(cstring_copy_string("wrong"))); + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_result_wrong_meta_type(struct plugin * plugin) +{ + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("wrong"))); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_result_wrong_meta_callid_type(struct plugin * plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, STRING_OBJ(cstring_copy_string("wrong"))); - helper_build_run_request(&info->request, plugin - ,OBJECT_TYPE_ARRAY /* meta array type */ - ,2 /* meta size */ - ,OBJECT_TYPE_STR /* target plugin key */ - ,OBJECT_TYPE_NIL /* call id type */ - ,OBJECT_TYPE_STR /* function name */ - ,OBJECT_TYPE_ARRAY /* arguments */ - ); + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); - assert_int_equal(0, handle_run(info->con->id, &info->request, info->con->cc.pluginkeystring, &info->api_error)); - free_params(info->request.params); + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, ARRAY_OBJ(args)); - return plugin; + return request; } + void functional_dispatch_handle_result(UNUSED(void **state)) { - connection_request_event_info info; - struct plugin *plugin; + struct api_error error = ERROR_INIT; + array request = ARRAY_DICT_INIT; + struct connection *con; - plugin = prepare_test(&info); + /* create test plugin that is used for the tests */ + struct plugin *plugin = helper_get_example_plugin(); - expect_check(__wrap_crypto_write, &deserialized, validate_result_request, NULL); - expect_check(__wrap_crypto_write, &deserialized, validate_result_response, NULL); + con = calloc_or_die(1, sizeof(struct connection)); + con->closed = true; + con->id = (uint64_t) randommod(281474976710656LL); + con->msgid = 4321; - /* - * The following asserts verifies a legitim run call. In detail, it - * verifies, that the forwarded run request by the server is of proper - * format. - * */ - helper_build_result_request(&info.request, plugin - ,OBJECT_TYPE_ARRAY /* meta array type */ - ,1 /* meta array size */ - ,OBJECT_TYPE_UINT /* call id type */ - ,OBJECT_TYPE_ARRAY /* arguments */ - ); - - assert_int_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); + assert_non_null(con); - /* - * The following asserts verify, that the handle_result method cancels - * as soon as illegitim result calls are processed. A API_ERROR must be - * set in order inform the caller later on. - */ + strlcpy(con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1); - /* size of payload too small */ - info.request.params.size = 1; + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->name.str))); + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->description.str))); + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->author.str))); + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->license.str))); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + array registerarguments = ARRAY_DICT_INIT; + ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); - /* size of payload too big */ - info.request.params.size = 3; + array registerfunc1 = ARRAY_DICT_INIT; + ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->description.str))); + ADD(registerfunc1, ARRAY_OBJ(registerarguments)); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + array registerfunctions = ARRAY_DICT_INIT; + ADD(registerfunctions, ARRAY_OBJ(registerfunc1)); - info.request.params.size = 2; + array registerrequest = ARRAY_DICT_INIT; + ADD(registerrequest, ARRAY_OBJ(registermeta)); + ADD(registerrequest, ARRAY_OBJ(registerfunctions)); - /* wrong meta size */ - helper_request_set_meta_size(&info.request, - OBJECT_TYPE_ARRAY, 2); + connect_to_db(); + assert_int_equal(0, connection_init()); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + error.isset = false; - helper_request_set_meta_size(&info.request, - OBJECT_TYPE_ARRAY, 1); + con->refcount++; - /* wrong meta type */ - helper_request_set_meta_size(&info.request, - OBJECT_TYPE_STR, 1); + connection_hashmap_put(con->id, con); + pluginkeys_hashmap_put(con->cc.pluginkeystring, con->id); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + handle_register(con->id, 123, con->cc.pluginkeystring, registerrequest, + &error); + assert_false(error.isset); + api_free_array(registerrequest); - helper_request_set_meta_size(&info.request, - OBJECT_TYPE_ARRAY, 1); + expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin); - /* wrong callid type */ - helper_request_set_callid(&info.request, OBJECT_TYPE_STR); + // RUN API CALL + array runmeta = ARRAY_DICT_INIT; + ADD(runmeta, STRING_OBJ(cstring_copy_string(plugin->key.str))); + ADD(runmeta, OBJECT_OBJ((object) OBJECT_INIT)); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + array runargs = ARRAY_DICT_INIT; + ADD(runargs, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(runargs, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); - helper_request_set_callid(&info.request, OBJECT_TYPE_UINT); + array runrequest = ARRAY_DICT_INIT; + ADD(runrequest, ARRAY_OBJ(runmeta)); + ADD(runrequest, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(runrequest, ARRAY_OBJ(runargs)); - /* wrong callid */ - info.request.params.obj[0].data.params.obj[0].data.uinteger = 0; + handle_run(con->id, 1234, con->cc.pluginkeystring, runrequest, &error); + api_free_array(runrequest); + + expect_check(__wrap_crypto_write, &deserialized, validate_result_request, NULL); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + request = api_result_valid(plugin); + handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + /* + * The following asserts verify, that the handle_result method cancels + * as soon as illegitim result calls are processed. A API_ERROR must be + * set in order inform the caller later on. + */ - info.request.params.obj[0].data.params.obj[0].data.uinteger = plugin->callid; + /* size of payload too small */ + request = api_result_wrong_args_size_small(plugin); + handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - /* wrong argument type */ - helper_request_set_args_size(&info.request, - OBJECT_TYPE_STR, 2); + /* size of payload too big */ + request = api_result_wrong_args_size_big(plugin); + handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - assert_int_not_equal(0, handle_result(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + /* wrong meta size */ + request = api_result_wrong_meta_size(plugin); + handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - helper_request_set_args_size(&info.request, - OBJECT_TYPE_ARRAY, 2); + /* wrong meta type */ + request = api_result_wrong_meta_type(plugin); + handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); + /* wrong callid type */ + request = api_result_wrong_meta_callid_type(plugin); + handle_result(con->id, 1234, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - free_params(info.request.params); helper_free_plugin(plugin); connection_teardown(); - FREE(info.con); + FREE(con); db_close(); } diff --git a/test/functional/dispatch-handle-run.c b/test/functional/dispatch-handle-run.c index 48f1d11..27ba977 100644 --- a/test/functional/dispatch-handle-run.c +++ b/test/functional/dispatch-handle-run.c @@ -17,132 +17,206 @@ #include #include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" #include "rpc/sb-rpc.h" +#include "rpc/connection/connection.h" +#include "api/helpers.h" +#include "api/sb-api.h" +#ifdef __linux__ +#include +#endif #include "helper-unix.h" #include "helper-all.h" #include "helper-validate.h" -static struct plugin *prepare_test(connection_request_event_info *info) +static array api_run_valid(struct plugin *plugin) { - struct api_error err = ERROR_INIT; - info->api_error = err; + array meta = ARRAY_DICT_INIT; + ADD(meta, STRING_OBJ(cstring_copy_string(plugin->key.str))); + ADD(meta, OBJECT_OBJ((object) OBJECT_INIT)); - /* create test plugin that is used for the tests */ - struct plugin *plugin = helper_get_example_plugin(); - helper_register_plugin(plugin); + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_run_call_not_registered_function(struct plugin *plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, STRING_OBJ(cstring_copy_string(plugin->key.str))); + ADD(meta, OBJECT_OBJ((object) OBJECT_INIT)); + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, STRING_OBJ(cstring_copy_string("wrong"))); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_run_wrong_meta_type(struct plugin *plugin) +{ + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("wrong"))); + ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_run_wrong_meta_size(struct plugin *plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, STRING_OBJ(cstring_copy_string(plugin->key.str))); + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); + + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(request, ARRAY_OBJ(args)); + + return request; +} + +static array api_run_wrong_pluginkey_type(struct plugin *plugin) +{ + array meta = ARRAY_DICT_INIT; + ADD(meta, OBJECT_OBJ((object) OBJECT_INIT)); + ADD(meta, OBJECT_OBJ((object) OBJECT_INIT)); + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(args, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); - /* establish fake connection to plugin */ - info->con = CALLOC(1, struct connection); - info->con->closed = true; - info->con->id = 12345; - connection_hashmap_put(info->con->id, info->con); - strlcpy(info->con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1); - assert_non_null(info->con); + array request = ARRAY_DICT_INIT; + ADD(request, ARRAY_OBJ(meta)); + ADD(request, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(request, ARRAY_OBJ(args)); - return plugin; + return request; } void functional_dispatch_handle_run(UNUSED(void **state)) { - connection_request_event_info info; - struct plugin *plugin; + struct plugin *plugin = helper_get_example_plugin(); + struct connection *con; + struct api_error error = ERROR_INIT; + array request; - plugin = prepare_test(&info); + con = calloc_or_die(1, sizeof(struct connection)); + con->closed = true; + con->id = (uint64_t) randommod(281474976710656LL); + con->msgid = 4321; - expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin); - expect_check(__wrap_crypto_write, &deserialized, validate_run_response, plugin); + assert_non_null(con); - /* - * The following asserts verifies a legitim run call. In detail, it - * verifies, that the forwarded run request by the server is of proper - * format. - * */ - helper_build_run_request(&info.request, plugin - ,OBJECT_TYPE_ARRAY /* meta array type */ - ,2 /* meta size */ - ,OBJECT_TYPE_STR /* target plugin key */ - ,OBJECT_TYPE_NIL /* call id type */ - ,OBJECT_TYPE_STR /* function name */ - ,OBJECT_TYPE_ARRAY /* arguments */ - ); - - //nfo.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error - - assert_int_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_false(info.api_error.isset); + strlcpy(con->cc.pluginkeystring, plugin->key.str, plugin->key.length+1); - /* - * The following asserts verify, that the handle_run method cancels - * as soon as illegitim run calls are processed. A API_ERROR must be - * set in order inform the caller later on. - */ + array registermeta = ARRAY_DICT_INIT; + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->name.str))); + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->description.str))); + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->author.str))); + ADD(registermeta, STRING_OBJ(cstring_copy_string(plugin->license.str))); - /* calling not registered function should fail */ - helper_request_set_function_name(&info.request, OBJECT_TYPE_STR, - "invalid funcion name"); + array registerarguments = ARRAY_DICT_INIT; + ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[0].str))); + ADD(registerarguments, STRING_OBJ(cstring_copy_string(plugin->function->args[1].str))); - assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + array registerfunc1 = ARRAY_DICT_INIT; + ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->name.str))); + ADD(registerfunc1, STRING_OBJ(cstring_copy_string(plugin->function->description.str))); + ADD(registerfunc1, ARRAY_OBJ(registerarguments)); - /* reset to correct request */ - helper_request_set_function_name(&info.request, OBJECT_TYPE_STR, - plugin->function->name.str); + array registerfunctions = ARRAY_DICT_INIT; + ADD(registerfunctions, ARRAY_OBJ(registerfunc1)); - /* meta object has wrong type */ - helper_request_set_meta_size(&info.request, OBJECT_TYPE_STR, 2); + array registerrequest = ARRAY_DICT_INIT; + ADD(registerrequest, ARRAY_OBJ(registermeta)); + ADD(registerrequest, ARRAY_OBJ(registerfunctions)); - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + connect_to_db(); + assert_int_equal(0, connection_init()); - /* reset to correct request */ - helper_request_set_meta_size(&info.request, OBJECT_TYPE_ARRAY, 2); + error.isset = false; - /* meta has wrong size */ - helper_request_set_meta_size(&info.request, OBJECT_TYPE_ARRAY, 3); + con->refcount++; - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + connection_hashmap_put(con->id, con); + pluginkeys_hashmap_put(con->cc.pluginkeystring, con->id); - /* reset to correct request */ - helper_request_set_meta_size(&info.request, OBJECT_TYPE_ARRAY, 2); + handle_register(con->id, 123, con->cc.pluginkeystring, registerrequest, + &error); + assert_false(error.isset); + api_free_array(registerrequest); - /* target plugin key has wrong type */ - helper_request_set_pluginkey_type(&info.request, OBJECT_TYPE_ARRAY, plugin->key.str); + expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin); - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); - info.api_error.isset = false; + request = api_run_valid(plugin); + handle_run(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); - /* reset to correct request */ - helper_request_set_pluginkey_type(&info.request, OBJECT_TYPE_STR, plugin->key.str); - array *meta = &info.request.params.obj[0].data.params; - free_string(meta->obj[0].data.string); + /* + * The following asserts verify, that the handle_run method cancels + * as soon as illegitim run calls are processed. A API_ERROR must be + * set in order inform the caller later on. + */ - /* call_id has wrong type */ - helper_request_set_callid(&info.request, OBJECT_TYPE_BIN); + /* calling not registered function should fail */ + request = api_run_call_not_registered_function(plugin); + handle_run(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - assert_false(info.api_error.isset); - assert_int_not_equal(0, handle_run(info.con->id, &info.request, info.con->cc.pluginkeystring, &info.api_error)); - assert_true(info.api_error.isset); - assert_true(info.api_error.type == API_ERROR_TYPE_VALIDATION); + /* meta object has wrong type */ + request = api_run_wrong_meta_type(plugin); + assert_false(error.isset); + handle_run(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - /* reset to correct request */ - helper_request_set_callid(&info.request, OBJECT_TYPE_NIL); + /* meta has wrong size */ + request = api_run_wrong_meta_size(plugin); + assert_false(error.isset); + handle_run(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); + + /* target plugin key has wrong type */ + request = api_run_wrong_pluginkey_type(plugin); + assert_false(error.isset); + handle_run(con->id, 123, con->cc.pluginkeystring, request, &error); + assert_true(error.isset); + assert_true(error.type == API_ERROR_TYPE_VALIDATION); + error.isset = false; + api_free_array(request); - free_params(info.request.params); helper_free_plugin(plugin); connection_teardown(); + FREE(con); db_close(); } diff --git a/test/functional/dispatch-handle-subscribe.c b/test/functional/dispatch-handle-subscribe.c new file mode 100644 index 0000000..254dfa3 --- /dev/null +++ b/test/functional/dispatch-handle-subscribe.c @@ -0,0 +1,151 @@ +/** + * Copyright (C) 2015 splone UG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include "sb-common.h" +#include "rpc/sb-rpc.h" +#include "rpc/connection/connection.h" +#include "api/helpers.h" +#include "api/sb-api.h" +#ifdef __linux__ +#include +#endif + +#include "helper-unix.h" +#include "helper-all.h" +#include "helper-validate.h" + +static array api_subscribe_valid(void) +{ + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("testname"))); + + return request; +} + +static array api_subscribe_wrong_args_size(void) +{ + array request = ARRAY_DICT_INIT; + ADD(request, STRING_OBJ(cstring_copy_string("testname"))); + ADD(request, STRING_OBJ(cstring_copy_string("wrong"))); + + return request; +} + +static array api_subscribe_wrong_args_type(void) +{ + array request = ARRAY_DICT_INIT; + ADD(request, OBJECT_OBJ((object) OBJECT_INIT)); + + return request; +} + +void functional_dispatch_handle_subscribe(UNUSED(void **state)) +{ + struct plugin *plugin = helper_get_example_plugin(); + struct connection *con1; + struct connection *con2; + struct api_error error = ERROR_INIT; + array request; + + con1 = calloc_or_die(1, sizeof(struct connection)); + con1->id = (uint64_t) randommod(281474976710656LL); + con1->msgid = 4321; + con1->subscribed_events = hashmap_new(cstr_t, ptr_t)(); + + con2 = calloc_or_die(1, sizeof(struct connection)); + con2->id = (uint64_t) randommod(281474976710656LL); + con2->msgid = 4321; + con2->subscribed_events = hashmap_new(cstr_t, ptr_t)(); + + assert_non_null(con1); + assert_non_null(con2); + + strlcpy(con1->cc.pluginkeystring, "ABCD", sizeof("ABCD") - 1); + strlcpy(con2->cc.pluginkeystring, "EFGH", sizeof("EFGH") - 1); + + connect_to_db(); + assert_int_equal(0, connection_init()); + + con1->refcount++; + con2->refcount++; + + connection_hashmap_put(con1->id, con1); + connection_hashmap_put(con2->id, con2); + pluginkeys_hashmap_put(con1->cc.pluginkeystring, con1->id); + pluginkeys_hashmap_put(con2->cc.pluginkeystring, con2->id); + + + + //expect_check(__wrap_crypto_write, &deserialized, validate_run_request, plugin); + + request = api_subscribe_valid(); + handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_subscribe_valid(); + handle_subscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_subscribe_valid(); + handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_subscribe_valid(); + handle_unsubscribe(con2->id, 123, con2->cc.pluginkeystring, request, &error); + assert_false(error.isset); + api_free_array(request); + + request = api_subscribe_wrong_args_size(); + handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_true(error.isset); + error.isset = false; + api_free_array(request); + + request = api_subscribe_wrong_args_type(); + handle_subscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_true(error.isset); + error.isset = false; + api_free_array(request); + + request = api_subscribe_wrong_args_size(); + handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_true(error.isset); + error.isset = false; + api_free_array(request); + + request = api_subscribe_wrong_args_type(); + handle_unsubscribe(con1->id, 123, con1->cc.pluginkeystring, request, &error); + assert_true(error.isset); + error.isset = false; + api_free_array(request); + + con1->closed = true; + con2->closed = true; + + hashmap_free(cstr_t, ptr_t)(con1->subscribed_events); + hashmap_free(cstr_t, ptr_t)(con2->subscribed_events); + + helper_free_plugin(plugin); + connection_teardown(); + FREE(con1); + FREE(con2); + db_close(); +} diff --git a/test/functional/msgpack-rpc-helper.c b/test/functional/msgpack-rpc-helper.c new file mode 100644 index 0000000..7ef67e3 --- /dev/null +++ b/test/functional/msgpack-rpc-helper.c @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2015 splone UG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "helper-unix.h" +#include "sb-common.h" +#include "api/helpers.h" +#include "rpc/msgpack/helpers.h" + +static object helper_valid_object_all_type(void) +{ + array test = ARRAY_DICT_INIT; + + dictionary dict_test = ARRAY_DICT_INIT; + + dictionary dict_abc = ARRAY_DICT_INIT; + PUT(dict_abc, "123", INTEGER_OBJ(123)); + PUT(dict_abc, "456", STRING_OBJ(cstring_copy_string("456"))); + + dictionary dict_def = ARRAY_DICT_INIT; + PUT(dict_def, "789", INTEGER_OBJ(789)); + PUT(dict_def, "987", STRING_OBJ(cstring_copy_string("987"))); + + dictionary dict_ghi = ARRAY_DICT_INIT; + PUT(dict_ghi, "654", INTEGER_OBJ(654)); + PUT(dict_ghi, "321", STRING_OBJ(cstring_copy_string("321"))); + + PUT(dict_test, "abc", DICTIONARY_OBJ(dict_abc)); + PUT(dict_test, "def", DICTIONARY_OBJ(dict_def)); + PUT(dict_test, "ghi", DICTIONARY_OBJ(dict_ghi)); + + array array_abc = ARRAY_DICT_INIT; + ADD(array_abc, OBJECT_OBJ((object) OBJECT_INIT)); + ADD(array_abc, FLOATING_OBJ(1.2345)); + ADD(array_abc, UINTEGER_OBJ(1234)); + ADD(array_abc, INTEGER_OBJ(-1234)); + + array array_def = ARRAY_DICT_INIT; + ADD(array_def, STRING_OBJ(cstring_copy_string("test"))); + ADD(array_def, BOOLEAN_OBJ(true)); + ADD(array_def, BOOLEAN_OBJ(false)); + + ADD(test, ARRAY_OBJ(array_abc)); + ADD(test, ARRAY_OBJ(array_def)); + ADD(test, DICTIONARY_OBJ(dict_test)); + + return ARRAY_OBJ(test); +} + +static object helper_valid_notification_object(void) +{ + array notification = ARRAY_DICT_INIT; + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string("test"))); + ADD(args, BOOLEAN_OBJ(true)); + ADD(args, BOOLEAN_OBJ(false)); + + ADD(notification, UINTEGER_OBJ(2)); + ADD(notification, STRING_OBJ(cstring_copy_string("test"))); + ADD(notification, ARRAY_OBJ(args)); + + return ARRAY_OBJ(notification); +} + +static object helper_valid_request_object(void) +{ + array request = ARRAY_DICT_INIT; + + array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(cstring_copy_string("test"))); + ADD(args, BOOLEAN_OBJ(true)); + ADD(args, BOOLEAN_OBJ(false)); + + ADD(request, UINTEGER_OBJ(0)); + ADD(request, UINTEGER_OBJ(1234)); + ADD(request, STRING_OBJ(cstring_copy_string("test"))); + ADD(request, ARRAY_OBJ(args)); + + return ARRAY_OBJ(request); +} + + +void functional_msgpack_rpc_helper(UNUSED(void **state)) +{ + struct api_error err = ERROR_INIT; + msgpack_sbuffer sbuf; + msgpack_packer pk; + + msgpack_sbuffer_init(&sbuf); + msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); + + // declare test object with all available types + + object serialize_object = helper_valid_object_all_type(); + msgpack_rpc_from_object(serialize_object, &pk); + + msgpack_zone mempool; + msgpack_zone_init(&mempool, 2048); + + msgpack_object deserialized; + msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); + msgpack_sbuffer_clear(&sbuf); + + array deserialized_array; + assert_true(msgpack_rpc_to_array(&deserialized, &deserialized_array)); + + object deserialized_object; + assert_true(msgpack_rpc_to_object(&deserialized, &deserialized_object)); + + // test response serialize without error set + msgpack_rpc_serialize_response(1234, &err, deserialized_object, &pk); + msgpack_sbuffer_clear(&sbuf); + + // test response serialize with error set + error_set(&err, API_ERROR_TYPE_EXCEPTION, "Error Error Error"); + msgpack_rpc_serialize_response(1234, &err, deserialized_object, &pk); + msgpack_sbuffer_clear(&sbuf); + + // notification helper tests + err.isset = false; + object notification_object = helper_valid_notification_object(); + msgpack_rpc_from_object(notification_object, &pk); + msgpack_object deserialized_not; + msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized_not); + assert_true(msgpack_rpc_is_notification(&deserialized_not)); + assert_null(msgpack_rpc_msg_id(&deserialized_not)); + uint64_t msgid_not = 1234; + msgpack_rpc_validate(&msgid_not, &deserialized_not, &err); + assert_false(err.isset); + msgpack_object *deserialized_not_args = msgpack_rpc_args(&deserialized_not); + assert_non_null(deserialized_not_args); + msgpack_object *deserialized_not_method = msgpack_rpc_method(&deserialized_not); + assert_non_null(deserialized_not_method); + msgpack_sbuffer_clear(&sbuf); + + // notification helper tests + err.isset = false; + object request_object = helper_valid_request_object(); + msgpack_rpc_from_object(request_object, &pk); + msgpack_object deserialized_req; + msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized_req); + assert_false(msgpack_rpc_is_notification(&deserialized_req)); + assert_non_null(msgpack_rpc_msg_id(&deserialized_req)); + uint64_t msgid_req = 1234; + msgpack_rpc_validate(&msgid_req, &deserialized_req, &err); + assert_false(err.isset); + msgpack_object *deserialized_req_args = msgpack_rpc_args(&deserialized_req); + assert_non_null(deserialized_req_args); + msgpack_object *deserialized_req_method = msgpack_rpc_method(&deserialized_req); + assert_non_null(deserialized_req_method); + + msgpack_sbuffer_clear(&sbuf); + + api_free_object(serialize_object); + api_free_array(deserialized_array); + api_free_object(deserialized_object); + api_free_object(notification_object); + api_free_object(request_object); + msgpack_zone_destroy(&mempool); + msgpack_sbuffer_destroy(&sbuf); +} diff --git a/test/helper-all.c b/test/helper-all.c index ac3152c..a2dcd65 100644 --- a/test/helper-all.c +++ b/test/helper-all.c @@ -18,6 +18,8 @@ #include "sb-common.h" #include "rpc/db/sb-db.h" +#include "api/helpers.h" +#include "api/sb-api.h" #include "helper-unix.h" #include "helper-all.h" @@ -69,33 +71,30 @@ void connect_and_create(char *pluginkey) struct plugin *helper_get_example_plugin(void) { - struct plugin *p = MALLOC(struct plugin); - struct function *f = MALLOC(struct function); - - if (!p || !f) - LOG_ERROR("[test] Failed to alloc mem for example plugin.\n"); + struct plugin *p = malloc_or_die(sizeof(struct plugin)); + struct function *f = malloc_or_die(sizeof(struct function)); p->key = (string) {.str = "0123456789ABCDEF", - .length = sizeof("0123456789ABCDEF") - 1}; + .length = sizeof("0123456789ABCDEF") - 1}; p->name = (string) {.str = "plugin name", - .length = sizeof("plugin name") - 1}; + .length = sizeof("plugin name") - 1}; p->description = (string) {.str = "plugin desc", - .length = sizeof("plugin desc") - 1}; + .length = sizeof("plugin desc") - 1}; p->license = (string) {.str = "plugin license", - .length = sizeof("plugin license") - 1}; + .length = sizeof("plugin license") - 1}; p->author = (string) {.str = "plugin author", - .length = sizeof("plugin author") - 1}; + .length = sizeof("plugin author") - 1}; p->callid = 0; f->name = (string) {.str = "function name", - .length = sizeof("function name") - 1}; + .length = sizeof("function name") - 1}; f->description = (string) {.str = "function desc", - .length = sizeof("function desc") - 1}; + .length = sizeof("function desc") - 1}; f->args[0] = (string) {.str = "arg 1", - .length = sizeof("arg 1") - 1}; + .length = sizeof("arg 1") - 1}; f->args[1] = (string) {.str = "arg 2", - .length = sizeof("arg 2") - 1}; + .length = sizeof("arg 2") - 1}; p->function = f; return p; @@ -106,221 +105,3 @@ void helper_free_plugin(struct plugin *p) FREE(p->function); FREE(p); } - - -/* Builds a register call and executes handle_register in order - * to register the plugin. */ -void helper_register_plugin(struct plugin *p) -{ - connection_request_event_info info; - struct message_request *register_request; - array *meta, *functions, *func1, *args; - struct api_error err = ERROR_INIT; - - info.request.msgid = 1; - register_request = &info.request; - assert_non_null(register_request); - - info.api_error = err; - - info.con = CALLOC(1, struct connection); - info.con->closed = true; - info.con->msgid = 1; - info.con->id = (uint64_t) randommod(281474976710656LL); - - assert_non_null(info.con); - - strlcpy(info.con->cc.pluginkeystring, p->key.str, p->key.length+1); - - connect_to_db(); - assert_int_equal(0, connection_init()); - - /* first level arrays: - * - * [meta] args->obj[0] - * [functions] args->obj[1] - */ - register_request->params.size = 2; - register_request->params.obj = CALLOC(2, struct message_object); - register_request->params.obj[0].type = OBJECT_TYPE_ARRAY; - register_request->params.obj[1].type = OBJECT_TYPE_ARRAY; - - /* meta array: - * - * [name] - * [desciption] - * [author] - * [license] - */ - meta = ®ister_request->params.obj[0].data.params; - meta->size = 4; - meta->obj = CALLOC(meta->size, struct message_object); - meta->obj[0].type = OBJECT_TYPE_STR; - meta->obj[0].data.string = cstring_copy_string(p->name.str); - meta->obj[1].type = OBJECT_TYPE_STR; - meta->obj[1].data.string = cstring_copy_string(p->description.str); - meta->obj[2].type = OBJECT_TYPE_STR; - meta->obj[2].data.string = cstring_copy_string(p->author.str); - meta->obj[3].type = OBJECT_TYPE_STR; - meta->obj[3].data.string = cstring_copy_string(p->license.str); - - functions = ®ister_request->params.obj[1].data.params; - functions->size = 1; - functions->obj = CALLOC(functions->size, struct message_object); - functions->obj[0].type = OBJECT_TYPE_ARRAY; - - func1 = &functions->obj[0].data.params; - func1->size = 3; - func1->obj = CALLOC(3, struct message_object); - func1->obj[0].type = OBJECT_TYPE_STR; - func1->obj[0].data.string = cstring_copy_string(p->function->name.str); - func1->obj[1].type = OBJECT_TYPE_STR; - func1->obj[1].data.string = cstring_copy_string(p->function->description.str); - func1->obj[2].type = OBJECT_TYPE_ARRAY; - - /* function arguments */ - args = &func1->obj[2].data.params; - args->size = 2; - args->obj = CALLOC(2, struct message_object); - args->obj[0].type = OBJECT_TYPE_STR; - args->obj[0].data.string = cstring_copy_string(p->function->args[0].str); - args->obj[1].type = OBJECT_TYPE_STR; - args->obj[1].data.string = cstring_copy_string(p->function->args[1].str); - - /* before running function, it must be registered successfully */ - info.api_error.isset = false; - expect_check(__wrap_crypto_write, &deserialized, - validate_register_response, - NULL); - - info.con->refcount++; - - connection_hashmap_put(info.con->id, info.con); - pluginkeys_hashmap_put(info.con->cc.pluginkeystring, info.con->id); - - assert_int_equal(0, handle_register(info.con->id, &info.request, - info.con->cc.pluginkeystring, &info.api_error)); - assert_false(info.api_error.isset); - - //hashmap_put(uint64_t, ptr_t)(connections, info.con->id, info.con); - free_params(register_request->params); - //FREE(args->obj); - //FREE(func1->obj); - //FREE(functions->obj); - //FREE(meta->obj); - //FREE(register_request->params.obj); -} - -/* Builds a run request */ -void helper_build_run_request(struct message_request *rr, - struct plugin *plugin, - message_object_type metaarraytype, - size_t metasize, - message_object_type metaclientidtype, - message_object_type metacallidtype, - message_object_type functionnametype, - message_object_type argstype -) -{ - array argsarray = ARRAY_INIT; - array *meta; - - rr->msgid = 1; - - rr->params.size = 3; - rr->params.obj = CALLOC(rr->params.size, struct message_object); - rr->params.obj[0].type = metaarraytype; - - meta = &rr->params.obj[0].data.params; - meta->size = metasize; - meta->obj = CALLOC(meta->size, struct message_object); - meta->obj[0].type = metaclientidtype; - meta->obj[0].data.string = cstring_copy_string(plugin->key.str); - meta->obj[1].type = metacallidtype; - - rr->params.obj[1].type = functionnametype; - rr->params.obj[1].data.string = cstring_copy_string(plugin->function->name.str); - - argsarray.size = 2; - argsarray.obj = CALLOC(argsarray.size, struct message_object); - argsarray.obj[0].type = OBJECT_TYPE_STR; /* first argument */ - argsarray.obj[0].data.string = cstring_copy_string(plugin->function->args[0].str); - argsarray.obj[1].type = OBJECT_TYPE_STR; /* second argument */ - argsarray.obj[1].data.string = cstring_copy_string(plugin->function->args[1].str); - - rr->params.obj[2].type = argstype; - rr->params.obj[2].data.params = argsarray; -} - - -void helper_build_result_request(struct message_request *rr, - struct plugin *plugin, - message_object_type metaarraytype, - size_t metasize, - message_object_type metacallidtype, - message_object_type argstype) -{ - array argsarray = ARRAY_INIT; - array *meta; - - rr->msgid = 1; - - rr->params.size = 2; - rr->params.obj = CALLOC(rr->params.size, struct message_object); - rr->params.obj[0].type = metaarraytype; - meta = &rr->params.obj[0].data.params; - meta->size = metasize; - meta->obj = CALLOC(meta->size, struct message_object); - meta->obj[0].type = metacallidtype; - meta->obj[0].data.uinteger = plugin->callid; - - argsarray.size = 2; - argsarray.obj = CALLOC(argsarray.size, struct message_object); - argsarray.obj[0].type = OBJECT_TYPE_STR; /* first argument */ - argsarray.obj[0].data.string = cstring_copy_string(plugin->function->args[0].str); - argsarray.obj[1].type = OBJECT_TYPE_STR; /* second argument */ - argsarray.obj[1].data.string = cstring_copy_string(plugin->function->args[1].str); - - rr->params.obj[1].type = argstype; - rr->params.obj[1].data.params = argsarray; -} - -void helper_request_set_callid(struct message_request *rr, - message_object_type callidtype) -{ - array *meta = &rr->params.obj[0].data.params; - - meta->obj[0].type = callidtype; -} - -void helper_request_set_meta_size(struct message_request *rr, - message_object_type metatype, uint64_t metasize) -{ - rr->params.obj[0].type = metatype; - rr->params.obj[0].data.params.size = metasize; -} - -void helper_request_set_args_size(struct message_request *rr, - message_object_type argstype, uint64_t argssize) -{ - rr->params.obj[1].type = argstype; - rr->params.obj[1].data.params.size = argssize; -} - -void helper_request_set_function_name(struct message_request *rr, - message_object_type nametype, char *name) -{ - free_string(rr->params.obj[1].data.string); - rr->params.obj[1].type = nametype; - rr->params.obj[1].data.string = cstring_copy_string(name); -} - -void helper_request_set_pluginkey_type(struct message_request *rr, - message_object_type pluginkeytype, char *pluginkey) -{ - array *meta = &rr->params.obj[0].data.params; - - free_string(meta->obj[0].data.string); - meta->obj[0].data.string = cstring_copy_string(pluginkey); - meta->obj[0].type = pluginkeytype; -} diff --git a/test/helper-all.h b/test/helper-all.h index 9974492..1931b9f 100644 --- a/test/helper-all.h +++ b/test/helper-all.h @@ -44,33 +44,3 @@ void register_test_function(void); struct plugin *helper_get_example_plugin(void); void helper_free_plugin(struct plugin *p); void helper_register_plugin(struct plugin *p); - -/* some helper functions to build and manipulate requests */ -void helper_request_set_args_size(struct message_request *rr, - message_object_type argstype, uint64_t argssize); -void helper_request_set_meta_size(struct message_request *rr, - message_object_type metatype, uint64_t metasize); -void helper_request_set_callid(struct message_request *rr, - message_object_type callidtype); -void helper_request_set_pluginkey_type(struct message_request *rr, - message_object_type pluginkeytype, char *pluginkey); -void helper_request_set_function_name(struct message_request *rr, - message_object_type nametype, char *name); - - -void helper_build_result_request(struct message_request *rr, - struct plugin *plugin, - message_object_type metaarraytype, - size_t metasize, - message_object_type metacallidtype, - message_object_type argstype); -void helper_build_run_request(struct message_request *rr, - struct plugin *plugin, - message_object_type metaarraytype, - size_t metasize, - message_object_type metaclientidtype, - message_object_type metacallidtype, - message_object_type functionnametype, - message_object_type argstype); - - diff --git a/test/helper-validate.c b/test/helper-validate.c index aac08e5..9fc7ce8 100644 --- a/test/helper-validate.c +++ b/test/helper-validate.c @@ -15,244 +15,130 @@ */ #include "rpc/db/sb-db.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" +#include "api/helpers.h" +#include "rpc/msgpack/helpers.h" #include "helper-unix.h" #include "helper-all.h" -int validate_register_response(const unsigned long data1, - UNUSED(const unsigned long data2)) -{ - struct msgpack_object *deserialized = (struct msgpack_object *) data1; - struct message_object request; - array params; - - wrap_crypto_write = true; - - assert_int_equal(0, unpack_params(deserialized, ¶ms)); - - /* msgpack request needs to be 1 */ - assert_true(params.obj[0].type == OBJECT_TYPE_UINT); - assert_int_equal(1, params.obj[0].data.uinteger); - - /* msg id which is random */ - assert_true(params.obj[1].type == OBJECT_TYPE_UINT); - - /* next field is NIL */ - assert_true(params.obj[2].type == OBJECT_TYPE_NIL); - - /* followed by an empty array */ - request = params.obj[3]; - assert_true(request.type == OBJECT_TYPE_ARRAY); - assert_int_equal(0, request.data.params.size); - - free_params(params); - - return (1); -} int validate_run_request(const unsigned long data1, UNUSED(const unsigned long data2)) { struct msgpack_object *deserialized = (struct msgpack_object *) data1; struct plugin *p = (struct plugin *) data2; - struct message_object meta, request, func, args; - array params; - assert_int_equal(0, unpack_params(deserialized, ¶ms)); + array message; + object request, meta, func, args; + + msgpack_rpc_to_array(deserialized, &message); /* msgpack request needs to be 0 */ - assert_true(params.obj[0].type == OBJECT_TYPE_UINT); - assert_int_equal(0, params.obj[0].data.uinteger); + assert_true(message.items[0].type == OBJECT_TYPE_UINT); + assert_int_equal(0, message.items[0].data.uinteger); /* msg id which is random */ - assert_true(params.obj[1].type == OBJECT_TYPE_UINT); + assert_true(message.items[1].type == OBJECT_TYPE_UINT); /* run call */ - assert_true(params.obj[2].type == OBJECT_TYPE_STR); - assert_string_equal(params.obj[2].data.string.str, "run"); + assert_true(message.items[2].type == OBJECT_TYPE_STR); + assert_string_equal(message.items[2].data.string.str, "run"); /* first level arrays: * * [meta] args->obj[0] * [functions] args->obj[1] */ - request = params.obj[3]; + request = message.items[3]; assert_true(request.type == OBJECT_TYPE_ARRAY); - assert_int_equal(3, request.data.params.size); + assert_int_equal(3, request.data.array.size); - meta = request.data.params.obj[0]; + meta = request.data.array.items[0]; assert_true(meta.type == OBJECT_TYPE_ARRAY); - assert_int_equal(2, meta.data.params.size); + assert_int_equal(2, meta.data.array.size); /* client2 id should be nil */ - assert_true(meta.data.params.obj[0].type == OBJECT_TYPE_NIL); + assert_true(meta.data.array.items[0].type == OBJECT_TYPE_NIL); /* verify that the server sent a proper callid */ - assert_true(meta.data.params.obj[1].type == OBJECT_TYPE_UINT); + assert_true(meta.data.array.items[1].type == OBJECT_TYPE_UINT); /* since the callid must be forwarded to the client1, the original * sender of the rpc call, we need to push the callid on the cmocka test * stack. this allows verifying of it later on */ - uint64_t callid = meta.data.params.obj[1].data.uinteger; - will_return(__wrap_loop_wait_for_response, OBJECT_TYPE_UINT); - will_return(__wrap_loop_wait_for_response, callid); - expect_value(validate_run_response, response.data.params.obj[0].data.uinteger, callid); + uint64_t callid = meta.data.array.items[1].data.uinteger; + will_return(__wrap_loop_process_events_until, OBJECT_TYPE_UINT); + will_return(__wrap_loop_process_events_until, callid); + p->callid = callid; /* the function to call on client2 side */ - func = request.data.params.obj[1]; + func = request.data.array.items[1]; assert_true(func.type == OBJECT_TYPE_STR); assert_string_equal(func.data.string.str, p->function->name.str); /* the function arguments to pass to client2 side */ - assert_true(request.data.params.obj[2].type == OBJECT_TYPE_ARRAY); - assert_int_equal(2, request.data.params.obj[2].data.params.size); + assert_true(request.data.array.items[2].type == OBJECT_TYPE_ARRAY); + assert_int_equal(2, request.data.array.items[2].data.array.size); - args = request.data.params.obj[2]; - assert_true(args.data.params.obj[0].type == OBJECT_TYPE_STR); - assert_string_equal(args.data.params.obj[0].data.string.str, p->function->args[0].str); + args = request.data.array.items[2]; + assert_true(args.data.array.items[0].type == OBJECT_TYPE_STR); + assert_string_equal(args.data.array.items[0].data.string.str, p->function->args[0].str); - assert_true(args.data.params.obj[1].type == OBJECT_TYPE_STR); - assert_string_equal(args.data.params.obj[1].data.string.str, p->function->args[1].str); + assert_true(args.data.array.items[1].type == OBJECT_TYPE_STR); + assert_string_equal(args.data.array.items[1].data.string.str, p->function->args[1].str); - free_params(params); + api_free_array(message); return (1); } -int validate_run_response(const unsigned long data1, - UNUSED(const unsigned long data2)) -{ - struct msgpack_object *deserialized = (struct msgpack_object *) data1; - struct message_object response; - struct plugin *p = (struct plugin *) data2; - array params; - - wrap_crypto_write = true; - - assert_int_equal(0, unpack_params(deserialized, ¶ms)); - - /* msgpack response needs to be 1 */ - assert_true(params.obj[0].type == OBJECT_TYPE_UINT); - assert_int_equal(1, params.obj[0].data.uinteger); - - /* msg id which is random */ - assert_true(params.obj[1].type == OBJECT_TYPE_UINT); - - /* method should be NIL */ - assert_true(params.obj[2].type == OBJECT_TYPE_NIL); - - /* first level arrays: - * - * [meta] args->obj[0] - */ - response = params.obj[3]; - assert_true(response.type == OBJECT_TYPE_ARRAY); - assert_int_equal(1, response.data.params.size); - - /* the server must send a call id, store the callid for further - * validation */ - assert_true(response.data.params.obj[0].type == OBJECT_TYPE_UINT); - check_expected(response.data.params.obj[0].data.uinteger); - //TODO validate callid against plugin->callid - if (p) { - assert_true(response.data.params.obj[0].data.uinteger == p->callid); - } - - free_params(params); - - return (1); -} - int validate_result_request(const unsigned long data1, UNUSED(const unsigned long data2)) { struct msgpack_object *deserialized = (struct msgpack_object *) data1; - struct message_object meta, request; - array params; + array message; + object meta, request; - assert_int_equal(0, unpack_params(deserialized, ¶ms)); + msgpack_rpc_to_array(deserialized, &message); /* msgpack request needs to be 0 */ - assert_true(params.obj[0].type == OBJECT_TYPE_UINT); - assert_int_equal(0, params.obj[0].data.uinteger); + assert_true(message.items[0].type == OBJECT_TYPE_UINT); + assert_int_equal(0, message.items[0].data.uinteger); /* msg id which is random */ - assert_true(params.obj[1].type == OBJECT_TYPE_UINT); + assert_true(message.items[1].type == OBJECT_TYPE_UINT); /* run call */ - assert_true(params.obj[2].type == OBJECT_TYPE_STR); - assert_string_equal(params.obj[2].data.string.str, "result"); + assert_true(message.items[2].type == OBJECT_TYPE_STR); + assert_string_equal(message.items[2].data.string.str, "result"); /* first level arrays: * * [meta] args->obj[0] * [functions] args->obj[1] */ - request = params.obj[3]; + request = message.items[3]; assert_true(request.type == OBJECT_TYPE_ARRAY); - assert_int_equal(2, request.data.params.size); + assert_int_equal(2, request.data.array.size); - meta = request.data.params.obj[0]; + meta = request.data.array.items[0]; assert_true(meta.type == OBJECT_TYPE_ARRAY); - assert_int_equal(1, meta.data.params.size); + assert_int_equal(1, meta.data.array.size); /* client2 id should be nil */ - assert_true(meta.data.params.obj[0].type == OBJECT_TYPE_UINT); + assert_true(meta.data.array.items[0].type == OBJECT_TYPE_UINT); /* since the callid must be forwarded to the client1, the original * sender of the rpc call, we need to push the callid on the cmocka test * stack. this allows verifying of it later on */ - uint64_t callid = meta.data.params.obj[0].data.uinteger; + uint64_t callid = meta.data.array.items[0].data.uinteger; - will_return(__wrap_loop_wait_for_response, OBJECT_TYPE_UINT); - will_return(__wrap_loop_wait_for_response, callid); - expect_value(validate_result_response, response.data.params.obj[0].data.uinteger, callid); + will_return(__wrap_loop_process_events_until, OBJECT_TYPE_UINT); + will_return(__wrap_loop_process_events_until, callid); - free_params(params); + api_free_array(message); return (1); } - -int validate_result_response(const unsigned long data1, - UNUSED(const unsigned long data2)) -{ - struct msgpack_object *deserialized = (struct msgpack_object *) data1; - struct message_object response; - array params; - - wrap_crypto_write = true; - - assert_int_equal(0, unpack_params(deserialized, ¶ms)); - - /* msgpack response needs to be 1 */ - assert_true(params.obj[0].type == OBJECT_TYPE_UINT); - assert_int_equal(1, params.obj[0].data.uinteger); - - /* msg id which is random */ - assert_true(params.obj[1].type == OBJECT_TYPE_UINT); - - /* method should be NIL */ - assert_true(params.obj[2].type == OBJECT_TYPE_NIL); - - /* first level arrays: - * - * [meta] args->obj[0] - */ - response = params.obj[3]; - assert_true(response.type == OBJECT_TYPE_ARRAY); - assert_int_equal(1, response.data.params.size); - - /* the server must send a call id, store the callid for further - * validation */ - assert_true(response.data.params.obj[0].type == OBJECT_TYPE_UINT); - check_expected(response.data.params.obj[0].data.uinteger); - - free_params(params); - - return (1); -} - - diff --git a/test/main.c b/test/main.c index 75895c6..c406bf0 100644 --- a/test/main.c +++ b/test/main.c @@ -17,8 +17,10 @@ #include "test-list.h" #include "helper-unix.h" #include "sb-common.h" +#include "main.h" int8_t verbose_level; +loop main_loop; int main(UNUSED(int argc), UNUSED(char **argv)) { diff --git a/test/test-list.h b/test/test-list.h index 28c9532..a8408f4 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -20,28 +20,7 @@ void unit_server_start(void **state); void unit_server_stop(void **state); -void unit_pack_string(void **state); -void unit_pack_int(void **state); -void unit_pack_uint(void **state); -void unit_pack_array(void **state); -void unit_pack_nil(void **state); -void unit_pack_float(void **state); -void unit_pack_bool(void **state); -void unit_unpack_string(void **state); -void unit_unpack_uint(void **state); -void unit_unpack_array(void **state); void unit_dispatch_table_get(void **state); -void unit_event_queue_put(void **state); -void unit_event_queue_get(void **state); -void unit_regression_issue_60(void **state); -void unit_message_deserialize_request(void **state); -void unit_message_deserialize_response(void **state); -void unit_message_deserialize_error_response(void **state); -void unit_message_serialize_request(void **state); -void unit_message_serialize_response(void **state); -void unit_message_serialize_error_response(void **state); -void unit_message_is_request(void **state); -void unit_message_is_response(void **state); void functional_client_connect(void **state); void functional_db_connect(void **state); @@ -55,6 +34,9 @@ void functional_filesystem_save_sync(void **state); void functional_dispatch_handle_register(void **state); void functional_dispatch_handle_run(void **state); void functional_dispatch_handle_result(void **state); +void functional_dispatch_handle_subscribe(void **state); +void functional_dispatch_handle_broadcast(void **state); +void functional_msgpack_rpc_helper(void **state); void functional_crypto(void **state); void functional_confparse(void **state); void functional_db_whitelist(void **state); @@ -63,23 +45,6 @@ const struct CMUnitTest tests[] = { cmocka_unit_test(unit_dispatch_table_get), cmocka_unit_test(unit_server_start), cmocka_unit_test(unit_server_stop), - cmocka_unit_test(unit_pack_string), - cmocka_unit_test(unit_pack_int), - cmocka_unit_test(unit_pack_uint), - cmocka_unit_test(unit_pack_nil), - cmocka_unit_test(unit_pack_float), - cmocka_unit_test(unit_pack_bool), - cmocka_unit_test(unit_pack_array), - cmocka_unit_test(unit_regression_issue_60), - cmocka_unit_test(unit_event_queue_put), - cmocka_unit_test(unit_message_deserialize_request), - cmocka_unit_test(unit_message_deserialize_response), - cmocka_unit_test(unit_message_deserialize_error_response), - cmocka_unit_test(unit_message_serialize_request), - cmocka_unit_test(unit_message_serialize_response), - cmocka_unit_test(unit_message_serialize_error_response), - cmocka_unit_test(unit_message_is_request), - cmocka_unit_test(unit_message_is_response), cmocka_unit_test(functional_db_connect), cmocka_unit_test(functional_db_plugin_add), cmocka_unit_test(functional_db_pluginkey_verify), @@ -91,6 +56,9 @@ const struct CMUnitTest tests[] = { cmocka_unit_test(functional_dispatch_handle_register), cmocka_unit_test(functional_dispatch_handle_run), cmocka_unit_test(functional_dispatch_handle_result), + cmocka_unit_test(functional_dispatch_handle_subscribe), + cmocka_unit_test(functional_dispatch_handle_broadcast), + cmocka_unit_test(functional_msgpack_rpc_helper), cmocka_unit_test(functional_crypto), cmocka_unit_test(functional_confparse), cmocka_unit_test(functional_db_whitelist), diff --git a/test/unit/event-queue-get.c b/test/unit/event-queue-get.c deleted file mode 100644 index f697075..0000000 --- a/test/unit/event-queue-get.c +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "sb-common.h" -#include "rpc/sb-rpc.h" -#include "helper-unix.h" - -equeue *equeue_root; -equeue *equeue_child_one; -static api_event events; - -void unit_event_queue_get(UNUSED(void **state)) -{ - equeue_root = equeue_new(NULL); - assert_non_null(equeue_root); - equeue_child_one = equeue_new(equeue_root); - assert_non_null(equeue_child_one); - - assert_int_not_equal(0, equeue_put(equeue_child_one, events)); - assert_false(TAILQ_EMPTY(&equeue_root->head)); - equeue_get(equeue_child_one); - assert_true(TAILQ_EMPTY(&equeue_root->head)); - - equeue_free(equeue_root); - equeue_free(equeue_child_one); -} diff --git a/test/unit/event-queue-put.c b/test/unit/event-queue-put.c deleted file mode 100644 index 5218f70..0000000 --- a/test/unit/event-queue-put.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include "sb-common.h" -#include "rpc/sb-rpc.h" -#include "helper-unix.h" - - -equeue *equeue_root; -equeue *equeue_child_one; -equeue *equeue_child_two; -static api_event events; - -void unit_event_queue_put(UNUSED(void **state)) -{ - equeue_root = equeue_new(NULL); - assert_non_null(equeue_root); - equeue_child_one = equeue_new(equeue_root); - assert_non_null(equeue_child_one); - equeue_child_two = equeue_new(equeue_root); - assert_non_null(equeue_child_two); - - assert_int_not_equal(0, equeue_put(equeue_root, events)); - assert_int_not_equal(0, equeue_put(NULL, events)); - assert_int_equal(0, equeue_put(equeue_child_one, events)); - - equeue_free(equeue_root); - equeue_free(equeue_child_one); - equeue_free(equeue_child_two); -} diff --git a/test/unit/message-deserialize-error-response.c b/test/unit/message-deserialize-error-response.c deleted file mode 100644 index c86929e..0000000 --- a/test/unit/message-deserialize-error-response.c +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_deserialize_error_response(UNUSED(void **state)) -{ - struct api_error error = ERROR_INIT; - msgpack_sbuffer sbuf; - msgpack_packer pk; - msgpack_zone mempool; - msgpack_object deserialized; - struct message_response response; - - msgpack_sbuffer_init(&sbuf); - msgpack_zone_init(&mempool, 2048); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - assert_true(message_is_error_response(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - free_params(response.params); - - /* wrong type type */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_nil(&pk); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong type */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong msgid */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_int(&pk, -1234); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong msgid value*/ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, UINT32_MAX); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong params */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_nil(&pk); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong params */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* null input params */ - /* null input params */ - assert_int_not_equal(0, message_deserialize_error_response(&response, - &deserialized, NULL)); - assert_int_not_equal(0, message_deserialize_error_response(&response, NULL, - &error)); - assert_int_not_equal(0, message_deserialize_error_response(NULL, - &deserialized, &error)); - - msgpack_zone_destroy(&mempool); - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-deserialize-request.c b/test/unit/message-deserialize-request.c deleted file mode 100644 index abe134c..0000000 --- a/test/unit/message-deserialize-request.c +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_deserialize_request(UNUSED(void **state)) -{ - struct api_error error = ERROR_INIT; - msgpack_sbuffer sbuf; - msgpack_packer pk; - msgpack_zone mempool; - msgpack_object deserialized; - struct message_request request; - - msgpack_sbuffer_init(&sbuf); - msgpack_zone_init(&mempool, 2048); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_str(&pk, 4); - msgpack_pack_str_body(&pk, "test", 4); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - free_string(request.method); - free_params(request.params); - - /* wrong type type*/ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_nil(&pk); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_str(&pk, 4); - msgpack_pack_str_body(&pk, "test", 4); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - /* wrong type */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_str(&pk, 4); - msgpack_pack_str_body(&pk, "test", 4); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong msgid */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_int(&pk, -1234); - msgpack_pack_str(&pk, 4); - msgpack_pack_str_body(&pk, "test", 4); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong msgid value*/ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint32(&pk, UINT32_MAX); - msgpack_pack_str(&pk, 4); - msgpack_pack_str_body(&pk, "test", 4); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong method */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_int(&pk, 1234); - msgpack_pack_nil(&pk); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong params */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_int(&pk, 1234); - msgpack_pack_str(&pk, 4); - msgpack_pack_str_body(&pk, "test", 4); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* null input params */ - assert_int_not_equal(0, message_deserialize_request(&request, &deserialized, - NULL)); - assert_int_not_equal(0, message_deserialize_request(&request, NULL, - &error)); - assert_int_not_equal(0, message_deserialize_request(NULL, &deserialized, - &error)); - - msgpack_zone_destroy(&mempool); - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-deserialize-response.c b/test/unit/message-deserialize-response.c deleted file mode 100644 index 6dde481..0000000 --- a/test/unit/message-deserialize-response.c +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_deserialize_response(UNUSED(void **state)) -{ - struct api_error error = ERROR_INIT; - msgpack_sbuffer sbuf; - msgpack_packer pk; - msgpack_zone mempool; - msgpack_object deserialized; - struct message_response response; - - msgpack_sbuffer_init(&sbuf); - msgpack_zone_init(&mempool, 2048); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_nil(&pk); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - free_params(response.params); - - /* wrong type type */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_nil(&pk); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_nil(&pk); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong type */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_nil(&pk); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong msgid */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_int(&pk, -1234); - msgpack_pack_nil(&pk); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong msgid value*/ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, UINT32_MAX); - msgpack_pack_nil(&pk); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong nil */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_array(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong params */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint32(&pk, 1234); - msgpack_pack_nil(&pk); - msgpack_pack_nil(&pk); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - &error)); - - msgpack_sbuffer_clear(&sbuf); - - /* null input params */ - assert_int_not_equal(0, message_deserialize_response(&response, &deserialized, - NULL)); - assert_int_not_equal(0, message_deserialize_response(&response, NULL, - &error)); - assert_int_not_equal(0, message_deserialize_response(NULL, &deserialized, - &error)); - - msgpack_zone_destroy(&mempool); - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-is-request.c b/test/unit/message-is-request.c deleted file mode 100644 index 8760e24..0000000 --- a/test/unit/message-is-request.c +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_is_request(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - msgpack_zone mempool; - msgpack_object deserialized; - - msgpack_sbuffer_init(&sbuf); - msgpack_zone_init(&mempool, 2048); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_true(message_is_request(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong type test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint8(&pk, 1); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_false(message_is_request(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - /* no array test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_uint8(&pk, 1); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_false(message_is_request(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - /* NULL test */ - assert_false(message_is_request(NULL)); - - msgpack_sbuffer_clear(&sbuf); - - msgpack_zone_destroy(&mempool); - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-is-response.c b/test/unit/message-is-response.c deleted file mode 100644 index 5ad9402..0000000 --- a/test/unit/message-is-response.c +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_is_response(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - msgpack_zone mempool; - msgpack_object deserialized; - - msgpack_sbuffer_init(&sbuf); - msgpack_zone_init(&mempool, 2048); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint8(&pk, 0); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_true(message_is_response(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - /* wrong type test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_array(&pk, 4); - msgpack_pack_uint8(&pk, 0); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint8(&pk, 1); - msgpack_pack_uint8(&pk, 1); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_false(message_is_response(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - /* no array test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - msgpack_pack_uint8(&pk, 1); - msgpack_unpack(sbuf.data, sbuf.size, NULL, &mempool, &deserialized); - - assert_false(message_is_response(&deserialized)); - - msgpack_sbuffer_clear(&sbuf); - - /* NULL test */ - assert_false(message_is_response(NULL)); - - msgpack_sbuffer_clear(&sbuf); - - msgpack_zone_destroy(&mempool); - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-serialize-error-response.c b/test/unit/message-serialize-error-response.c deleted file mode 100644 index 6dace64..0000000 --- a/test/unit/message-serialize-error-response.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_serialize_error_response(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - struct api_error error = ERROR_INIT; - msgpack_packer pk; - - error_set(&error, API_ERROR_TYPE_VALIDATION, "test error"); - - msgpack_sbuffer_init(&sbuf); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - assert_int_equal(0, message_serialize_error_response(&pk, &error, 1234)); - msgpack_sbuffer_clear(&sbuf); - - /* null check */ - assert_int_not_equal(0, message_serialize_error_response(NULL, NULL, 1234)); - - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-serialize-request.c b/test/unit/message-serialize-request.c deleted file mode 100644 index 371e32a..0000000 --- a/test/unit/message-serialize-request.c +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_serialize_request(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - struct message_request request; - array params; - - params.size = 1; - params.obj = CALLOC(1, struct message_object); - params.obj[0].type = OBJECT_TYPE_UINT; - params.obj[0].data.uinteger = 1234; - - msgpack_sbuffer_init(&sbuf); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - request.msgid = 1234; - request.method = (string) {.str = "test method", - .length = sizeof("test method") - 1}; - request.params = params; - assert_int_equal(0, message_serialize_request(&request, &pk)); - msgpack_sbuffer_clear(&sbuf); - - /* no valid string */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - request.msgid = 1234; - request.method = (string) STRING_INIT; - request.params = params; - assert_int_not_equal(0, message_serialize_request(&request, &pk)); - msgpack_sbuffer_clear(&sbuf); - - free_params(request.params); - - params.size = 1; - params.obj = CALLOC(1, struct message_object); - params.obj[0].type = 1000; - params.obj[0].data.uinteger = 1234; - - /* no valid params */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - request.msgid = 1234; - request.method = (string) {.str = "test method", - .length = sizeof("test method") - 1}; - request.params = params; - assert_int_not_equal(0, message_serialize_request(&request, &pk)); - msgpack_sbuffer_clear(&sbuf); - - free_params(request.params); - - /* null check */ - assert_int_not_equal(0, message_serialize_request(NULL, NULL)); - - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/message-serialize-response.c b/test/unit/message-serialize-response.c deleted file mode 100644 index 8809e86..0000000 --- a/test/unit/message-serialize-response.c +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (C) 2016 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_message_serialize_response(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - struct message_response response; - array params; - - params.size = 1; - params.obj = CALLOC(1, struct message_object); - params.obj[0].type = OBJECT_TYPE_UINT; - params.obj[0].data.uinteger = 1234; - - msgpack_sbuffer_init(&sbuf); - - /* positiv test */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - response.msgid = 1234; - response.params = params; - assert_int_equal(0, message_serialize_response(&response, &pk)); - msgpack_sbuffer_clear(&sbuf); - - free_params(response.params); - - params.size = 1; - params.obj = CALLOC(1, struct message_object); - params.obj[0].type = 1000; - params.obj[0].data.uinteger = 1234; - - /* no valid params */ - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - response.msgid = 1234; - response.params = params; - assert_int_not_equal(0, message_serialize_response(&response, &pk)); - msgpack_sbuffer_clear(&sbuf); - - free_params(response.params); - - /* null check */ - assert_int_not_equal(0, message_serialize_response(NULL, NULL)); - - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/pack-array.c b/test/unit/pack-array.c deleted file mode 100644 index 2bf5767..0000000 --- a/test/unit/pack-array.c +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_pack_array(UNUSED(void **state)) -{ - array params = ARRAY_INIT; - msgpack_sbuffer sbuf; - msgpack_packer pk; - - params.size = 5; - params.obj = CALLOC(params.size, struct message_object); - - params.obj[0].type = OBJECT_TYPE_UINT; - params.obj[0].data.uinteger = 5; - - params.obj[1].type = OBJECT_TYPE_INT; - params.obj[1].data.uinteger = 5; - - params.obj[2].type = OBJECT_TYPE_ARRAY; - params.obj[2].data.params.size = 1; - params.obj[2].data.params.obj = CALLOC(params.obj[2].data.params.size, - struct message_object); - params.obj[2].data.params.obj[0].type = OBJECT_TYPE_INT; - params.obj[2].data.params.obj[0].data.uinteger = 6; - - params.obj[3].type = OBJECT_TYPE_BOOL; - params.obj[3].data.boolean = true; - - params.obj[4].type = OBJECT_TYPE_FLOAT; - params.obj[4].data.floating = 1.2345; - - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - assert_int_equal(0, pack_params(&pk, params)); - assert_int_not_equal(0, pack_params(NULL, params)); - - msgpack_sbuffer_destroy(&sbuf); - - free_params(params); -} diff --git a/test/unit/pack-int.c b/test/unit/pack-int.c deleted file mode 100644 index 86e98b2..0000000 --- a/test/unit/pack-int.c +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_pack_int(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - assert_int_equal(0, pack_int8(&pk, 0)); - assert_int_equal(0, pack_int8(&pk, 1)); - assert_int_equal(0, pack_int8(&pk, 11)); - assert_int_not_equal(0, pack_int8(NULL, 11)); - - assert_int_equal(0, pack_int16(&pk, 0)); - assert_int_equal(0, pack_int16(&pk, 1)); - assert_int_equal(0, pack_int16(&pk, 11)); - assert_int_not_equal(0, pack_int16(NULL, 11)); - - assert_int_equal(0, pack_int32(&pk, 0)); - assert_int_equal(0, pack_int32(&pk, 1)); - assert_int_equal(0, pack_int32(&pk, 11)); - assert_int_not_equal(0, pack_int32(NULL, 11)); - - assert_int_equal(0, pack_int64(&pk, 0)); - assert_int_equal(0, pack_int64(&pk, 1)); - assert_int_equal(0, pack_int64(&pk, 11)); - assert_int_not_equal(0, pack_int64(NULL, 11)); - - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/pack-uint.c b/test/unit/pack-uint.c deleted file mode 100644 index 2253f87..0000000 --- a/test/unit/pack-uint.c +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_pack_uint(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_packer pk; - - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - assert_int_equal(0, pack_uint8(&pk, 0)); - assert_int_equal(0, pack_uint8(&pk, 1)); - assert_int_equal(0, pack_uint8(&pk, 11)); - assert_int_not_equal(0, pack_uint8(NULL, 11)); - - assert_int_equal(0, pack_uint16(&pk, 0)); - assert_int_equal(0, pack_uint16(&pk, 1)); - assert_int_equal(0, pack_uint16(&pk, 11)); - assert_int_not_equal(0, pack_uint16(NULL, 11)); - - assert_int_equal(0, pack_uint32(&pk, 0)); - assert_int_equal(0, pack_uint32(&pk, 1)); - assert_int_equal(0, pack_uint32(&pk, 11)); - assert_int_not_equal(0, pack_uint32(NULL, 11)); - - assert_int_equal(0, pack_uint64(&pk, 0)); - assert_int_equal(0, pack_uint64(&pk, 1)); - assert_int_equal(0, pack_uint64(&pk, 11)); - assert_int_not_equal(0, pack_uint64(NULL, 11)); - - msgpack_sbuffer_destroy(&sbuf); -} diff --git a/test/unit/regression-issue-60.c b/test/unit/regression-issue-60.c deleted file mode 100644 index a3ec4cd..0000000 --- a/test/unit/regression-issue-60.c +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - -void unit_regression_issue_60(UNUSED(void **state)) -{ - array params; - struct msgpack_object deserialized; - msgpack_unpacked result; - msgpack_sbuffer sbuf; - msgpack_packer pk; - - size_t off = 0; - - msgpack_sbuffer_init(&sbuf); - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - msgpack_pack_array(&pk, 0); - - msgpack_unpacked_init(&result); - msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off); - msgpack_sbuffer_destroy(&sbuf); - - deserialized = result.data; - - assert_int_equal(0, unpack_params(&deserialized, ¶ms)); - free_params(params); - msgpack_unpacked_destroy(&result); -} diff --git a/test/unit/server-start.c b/test/unit/server-start.c index abbb227..544d279 100644 --- a/test/unit/server-start.c +++ b/test/unit/server-start.c @@ -17,12 +17,12 @@ #include #include +#include "helper-all.h" #include "sb-common.h" #include "rpc/sb-rpc.h" #include "helper-unix.h" #include "rpc/db/sb-db.h" - -uv_loop_t loop; +#include "main.h" void unit_server_start(UNUSED(void **state)) { @@ -39,7 +39,7 @@ void unit_server_start(UNUSED(void **state)) srand((unsigned)time(NULL)); assert_int_equal(0, server_init()); - uv_loop_init(&loop); + loop_init(&main_loop, NULL); snprintf(buf1, 19, "/tmp/splnbx-%d", rand() % (999999 + 1 - 100000) + 100000); assert_int_equal(0, server_start_pipe(buf1)); @@ -58,8 +58,8 @@ void unit_server_start(UNUSED(void **state)) server_close(); - uv_run(&loop, UV_RUN_ONCE); - uv_loop_close(&loop); + uv_run(&main_loop.uv, UV_RUN_ONCE); + uv_loop_close(&main_loop.uv); db_close(); } diff --git a/test/unit/server-stop.c b/test/unit/server-stop.c index 53dbf6c..4dc5632 100644 --- a/test/unit/server-stop.c +++ b/test/unit/server-stop.c @@ -15,18 +15,18 @@ */ #include "sb-common.h" +#include "helper-all.h" #include "rpc/sb-rpc.h" #include "helper-unix.h" #include "rpc/db/sb-db.h" - -uv_loop_t loop; +#include "main.h" void unit_server_stop(UNUSED(void **state)) { boxaddr addr; uint16_t port; - uv_loop_init(&loop); + loop_init(&main_loop, NULL); connect_to_db(); box_addr_port_lookup("127.0.0.1:11111", &addr, &port); @@ -38,8 +38,8 @@ void unit_server_stop(UNUSED(void **state)) server_close(); - uv_run(&loop, UV_RUN_ONCE); - uv_loop_close(&loop); + uv_run(&main_loop.uv, UV_RUN_ONCE); + uv_loop_close(&main_loop.uv); db_close(); } diff --git a/test/unit/unpack-array.c b/test/unit/unpack-array.c deleted file mode 100644 index da2e2da..0000000 --- a/test/unit/unpack-array.c +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -static msgpack_object deserialized; - -void init_unpack_array(msgpack_object_type type) -{ - size_t off = 0; - - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - msgpack_pack_array(&pk, 3); - if (type == MSGPACK_OBJECT_NIL) { - msgpack_pack_nil(&pk); - } else if (type == MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_pack_int(&pk, 1); - } else if (type == MSGPACK_OBJECT_BOOLEAN) { - msgpack_pack_true(&pk); - } else if (type == MSGPACK_OBJECT_MAP) { - msgpack_pack_map(&pk, 1); - } else if (type == MSGPACK_OBJECT_ARRAY) { - msgpack_pack_array(&pk, 3); - msgpack_pack_int(&pk, 1); - msgpack_pack_int(&pk, 1); - msgpack_pack_int(&pk, 1); - } else if (type == MSGPACK_OBJECT_FLOAT) { - msgpack_pack_double(&pk, 1.2); - } - msgpack_pack_true(&pk); - msgpack_pack_str(&pk, 7); - msgpack_pack_str_body(&pk, "example", 7); - - msgpack_unpacked result; - - msgpack_unpacked_init(&result); - msgpack_unpack_next(&result, sbuf.data, sbuf.size, &off); - msgpack_sbuffer_destroy(&sbuf); - deserialized = result.data; -} - -void unit_unpack_array(UNUSED(void **state)) -{ - array params; - - init_unpack_array(MSGPACK_OBJECT_NIL); - assert_int_equal(0, unpack_params(&deserialized, ¶ms)); - init_unpack_array(MSGPACK_OBJECT_ARRAY); - assert_int_equal(0, unpack_params(&deserialized, ¶ms)); - init_unpack_array(MSGPACK_OBJECT_FLOAT); - assert_int_equal(0, unpack_params(&deserialized, ¶ms)); - init_unpack_array(MSGPACK_OBJECT_MAP); - assert_int_equal(0, unpack_params(&deserialized, ¶ms)); - init_unpack_array(MSGPACK_OBJECT_POSITIVE_INTEGER); - assert_int_equal(0, unpack_params(&deserialized, ¶ms)); -} diff --git a/test/unit/unpack-string.c b/test/unit/unpack-string.c deleted file mode 100644 index ffe2c34..0000000 --- a/test/unit/unpack-string.c +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -void unit_unpack_string(UNUSED(void **state)) -{ - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - msgpack_pack_str(&pk, 64); - msgpack_pack_str_body( - &pk, - "TeiwieDoowuiMeix6SooxieFievee2io3ohhu5uo5ughu8cieja4iu6chuirijae", - 64); - - msgpack_object deserialized; - msgpack_unpack(sbuf.data, sbuf.size, NULL, NULL, &deserialized); - msgpack_sbuffer_destroy(&sbuf); - - assert_non_null(unpack_string(&deserialized).str); -} diff --git a/test/unit/unpack-uint.c b/test/unit/unpack-uint.c deleted file mode 100644 index f9463b2..0000000 --- a/test/unit/unpack-uint.c +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (C) 2015 splone UG - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include - -#include "sb-common.h" -#include "rpc/msgpack/sb-msgpack-rpc.h" -#include "helper-unix.h" - - -static msgpack_object deserialized; - -void init_unpack_uint(uint64_t type) -{ - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); - msgpack_packer pk; - msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write); - - msgpack_pack_uint64(&pk, type); - - msgpack_unpack(sbuf.data, sbuf.size, NULL, NULL, &deserialized); - msgpack_sbuffer_destroy(&sbuf); -} - -void unit_unpack_uint(UNUSED(void **state)) -{ - init_unpack_uint(0); - assert_int_equal(0, unpack_uint(&deserialized)); - init_unpack_uint(1); - assert_int_equal(1, unpack_uint(&deserialized)); -} diff --git a/test/wrapper-functions.c b/test/wrapper-functions.c index 585f976..0d09863 100644 --- a/test/wrapper-functions.c +++ b/test/wrapper-functions.c @@ -2,6 +2,9 @@ #include #include #include "rpc/sb-rpc.h" +#include "rpc/msgpack/helpers.h" +#include "rpc/connection/loop.h" +#include "api/helpers.h" #include "helper-unix.h" #include "helper-all.h" @@ -42,7 +45,7 @@ int __wrap_outputstream_write(UNUSED(outputstream *ostream), char *buffer, size_ break; case 'M': /* message packet */ - assert_int_equal(0, validate_crypto_write(buffer, len)); + assert_int_equal(0, validate_crypto_write((unsigned char*)buffer, len)); break; default: LOG_WARNING("Illegal identifier suffix."); @@ -52,15 +55,19 @@ int __wrap_outputstream_write(UNUSED(outputstream *ostream), char *buffer, size_ return (0); } -void __wrap_loop_wait_for_response(UNUSED(struct connection *con), - struct callinfo *cinfo) +void __wrap_loop_process_events_until(UNUSED(loop *loop), + UNUSED(struct connection *con), struct callinfo *cinfo) { assert_non_null(cinfo); /* The callid and the message_params type are determined by the unit test. */ - cinfo->response.params.size = 1; - cinfo->response.params.obj = CALLOC(cinfo->response.params.size, - struct message_object); - cinfo->response.params.obj[0].type = (message_object_type)mock(); - cinfo->response.params.obj[0].data.uinteger = (uint64_t)mock(); + + cinfo->result = ARRAY_OBJ(((array) { + .size = 1, + .capacity = 1, + .items = calloc(1, sizeof(object)), + })); + + cinfo->result.data.array.items[0].type = (object_type)mock(); + cinfo->result.data.array.items[0].data.uinteger = (uint64_t)mock(); } diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt new file mode 100644 index 0000000..3321498 --- /dev/null +++ b/third-party/CMakeLists.txt @@ -0,0 +1,108 @@ +# This is not meant to be included by the top-level. +cmake_minimum_required (VERSION 2.8.7) +project(SPLONEBOX_DEPS) + +# Point CMake at any custom modules we may ship +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + +# In Windows/MSVC CMAKE_BUILD_TYPE changes the paths/linking of the build +# recipes (libuv, msgpack), make sure it is set +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr" CACHE PATH "Dependencies install directory.") +set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin" CACHE PATH "Dependencies binary install directory.") +set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib" CACHE PATH "Dependencies library install directory.") +set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build" CACHE PATH "Dependencies build directory.") +set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads" CACHE PATH "Dependencies download directory.") + +option(USE_BUNDLED "Use bundled dependencies." ON) + +option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED}) +option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED}) + +#XXX(tarruda): Lua is only used for debugging the functional test client, no +# build it unless explicitly requested +option(USE_BUNDLED_LUA "Use the bundled version of lua." OFF) + +if(USE_BUNDLED AND (NOT WIN32)) + option(USE_BUNDLED_GPERF "Use the bundled version of gperf." ON) +else() + option(USE_BUNDLED_GPERF "Use the bundled version of gperf." OFF) +endif() + +option(USE_EXISTING_SRC_DIR "Skip download of deps sources in case of existing source directory." OFF) + +if(UNIX) + find_program(MAKE_PRG NAMES gmake make) + if(MAKE_PRG) + execute_process( + COMMAND "${MAKE_PRG}" --version + OUTPUT_VARIABLE MAKE_VERSION_INFO) + if(NOT "${OUTPUT_VARIABLE}" MATCHES ".*GNU.*") + unset(MAKE_PRG) + endif() + endif() + if(NOT MAKE_PRG) + message(FATAL_ERROR "GNU Make is required to build the dependencies.") + else() + message(STATUS "Found GNU Make at ${MAKE_PRG}") + endif() +endif() + +# When using make, use the $(MAKE) variable to avoid warning about the job +# server. +if(CMAKE_GENERATOR MATCHES "Makefiles") + set(MAKE_PRG "$(MAKE)") +endif() + +if(CMAKE_C_COMPILER_ARG1) + set(DEPS_C_COMPILER "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}") +else() + set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}") +endif() + +# Cross compiling: use these for dependencies built for the +# HOST system, when not crosscompiling these should be the +# same as DEPS_*. Except when targeting Unix in which case +# want all the dependencies to use the same compiler. +if(CMAKE_CROSSCOMPILING AND NOT UNIX) + set(HOSTDEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/host") + set(HOSTDEPS_BIN_DIR "${HOSTDEPS_INSTALL_DIR}/bin") + set(HOSTDEPS_LIB_DIR "${HOSTDEPS_INSTALL_DIR}/lib") + set(HOSTDEPS_C_COMPILER "${HOST_C_COMPILER}") +else() + set(HOSTDEPS_INSTALL_DIR "${DEPS_INSTALL_DIR}") + set(HOSTDEPS_BIN_DIR "${DEPS_BIN_DIR}") + set(HOSTDEPS_LIB_DIR "${DEPS_LIB_DIR}") + set(HOSTDEPS_C_COMPILER "${DEPS_C_COMPILER}") +endif() + +include(ExternalProject) + +set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.10.0.tar.gz) +set(LIBUV_SHA256 50f4ed57d65af4ab634e2cbdd90c49213020e15b4d77d3631feb633cbba9239f) + +set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/cpp-1.0.0.tar.gz) +set(MSGPACK_SHA256 afda64ca445203bb7092372b822bae8b2539fdcebbfc3f753f393628c2bcfe7d) + +if(USE_BUNDLED_LIBUV) + include(BuildLibuv) +endif() + +if(USE_BUNDLED_MSGPACK) + include(BuildMsgpack) +endif() + + +add_custom_target(clean-shared-libraries + COMMAND ${CMAKE_COMMAND} + -DREMOVE_FILE_GLOB=${DEPS_INSTALL_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}*${CMAKE_SHARED_LIBRARY_SUFFIX}* + -P ${PROJECT_SOURCE_DIR}/cmake/RemoveFiles.cmake + DEPENDS ${THIRD_PARTY_DEPS} +) + +add_custom_target(third-party ALL + COMMAND ${CMAKE_COMMAND} -E touch .third-party + DEPENDS clean-shared-libraries) diff --git a/third-party/cmake/BuildLibuv.cmake b/third-party/cmake/BuildLibuv.cmake new file mode 100644 index 0000000..5482f28 --- /dev/null +++ b/third-party/cmake/BuildLibuv.cmake @@ -0,0 +1,102 @@ +include(CMakeParseArguments) + +# BuildLibuv(TARGET targetname CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...) +# Reusable function to build libuv, wraps ExternalProject_Add. +# Failing to pass a command argument will result in no command being run +function(BuildLibuv) + cmake_parse_arguments(_libuv + "BUILD_IN_SOURCE" + "TARGET" + "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND" + ${ARGN}) + + if(NOT _libuv_CONFIGURE_COMMAND AND NOT _libuv_BUILD_COMMAND + AND NOT _libuv_INSTALL_COMMAND) + message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND") + endif() + if(NOT _libuv_TARGET) + set(_libuv_TARGET "libuv") + endif() + + ExternalProject_Add(${_libuv_TARGET} + PREFIX ${DEPS_BUILD_DIR} + URL ${LIBUV_URL} + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libuv + DOWNLOAD_COMMAND ${CMAKE_COMMAND} + -DPREFIX=${DEPS_BUILD_DIR} + -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/libuv + -DURL=${LIBUV_URL} + -DEXPECTED_SHA256=${LIBUV_SHA256} + -DTARGET=${_libuv_TARGET} + -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake + BUILD_IN_SOURCE ${_libuv_BUILD_IN_SOURCE} + CONFIGURE_COMMAND "${_libuv_CONFIGURE_COMMAND}" + BUILD_COMMAND "${_libuv_BUILD_COMMAND}" + INSTALL_COMMAND "${_libuv_INSTALL_COMMAND}") +endfunction() + +set(UNIX_CFGCMD sh ${DEPS_BUILD_DIR}/src/libuv/autogen.sh && + ${DEPS_BUILD_DIR}/src/libuv/configure --with-pic --disable-shared + --prefix=${DEPS_INSTALL_DIR} --libdir=${DEPS_INSTALL_DIR}/lib + CC=${DEPS_C_COMPILER}) + +if(UNIX) + BuildLibuv( + CONFIGURE_COMMAND ${UNIX_CFGCMD} + INSTALL_COMMAND ${MAKE_PRG} V=1 install) + +elseif(MINGW AND CMAKE_CROSSCOMPILING) + # Build libuv for the host + BuildLibuv(TARGET libuv_host + CONFIGURE_COMMAND sh ${DEPS_BUILD_DIR}/src/libuv_host/autogen.sh && ${DEPS_BUILD_DIR}/src/libuv_host/configure --with-pic --disable-shared --prefix=${HOSTDEPS_INSTALL_DIR} CC=${HOST_C_COMPILER} + INSTALL_COMMAND ${MAKE_PRG} V=1 install) + + # Build libuv for the target + BuildLibuv( + CONFIGURE_COMMAND ${UNIX_CFGCMD} --host=${CROSS_TARGET} + INSTALL_COMMAND ${MAKE_PRG} V=1 install) + +elseif(MINGW) + + # Native MinGW + BuildLibUv(BUILD_IN_SOURCE + BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} -f Makefile.mingw + INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_BUILD_DIR}/src/libuv/libuv.a ${DEPS_INSTALL_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/include + COMMAND ${CMAKE_COMMAND} -E copy_directory ${DEPS_BUILD_DIR}/src/libuv/include ${DEPS_INSTALL_DIR}/include + ) + +elseif(WIN32 AND MSVC) + + find_package(PythonInterp 2.6 REQUIRED) + if(NOT PYTHONINTERP_FOUND OR PYTHON_VERSION_MAJOR GREATER 2) + message(FATAL_ERROR "Python2 is required to build libuv on windows, use -DPYTHON_EXECUTABLE to set a python interpreter") + endif() + + string(FIND ${CMAKE_GENERATOR} Win64 VS_WIN64) + if(VS_WIN64 EQUAL -1) + set(VS_ARCH x86) + else() + set(VS_ARCH x64) + endif() + string(TOLOWER ${CMAKE_BUILD_TYPE} LOWERCASE_BUILD_TYPE) + set(UV_OUTPUT_DIR ${DEPS_BUILD_DIR}/src/libuv/${CMAKE_BUILD_TYPE}) + BuildLibUv( + BUILD_COMMAND set PYTHON=${PYTHON_EXECUTABLE} COMMAND ${DEPS_BUILD_DIR}/src/libuv/vcbuild.bat shared ${LOWERCASE_BUILD_TYPE} ${VS_ARCH} + INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.lib ${DEPS_INSTALL_DIR}/lib + # Some applications (lua-client/luarocks) look for uv.lib instead of libuv.lib + COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.lib ${DEPS_INSTALL_DIR}/lib/uv.lib + COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.dll ${DEPS_INSTALL_DIR}/bin/ + COMMAND ${CMAKE_COMMAND} -E copy ${UV_OUTPUT_DIR}/libuv.dll ${DEPS_INSTALL_DIR}/bin/uv.dll + COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_INSTALL_DIR}/include + COMMAND ${CMAKE_COMMAND} -E copy_directory ${DEPS_BUILD_DIR}/src/libuv/include ${DEPS_INSTALL_DIR}/include) + +else() + message(FATAL_ERROR "Trying to build libuv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}") +endif() + +list(APPEND THIRD_PARTY_DEPS libuv) diff --git a/third-party/cmake/BuildMsgpack.cmake b/third-party/cmake/BuildMsgpack.cmake new file mode 100644 index 0000000..6b38508 --- /dev/null +++ b/third-party/cmake/BuildMsgpack.cmake @@ -0,0 +1,79 @@ +include(CMakeParseArguments) + +# BuildMsgpack(CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...) +# Reusable function to build msgpack, wraps ExternalProject_Add. +# Failing to pass a command argument will result in no command being run +function(BuildMsgpack) + cmake_parse_arguments(_msgpack + "" + "" + "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND" + ${ARGN}) + + if(NOT _msgpack_CONFIGURE_COMMAND AND NOT _msgpack_BUILD_COMMAND + AND NOT _msgpack_INSTALL_COMMAND) + message(FATAL_ERROR "Must pass at least one of CONFIGURE_COMMAND, BUILD_COMMAND, INSTALL_COMMAND") + endif() + + ExternalProject_Add(msgpack + PREFIX ${DEPS_BUILD_DIR} + URL ${MSGPACK_URL} + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/msgpack + DOWNLOAD_COMMAND ${CMAKE_COMMAND} + -DPREFIX=${DEPS_BUILD_DIR} + -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/msgpack + -DURL=${MSGPACK_URL} + -DEXPECTED_SHA256=${MSGPACK_SHA256} + -DTARGET=msgpack + -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake + CONFIGURE_COMMAND "${_msgpack_CONFIGURE_COMMAND}" + BUILD_COMMAND "${_msgpack_BUILD_COMMAND}" + INSTALL_COMMAND "${_msgpack_INSTALL_COMMAND}") +endfunction() + +set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack + -DMSGPACK_ENABLE_CXX=OFF + -DMSGPACK_BUILD_TESTS=OFF + -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1} -fPIC" + -DCMAKE_GENERATOR=${CMAKE_GENERATOR}) + +set(MSGPACK_BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}) +set(MSGPACK_INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${CMAKE_BUILD_TYPE}) + +if(MINGW AND CMAKE_CROSSCOMPILING) + get_filename_component(TOOLCHAIN ${CMAKE_TOOLCHAIN_FILE} REALPATH) + set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack + -DMSGPACK_ENABLE_CXX=OFF + -DMSGPACK_BUILD_TESTS=OFF + -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} + # Pass toolchain + -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + # Hack to avoid -rdynamic in Mingw + -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="") +elseif(MSVC) + # Same as Unix without fPIC + set(MSGPACK_CONFIGURE_COMMAND ${CMAKE_COMMAND} ${DEPS_BUILD_DIR}/src/msgpack + -DMSGPACK_ENABLE_CXX=OFF + -DMSGPACK_BUILD_TESTS=OFF + -DCMAKE_INSTALL_PREFIX=${DEPS_INSTALL_DIR} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_COMPILER_ARG1}" + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + # Make sure we use the same generator, otherwise we may + # accidentaly end up using different MSVC runtimes + -DCMAKE_GENERATOR=${CMAKE_GENERATOR}) + # Place the DLL in the bin folder + set(MSGPACK_INSTALL_COMMAND ${MSGPACK_INSTALL_COMMAND} + COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_INSTALL_DIR}/lib/msgpack.dll ${DEPS_INSTALL_DIR}/bin) +endif() + +BuildMsgpack(CONFIGURE_COMMAND ${MSGPACK_CONFIGURE_COMMAND} + BUILD_COMMAND ${MSGPACK_BUILD_COMMAND} + INSTALL_COMMAND ${MSGPACK_INSTALL_COMMAND}) + +list(APPEND THIRD_PARTY_DEPS msgpack) diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/third-party/cmake/DownloadAndExtractFile.cmake new file mode 100644 index 0000000..24e431b --- /dev/null +++ b/third-party/cmake/DownloadAndExtractFile.cmake @@ -0,0 +1,162 @@ +if(NOT DEFINED PREFIX) + message(FATAL_ERROR "PREFIX must be defined.") +endif() + +if(NOT DEFINED URL) + message(FATAL_ERROR "URL must be defined.") +endif() + +if(NOT DEFINED DOWNLOAD_DIR) + message(FATAL_ERROR "DOWNLOAD_DIR must be defined.") +endif() + +if(NOT DEFINED EXPECTED_SHA256) + message(FATAL_ERROR "EXPECTED_SHA256 must be defined.") +endif() + +if(NOT DEFINED TARGET) + message(FATAL_ERROR "TARGET must be defined.") +endif() + +set(SRC_DIR ${PREFIX}/src/${TARGET}) + +# Check whether the source has been downloaded. If true, skip it. +# Useful for external downloads like homebrew. +if(USE_EXISTING_SRC_DIR) + if(EXISTS "${SRC_DIR}" AND IS_DIRECTORY "${SRC_DIR}") + file(GLOB EXISTED_FILES "${SRC_DIR}/*") + if(EXISTED_FILES) + message(STATUS "${SRC_DIR} is found and not empty, skipping download and extraction. ") + return() + endif() + endif() + message(FATAL_ERROR "USE_EXISTING_SRC_DIR set to ON, but '${SRC_DIR}' does not exist or is empty.") +endif() + +# Taken from ExternalProject_Add. Let's hope we can drop this one day when +# ExternalProject_Add allows you to disable SHOW_PROGRESS on the file download. +if(TIMEOUT) + set(timeout_args TIMEOUT ${timeout}) + set(timeout_msg "${timeout} seconds") +else() + set(timeout_args "# no TIMEOUT") + set(timeout_msg "none") +endif() + +string(REGEX MATCH "[^/\\?]*$" fname "${URL}") +if(NOT "${fname}" MATCHES "(\\.|=)(bz2|tar|tgz|tar\\.gz|zip)$") + string(REGEX MATCH "([^/\\?]+(\\.|=)(bz2|tar|tgz|tar\\.gz|zip))/.*$" match_result "${URL}") + set(fname "${CMAKE_MATCH_1}") +endif() +if(NOT "${fname}" MATCHES "(\\.|=)(bz2|tar|tgz|tar\\.gz|zip)$") + message(FATAL_ERROR "Could not extract tarball filename from url:\n ${url}") +endif() +string(REPLACE ";" "-" fname "${fname}") + +set(file ${DOWNLOAD_DIR}/${fname}) +message(STATUS "file: ${file}") + +message(STATUS "downloading... + src='${URL}' + dst='${file}' + timeout='${timeout_msg}'") + +file(DOWNLOAD ${URL} ${file} + ${timeout_args} + ${hash_args} + STATUS status + LOG log) + +list(GET status 0 status_code) +list(GET status 1 status_string) + +if(NOT status_code EQUAL 0) + message(FATAL_ERROR "error: downloading '${URL}' failed + status_code: ${status_code} + status_string: ${status_string} + log: ${log} +") +endif() + +set(NULL_SHA256 "0000000000000000000000000000000000000000000000000000000000000000") + +# Allow users to use "SKIP" or "skip" as the sha256 to skip checking the hash. +# You can still use the all zeros hash too. +if((EXPECTED_SHA256 STREQUAL "SKIP") OR (EXPECTED_SHA256 STREQUAL "skip")) + set(EXPECTED_SHA256 ${NULL_SHA256}) +endif() + +# We could avoid computing the SHA256 entirely if a NULL_SHA256 was given, +# but we want to warn users of an empty file. +file(SHA256 ${file} ACTUAL_SHA256) +if(ACTUAL_SHA256 STREQUAL "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + # File was empty. It's likely due to lack of SSL support. + message(FATAL_ERROR + "Failed to download ${URL}. The file is empty and likely means CMake " + "was built without SSL support. Please use a version of CMake with " + "proper SSL support. See " + "https://github.com/neovim/neovim/wiki/Building-Neovim#build-prerequisites " + "for more information.") +elseif((NOT EXPECTED_SHA256 STREQUAL NULL_SHA256) AND + (NOT EXPECTED_SHA256 STREQUAL ACTUAL_SHA256)) + # Wasn't a NULL SHA256 and we didn't match, so we fail. + message(FATAL_ERROR + "Failed to download ${URL}. Expected a SHA256 of " + "${EXPECTED_SHA256} but got ${ACTUAL_SHA256} instead.") +endif() + +message(STATUS "downloading... done") + +# Slurped from a generated extract-TARGET.cmake file. +message(STATUS "extracting... + src='${file}' + dst='${SRC_DIR}'") + +if(NOT EXISTS "${file}") + message(FATAL_ERROR "error: file to extract does not exist: '${file}'") +endif() + +# Prepare a space for extracting: +# +set(i 1234) +while(EXISTS "${SRC_DIR}/../ex-${TARGET}${i}") + math(EXPR i "${i} + 1") +endwhile() +set(ut_dir "${SRC_DIR}/../ex-${TARGET}${i}") +file(MAKE_DIRECTORY "${ut_dir}") + +# Extract it: +# +message(STATUS "extracting... [tar xfz]") +execute_process(COMMAND ${CMAKE_COMMAND} -E tar xfz ${file} + WORKING_DIRECTORY ${ut_dir} + RESULT_VARIABLE rv) + +if(NOT rv EQUAL 0) + message(STATUS "extracting... [error clean up]") + file(REMOVE_RECURSE "${ut_dir}") + message(FATAL_ERROR "error: extract of '${file}' failed") +endif() + +# Analyze what came out of the tar file: +# +message(STATUS "extracting... [analysis]") +file(GLOB contents "${ut_dir}/*") +list(LENGTH contents n) +if(NOT n EQUAL 1 OR NOT IS_DIRECTORY "${contents}") + set(contents "${ut_dir}") +endif() + +# Move "the one" directory to the final directory: +# +message(STATUS "extracting... [rename]") +file(REMOVE_RECURSE ${SRC_DIR}) +get_filename_component(contents ${contents} ABSOLUTE) +file(RENAME ${contents} ${SRC_DIR}) + +# Clean up: +# +message(STATUS "extracting... [clean up]") +file(REMOVE_RECURSE "${ut_dir}") + +message(STATUS "extracting... done") diff --git a/third-party/cmake/RemoveFiles.cmake b/third-party/cmake/RemoveFiles.cmake new file mode 100644 index 0000000..88e2bc7 --- /dev/null +++ b/third-party/cmake/RemoveFiles.cmake @@ -0,0 +1,5 @@ +file(GLOB_RECURSE FILES_TO_REMOVE ${REMOVE_FILE_GLOB}) + +if(FILES_TO_REMOVE) + file(REMOVE ${FILES_TO_REMOVE}) +endif()